From 4abcdc7d0ae45fed0d55c0815646fb8b9b3d9492 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 1 Sep 2026 10:49:31 +0000 Subject: [PATCH] ci: gate generated-artifact drift, and stop builds mutating tracked source The repo commits the generated artifacts of @epilot/sdk and @epilot/cli, but nothing verified the committed copies matched their generators. CI ran generate-sdk and build on every push, tested the result and discarded it, so drift accumulated silently. Two gates in the test job: - after running both generators, fail if the tree is dirty, so a PR that changes a spec without regenerating is rejected with the command to run. - directly after `pnpm build`, fail if the tree is dirty, so a build can never quietly regenerate or re-fetch committed source. Checked before `pnpm test` so a dirty tree can only be the build's doing. The auto-release job now regenerates the CLI after bumping its version and stages all of packages/cli/. It previously staged only package.json: the pre-bump "Build and test CLI" step generated src/index.ts with the old version and "Rebuild CLI with bumped version" runs after the push, so neither produced output the release commit could carry. Gate 2 needs two client builds to stop generating. journey-client and configuration-hub-client were the only 2 of 52 running codegen from `build`; journey's `npm run openapi` fetched the live spec from docs.api.epilot.io and overwrote committed source, so a build had a network dependency and its result varied with whatever was deployed. Both now match the other 50: `build` only compiles, `typegen` moved to `prepublishOnly` so published tarballs still carry freshly generated types. Updating a spec stays the deliberate `npm run openapi` step from CONTRIBUTING.md. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01MhYbcfHQfe7bk2R37P75zV --- .github/workflows/ci.yml | 37 ++++++++++++++++++- clients/configuration-hub-client/package.json | 4 +- clients/journey-client/package.json | 4 +- 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 03b8efe70..7791caa03 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,7 +38,34 @@ jobs: cache: "pnpm" - run: pnpm install --frozen-lockfile - run: pnpm generate-sdk + - run: pnpm --filter @epilot/cli generate + + # The repo commits the generated artifacts of both @epilot/sdk and + # @epilot/cli. Regenerating above and finding no diff is what keeps them + # honest — without this the generators run, the output is thrown away, + # and the committed copies drift unnoticed. + - name: Check generated artifacts are up to date + run: | + if ! git diff --quiet; then + echo "::error::Generated artifacts are out of date. Run 'pnpm generate-sdk && pnpm --filter @epilot/cli generate' and commit the result." + git diff --stat + exit 1 + fi + - run: pnpm build + + # A build must not mutate tracked source: no code generation, no network + # fetches of live specs. Updating a spec is the deliberate + # 'npm run openapi' step documented in CONTRIBUTING.md. Checked directly + # after the build so a dirty tree can only be the build's doing. + - name: Check build left the tree clean + run: | + if ! git diff --quiet; then + echo "::error::'pnpm build' modified tracked files. A build must not regenerate or re-fetch committed source." + git diff --stat + exit 1 + fi + - run: pnpm test auto-release: @@ -145,7 +172,15 @@ jobs: pkg.version = '${CLI_VERSION}'; fs.writeFileSync(path, JSON.stringify(pkg, null, 2) + '\n'); " - FILES_TO_ADD="$FILES_TO_ADD packages/cli/package.json packages/cli-wrapper/package.json" + # src/index.ts embeds CLI_VERSION and definitions/ are copied from + # clients/, so the generated artifacts have to be rebuilt after the + # bump and staged with it. The "Build and test CLI" step above ran + # before the bump, and "Rebuild CLI with bumped version" runs after + # the push, so neither produces output this commit can carry. + cd "$GITHUB_WORKSPACE" + pnpm --filter @epilot/cli generate + + FILES_TO_ADD="$FILES_TO_ADD packages/cli/ packages/cli-wrapper/package.json" TAGS="$TAGS @epilot/cli@${CLI_VERSION}" fi diff --git a/clients/configuration-hub-client/package.json b/clients/configuration-hub-client/package.json index 25f63351d..9b3898a10 100644 --- a/clients/configuration-hub-client/package.json +++ b/clients/configuration-hub-client/package.json @@ -10,8 +10,8 @@ "bundle-definition": "webpack", "openapi": "node ../../scripts/update-openapi.js https://docs.api.epilot.io/configuration-hub.yaml", "typegen": "openapi typegen src/openapi.json --client > src/openapi.d.ts", - "build": "npm run typegen && npm run typescript && npm run bundle-definition", - "prepublishOnly": "npm run build" + "build": "npm run typescript && npm run bundle-definition", + "prepublishOnly": "npm run typegen && npm run build" }, "files": [ "*.js", diff --git a/clients/journey-client/package.json b/clients/journey-client/package.json index 9d1b41661..e5f0f7f4e 100644 --- a/clients/journey-client/package.json +++ b/clients/journey-client/package.json @@ -10,9 +10,9 @@ "bundle-definition": "webpack", "openapi": "node ../../scripts/update-openapi.js https://docs.api.epilot.io/journey-config.yaml", "typegen": "openapi typegen src/openapi.json --client > src/openapi.d.ts", - "build": "npm run openapi && npm run typegen && npm run typescript && npm run bundle-definition", + "build": "npm run typescript && npm run bundle-definition", "eslint": "../node_modules/.bin/eslint -c ../.eslintrc.yml", - "prepublishOnly": "npm run build" + "prepublishOnly": "npm run typegen && npm run build" }, "files": [ "*.js",