From a75167fab2da69e53b4ade7abe54d1bb7d90cef2 Mon Sep 17 00:00:00 2001 From: Tom Boucher Date: Mon, 13 Apr 2026 08:56:12 -0400 Subject: [PATCH] fix(ci): address 5 pipeline integrity issues from release audit (#4119) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - version-stamp.mjs: regenerate package-lock.json after dev version stamp (mirrors the same fix applied to bump-version.mjs in #4116) - bump-version.mjs: regenerate root and web/package-lock.json after version bump so both lockfiles are always in sync at release time - pipeline.yml: add post-bump validation step that verifies all package.json files parse as valid JSON before the release commit is made - pipeline.yml: split "Commit, tag, and push" — commit+tag+rebase happen before build, but git push is deferred until after build and npm publish both succeed, preventing a broken tag from landing on main - pipeline.yml: emit a ::warning:: annotation when live LLM tests fail so failures are visible in the Actions UI instead of silently swallowed Closes #4118 Co-authored-by: Claude Sonnet 4.6 --- .github/workflows/pipeline.yml | 22 +++++++++++++++++----- scripts/bump-version.mjs | 13 ++++++++++--- scripts/version-stamp.mjs | 13 +++++++++++-- 3 files changed, 38 insertions(+), 10 deletions(-) diff --git a/.github/workflows/pipeline.yml b/.github/workflows/pipeline.yml index d1dd34ee2..7ab9807c2 100644 --- a/.github/workflows/pipeline.yml +++ b/.github/workflows/pipeline.yml @@ -177,7 +177,7 @@ jobs: - name: Run live LLM tests (optional) continue-on-error: true - run: npm run test:live + run: npm run test:live || echo "::warning::Live LLM tests failed — non-blocking, but worth investigating" env: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} @@ -197,21 +197,26 @@ jobs: RELEASE_VERSION: ${{ steps.release.outputs.version }} run: node scripts/bump-version.mjs "$RELEASE_VERSION" + - name: Validate package files after version bump + run: | + node -e "require('./package.json')" && \ + node -e "require('./packages/pi-coding-agent/package.json')" && \ + node -e "require('./pkg/package.json')" && \ + echo "All package.json files are valid" + - name: Update CHANGELOG.md run: node scripts/update-changelog.mjs /tmp/changelog-entry.md - - name: Commit, tag, and push + - name: Commit and tag release env: RELEASE_VERSION: ${{ steps.release.outputs.version }} run: | git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" - git add package.json package-lock.json CHANGELOG.md native/npm/*/package.json pkg/package.json packages/pi-coding-agent/package.json + git add package.json package-lock.json web/package-lock.json CHANGELOG.md native/npm/*/package.json pkg/package.json packages/pi-coding-agent/package.json git commit -m "release: v${RELEASE_VERSION}" git tag "v${RELEASE_VERSION}" git pull --rebase origin main - git push origin main - git push origin "v${RELEASE_VERSION}" - name: Build release run: npm run build @@ -231,6 +236,13 @@ jobs: fi } + - name: Push release commit and tag + env: + RELEASE_VERSION: ${{ steps.release.outputs.version }} + run: | + git push origin main + git push origin "v${RELEASE_VERSION}" + - name: Create GitHub Release env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/scripts/bump-version.mjs b/scripts/bump-version.mjs index 35595290c..4f7286ceb 100644 --- a/scripts/bump-version.mjs +++ b/scripts/bump-version.mjs @@ -3,7 +3,7 @@ * Bump version in package.json, then sync platform packages and pkg/package.json. * Usage: node scripts/bump-version.mjs */ -import { readFileSync, writeFileSync } from "fs"; +import { readFileSync, writeFileSync, existsSync } from "fs"; import { resolve, dirname } from "path"; import { execSync } from "child_process"; import { fileURLToPath } from "url"; @@ -38,7 +38,14 @@ execSync("node native/scripts/sync-platform-versions.cjs", { cwd: root, stdio: " // 4. Sync pkg/package.json (reads from pi-coding-agent) execSync("node scripts/sync-pkg-version.cjs", { cwd: root, stdio: "inherit" }); -// 5. Regenerate package-lock.json to match the new version. +// 5. Regenerate root package-lock.json to match the new version. // --package-lock-only updates the lockfile in-place without touching node_modules. -execSync("npm install --package-lock-only", { cwd: root, stdio: "inherit" }); +execSync("npm install --package-lock-only --ignore-scripts", { cwd: root, stdio: "inherit" }); console.log(`[bump-version] package-lock.json regenerated at ${newVersion}`); + +// 6. Regenerate web/package-lock.json if the web app is present. +const webDir = resolve(root, "web"); +if (existsSync(webDir)) { + execSync("npm install --package-lock-only --ignore-scripts", { cwd: webDir, stdio: "inherit" }); + console.log(`[bump-version] web/package-lock.json regenerated`); +} diff --git a/scripts/version-stamp.mjs b/scripts/version-stamp.mjs index b673b424d..72b5c81b5 100644 --- a/scripts/version-stamp.mjs +++ b/scripts/version-stamp.mjs @@ -1,5 +1,10 @@ import { readFileSync, writeFileSync } from "fs"; -import { execFileSync } from "child_process"; +import { execFileSync, execSync } from "child_process"; +import { fileURLToPath } from "url"; +import { dirname, resolve } from "path"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const root = resolve(__dirname, ".."); const pkgPath = new URL("../package.json", import.meta.url); const pkg = JSON.parse(readFileSync(pkgPath, "utf8")); @@ -9,5 +14,9 @@ const devVersion = `${pkg.version}-dev.${shortSha}`; pkg.version = devVersion; writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n"); - console.log(`Stamped version: ${devVersion}`); + +// Regenerate package-lock.json to reflect the stamped dev version. +// --package-lock-only updates the lockfile in-place without touching node_modules. +execSync("npm install --package-lock-only --ignore-scripts", { cwd: root, stdio: "inherit" }); +console.log(`[version-stamp] package-lock.json regenerated at ${devVersion}`);