diff --git a/apps/docs/package.json b/apps/docs/package.json index a969a9b..38d8a21 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -3,7 +3,7 @@ "private": true, "type": "module", "scripts": { - "build-storybook": "storybook build --disable-telemetry --output-dir ../../.artifacts/storybook-static", + "build-storybook": "node ../../scripts/harness/build-storybook.mjs", "storybook": "WATCHPACK_POLLING=true CHOKIDAR_USEPOLLING=true storybook dev -p 6006 --ci --disable-telemetry", "storybook:smoke": "WATCHPACK_POLLING=true CHOKIDAR_USEPOLLING=true storybook dev -p 6006 --ci --smoke-test --disable-telemetry", "typecheck": "tsc --noEmit -p tsconfig.json" diff --git a/package.json b/package.json index 0045d3e..be2fec2 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ }, "scripts": { "build": "pnpm --filter @ai-ui/tokens build && pnpm --filter @ai-ui/ui build", - "build:docs": "pnpm --dir apps/docs run build-storybook", + "build:docs": "node ./scripts/harness/build-storybook.mjs", "changeset": "changeset", "changeset:status": "changeset status --verbose", "dev:docs": "pnpm --dir apps/docs run storybook", @@ -30,7 +30,7 @@ "registry:check": "node ./scripts/build-registry.mjs --check", "registry:install": "node ./scripts/registry-install.mjs", "release:publish": "pnpm build && pnpm changeset publish", - "release:validate": "pnpm lint && pnpm typecheck && pnpm test && pnpm build && pnpm registry:check && pnpm test:registry:consumer && pnpm test:package:consumer", + "release:validate": "pnpm harness:validate:release", "release:version": "pnpm changeset version && pnpm install --lockfile-only && pnpm registry:build", "test": "pnpm --filter @ai-ui/ui test", "test:e2e": "playwright test", diff --git a/scripts/harness/build-storybook.mjs b/scripts/harness/build-storybook.mjs new file mode 100644 index 0000000..9cee68f --- /dev/null +++ b/scripts/harness/build-storybook.mjs @@ -0,0 +1,62 @@ +import fs from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; +import { spawn } from "node:child_process"; + +import { repoRoot } from "./core.mjs"; + +const docsRoot = path.join(repoRoot, "apps", "docs"); +const finalOutputDir = path.join(repoRoot, ".artifacts", "storybook-static"); +const tempRootDir = path.join(repoRoot, ".artifacts", "storybook-builds"); + +async function run(command, args, options = {}) { + await new Promise((resolve, reject) => { + const child = spawn(command, args, { + cwd: repoRoot, + stdio: "inherit", + ...options + }); + + child.on("error", reject); + child.on("exit", (code) => { + if (code === 0) { + resolve(); + return; + } + + reject(new Error(`${command} ${args.join(" ")} exited with code ${code ?? "unknown"}`)); + }); + }); +} + +async function main() { + await fs.mkdir(tempRootDir, { recursive: true }); + + const tempDir = await fs.mkdtemp(path.join(tempRootDir, `${os.hostname().replace(/[^a-zA-Z0-9_-]/g, "-")}-`)); + + try { + await run( + "pnpm", + [ + "exec", + "storybook", + "build", + "--disable-telemetry", + "--output-dir", + tempDir + ], + { + cwd: docsRoot, + env: process.env + } + ); + + await fs.rm(finalOutputDir, { recursive: true, force: true }); + await fs.rename(tempDir, finalOutputDir); + } catch (error) { + await fs.rm(tempDir, { recursive: true, force: true }); + throw error; + } +} + +await main();