Compare commits

...

3 commits

Author SHA1 Message Date
22f50d64f9 Merge pull request #33 from codex/turbo-idiomatic-workspace
build(turbo): normalize workspace task graph
2026-06-20 06:19:39 +02:00
vince
c8183bcc47 fix(turbo): rely on graph build before e2e 2026-06-20 06:16:41 +02:00
vince
ae6eabed6f build(turbo): normalize workspace task graph 2026-06-20 06:10:16 +02:00
6 changed files with 138 additions and 32 deletions

View file

@ -32,7 +32,8 @@ This MVP uses Drizzle with Bun SQLite for local file-backed persistence.
- `bun run test`: run the unit test stage in all workspaces.
- `bun run test:unit`: run Vitest explicitly as the unit test stage.
- `bun run test:e2e`: build and run Playwright browser smoke and QA checks.
- `bun run test:qa`: run the release gate through Turbo.
- `bun run test:qa`: run the release gate through Turbo across check, unit,
build, Storybook, and e2e tasks.
- `bun run build`: build the UI package, production website, and Bun server.
- `bun run preview`: preview the production web build.
- `bun run storybook`: start the UI package component explorer on port 6006.
@ -102,7 +103,9 @@ bun run test:qa
The gate runs TypeScript checks, Vitest coverage for model,
persistence, renderer, datasource mocks, and presentation boundaries, the
production build, the static Storybook build, and Playwright desktop/mobile
smoke checks against the built adapter output. Playwright also performs
smoke checks against the built adapter output. Turbo owns the release task
graph; app package scripts stay as leaf commands and do not re-run the QA
pipeline internally. Playwright also performs
baseline screenshot checks, keyboard navigation checks, reduced-motion checks,
landmark checks, and axe accessibility checks against the real model-driven
route.

View file

@ -11,7 +11,6 @@
"test": "vitest run",
"test:unit": "vitest run",
"test:e2e": "env -u NO_COLOR playwright test",
"test:qa": "bun run check && bun run test:unit && bun run build && bun run test:e2e",
"db:generate": "drizzle-kit generate",
"db:check": "drizzle-kit check"
},

View file

@ -21,13 +21,13 @@ export default defineConfig({
},
webServer: [
{
command: `DISABLE_LIVE_DATASOURCES=1 bun run build && DISABLE_LIVE_DATASOURCES=1 DATABASE_URL=${databaseUrl} HOST=127.0.0.1 PORT=${port} bun build/index.js`,
command: `DISABLE_LIVE_DATASOURCES=1 DATABASE_URL=${databaseUrl} HOST=127.0.0.1 PORT=${port} bun build/index.js`,
url: baseURL,
reuseExistingServer: false,
timeout: 120_000,
},
{
command: `cd ../.. && bun run build-storybook && STORYBOOK_STATIC_PORT=${storybookPort} bun apps/web/tests/e2e/storybook-server.ts`,
command: `cd ../.. && STORYBOOK_STATIC_PORT=${storybookPort} bun apps/web/tests/e2e/storybook-server.ts`,
url: storybookURL,
reuseExistingServer: false,
timeout: 120_000,

View file

@ -18,10 +18,62 @@ describe("workspace boundaries", () => {
expect(packageJson.private).toBe(true);
expect(packageJson.workspaces).toEqual(["apps/*", "packages/*"]);
expect(packageJson.scripts?.build).toBe("turbo build");
expect(packageJson.scripts?.build).toBe("turbo run build");
expect(existsSync(join(root, "turbo.json"))).toBe(true);
});
test("keeps release orchestration in the root turbo task graph", () => {
const rootPackage = JSON.parse(
readFileSync(join(root, "package.json"), "utf8"),
) as {
scripts?: Record<string, string>;
};
const webPackage = JSON.parse(
readFileSync(join(root, "apps/web/package.json"), "utf8"),
) as { scripts?: Record<string, string> };
const turboConfig = JSON.parse(
readFileSync(join(root, "turbo.json"), "utf8"),
) as {
globalDependencies?: string[];
tasks?: Record<string, { env?: string[] }>;
};
expect(rootPackage.scripts?.["test:qa"]).toBe(
"turbo run check test:unit build @dimensionlab/ui#build-storybook @dimensionlab/web#test:e2e",
);
expect(webPackage.scripts).not.toHaveProperty("test:qa");
expect(turboConfig.tasks).not.toHaveProperty("test:qa");
expect(turboConfig.globalDependencies).toEqual(
expect.arrayContaining(["bun.lock", "tsconfig.base.json"]),
);
expect(turboConfig.tasks?.["test:e2e"]?.env).toEqual(
expect.arrayContaining([
"CI",
"PLAYWRIGHT_DATABASE_URL",
"PLAYWRIGHT_PORT",
"PLAYWRIGHT_STORYBOOK_PORT",
]),
);
});
test("lets turbo build app and Storybook artifacts before e2e serves them", () => {
const playwrightConfig = readFileSync(
join(root, "apps/web/playwright.config.ts"),
"utf8",
);
const turboConfig = JSON.parse(
readFileSync(join(root, "turbo.json"), "utf8"),
) as {
tasks?: Record<string, { dependsOn?: string[] }>;
};
expect(playwrightConfig).not.toContain("bun run build &&");
expect(playwrightConfig).not.toContain("bun run build-storybook");
expect(turboConfig.tasks?.["test:e2e"]?.dependsOn).toEqual(
expect.arrayContaining(["@dimensionlab/ui#build-storybook"]),
);
});
test("keeps the website app and reusable UI library as separate packages", () => {
const webPackage = JSON.parse(
readFileSync(join(root, "apps/web/package.json"), "utf8"),

View file

@ -9,18 +9,18 @@
"packages/*"
],
"scripts": {
"dev": "turbo dev --filter=@dimensionlab/web",
"build": "turbo build",
"preview": "bun run --cwd apps/web preview",
"storybook": "turbo storybook --filter=@dimensionlab/ui",
"build-storybook": "turbo build-storybook --filter=@dimensionlab/ui",
"check": "turbo check",
"test": "turbo test:unit",
"test:unit": "turbo test:unit",
"test:e2e": "turbo test:e2e --filter=@dimensionlab/web",
"test:qa": "turbo test:qa",
"db:generate": "bun run --cwd apps/web db:generate",
"db:check": "bun run --cwd apps/web db:check"
"dev": "turbo run dev --filter=@dimensionlab/web",
"build": "turbo run build",
"preview": "turbo run preview --filter=@dimensionlab/web",
"storybook": "turbo run storybook --filter=@dimensionlab/ui",
"build-storybook": "turbo run build-storybook --filter=@dimensionlab/ui",
"check": "turbo run check",
"test": "turbo run test:unit",
"test:unit": "turbo run test:unit",
"test:e2e": "turbo run test:e2e --filter=@dimensionlab/web",
"test:qa": "turbo run check test:unit build @dimensionlab/ui#build-storybook @dimensionlab/web#test:e2e",
"db:generate": "turbo run db:generate --filter=@dimensionlab/web",
"db:check": "turbo run db:check --filter=@dimensionlab/web"
},
"devDependencies": {
"turbo": "^2.5.0"

View file

@ -1,9 +1,16 @@
{
"$schema": "https://turbo.build/schema.json",
"globalDependencies": [
"bun.lock",
"package.json",
"tsconfig.base.json",
"tsconfig.json"
],
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", "build/**"]
"outputs": ["dist/**", "build/**"],
"env": ["NODE_ENV", "VITE_*"]
},
"check": {
"dependsOn": ["^build"],
@ -11,33 +18,78 @@
},
"test:unit": {
"dependsOn": ["^build"],
"outputs": []
"outputs": [],
"env": [
"AGENT_CONFIG_TOKEN",
"DASHBOARD_MIGRATIONS_DIR",
"DATABASE_URL",
"DISABLE_LIVE_DATASOURCES",
"PROMETHEUS_BASE_URL"
]
},
"build-storybook": {
"dependsOn": ["^build"],
"outputs": ["storybook-static/**"]
"outputs": ["storybook-static/**"],
"env": ["NODE_ENV", "STORYBOOK_*", "VITE_*"]
},
"test:e2e": {
"dependsOn": ["build", "^build"],
"outputs": ["test-results/**", "playwright-report/**"]
},
"test:qa": {
"dependsOn": [
"check",
"test:unit",
"build",
"build-storybook",
"test:e2e"
"^build",
"@dimensionlab/ui#build-storybook"
],
"outputs": []
"outputs": ["test-results/**", "playwright-report/**"],
"env": [
"AGENT_CONFIG_TOKEN",
"CI",
"DASHBOARD_MIGRATIONS_DIR",
"DATABASE_URL",
"DISABLE_LIVE_DATASOURCES",
"PLAYWRIGHT_DATABASE_URL",
"PLAYWRIGHT_PORT",
"PLAYWRIGHT_STORYBOOK_PORT",
"PROMETHEUS_BASE_URL",
"STORYBOOK_STATIC_PORT"
]
},
"db:generate": {
"cache": false
},
"db:check": {
"outputs": [],
"env": ["DATABASE_URL"]
},
"dev": {
"cache": false,
"persistent": true
"persistent": true,
"env": [
"AGENT_CONFIG_TOKEN",
"DASHBOARD_DEV_API_HOST",
"DASHBOARD_DEV_API_PORT",
"DASHBOARD_DEV_API_TARGET",
"DASHBOARD_MIGRATIONS_DIR",
"DATABASE_URL",
"HOST",
"PORT",
"PROMETHEUS_BASE_URL"
]
},
"preview": {
"cache": false,
"persistent": true,
"env": [
"AGENT_CONFIG_TOKEN",
"DASHBOARD_MIGRATIONS_DIR",
"DATABASE_URL",
"HOST",
"PORT",
"PROMETHEUS_BASE_URL"
]
},
"storybook": {
"cache": false,
"persistent": true
"persistent": true,
"env": ["HOST", "PORT", "STORYBOOK_*", "VITE_*"]
}
}
}