From 23c2c2aba606846ea45dbf87fb4f02bace2d7a1a Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 9 May 2026 17:49:31 +0000 Subject: [PATCH 1/2] ci(publish): refresh pnpm-lock.yaml after npm publish, not before MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR #403 added `pnpm install --lockfile-only` immediately after the bump step. Under pnpm 10's default `link-workspace-packages=false`, that install tries to resolve the `relayburn` and `@relayburn/sdk` umbrellas' per-platform `optionalDependencies` (`@relayburn/{cli,sdk}-`) at the new RELEASE_VER from the npm registry — but those versions do not exist yet. The publish loop that creates them runs later in the same job, and the "Verify new versions are not yet published" step explicitly asserts the unpublished state. The lockfile refresh therefore failed before any release could go out. Move the refresh to a new step that runs AFTER the npm publish loop, when the new versions are live on the registry. Amend the resulting lockfile diff into the version commit created earlier so the release stays a single atomic commit on `main`. The amend is safe because nothing has been pushed yet — "Tag + push" runs immediately after and pushes both the amended commit and the tags created against it. Skip the refresh on dry runs and on `version: none` re-publishes (no release commit exists to amend, and there are no newly-published versions to resolve). A previous attempt set `link-workspace-packages=true` in `.npmrc` so pnpm would resolve the umbrella optionalDeps to the local workspace packages. That made the lockfile-only step succeed before publish, but it broke local + CI installs: pnpm symlinked the empty workspace platform packages under `node_modules/`, which don't contain the prebuilt napi `.node` binary (that's only built and staged inside the publish workflow), so `@relayburn/sdk` failed to load its native binding and MCP tests broke. https://claude.ai/code/session_01U68Cv49ey4EiLvCR2cikdE --- .github/workflows/publish.yml | 58 ++++++++++++++++++++++++++++++----- 1 file changed, 50 insertions(+), 8 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 539d96e8..bf0ee547 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -434,14 +434,15 @@ jobs: # Refresh Cargo.lock to reflect the new workspace version. cargo update --workspace - # Refresh pnpm-lock.yaml so the workspace specifiers + optional - # platform dep versions match the bumped package.json files. Without - # this, the lock file lags one release behind every publish run - # (e.g. 2.7.0 ships, but the lock still pins 2.6.1 for - # `@relayburn/{cli,sdk}-`), and the next `pnpm install - # --frozen-lockfile` on `main` fails. `--lockfile-only` skips - # node_modules churn — we already installed dependencies above. - pnpm install --lockfile-only + # Note: pnpm-lock.yaml is refreshed AFTER the npm publish loop + # below — see the "Refresh pnpm-lock.yaml" step. Doing it here + # would fail because the umbrella optionalDeps pin exact + # RELEASE_VER specs against `@relayburn/{cli,sdk}-`, but + # those versions don't exist on the npm registry yet (the + # "Verify new versions are not yet published" step right below + # explicitly asserts that). pnpm 10's default + # `link-workspace-packages=false` means it would try to fetch + # those unpublished versions from the registry and abort. # Belt-and-suspenders alongside the precursor's heal: even if the # local→npm baseline was in sync there, the computed release @@ -1112,6 +1113,47 @@ jobs: npm publish "$TARBALL" $COMMON_FLAGS done + # Refresh pnpm-lock.yaml AFTER the publish loop so the umbrella + # `optionalDependencies` (`@relayburn/{cli,sdk}-`) resolve + # against the now-published RELEASE_VER tarballs on the npm + # registry. Doing this before publish would fail under pnpm 10's + # default `link-workspace-packages=false` because the new versions + # don't exist on the registry yet (see the comment in the "Apply + # release version" step above). + # + # Without this refresh, the lock file lags one release behind every + # publish run (e.g. 2.7.0 ships, but the lock still pins 2.6.1 for + # `@relayburn/{cli,sdk}-`), and the next + # `pnpm install --frozen-lockfile` on `main` fails. + # + # The lockfile change is amended into the version commit created by + # the "Commit version bumps" step earlier so the release is one + # atomic commit on `main`. The amend is safe because nothing has + # been pushed yet (the "Tag + push" step below pushes both the + # amended commit and the tags created against it). + # + # `--lockfile-only` skips node_modules churn — we already installed + # dependencies near the top of the job. On dry runs there's nothing + # to amend (the version commit step is skipped) and there are no + # newly-published versions on the registry, so we skip the refresh + # entirely. + - name: Refresh pnpm-lock.yaml against published versions + if: ${{ github.event.inputs.dry_run != 'true' && (github.event.inputs.version != 'none' || github.event.inputs.custom_version != '') }} + run: | + set -euo pipefail + pnpm install --lockfile-only + + if git diff --quiet -- pnpm-lock.yaml; then + echo "pnpm-lock.yaml unchanged after refresh — nothing to amend." + exit 0 + fi + + # The "Commit version bumps" step runs under the same condition + # as this step, so a release commit is guaranteed to exist at + # HEAD when we get here. Amend the lockfile into it. + git add pnpm-lock.yaml + git commit --amend --no-edit + # Annotated tags (-a) so `git push --follow-tags` actually pushes them; # lightweight tags are skipped by --follow-tags. # From a52762f4478c40a3cb0d612113cd87a783269ec9 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 9 May 2026 17:53:01 +0000 Subject: [PATCH 2/2] ci(publish): guard lockfile amend against missing release commit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `Commit version bumps` only commits when `git diff --cached --quiet` is false. If the heal step already brought every package to the target version, `npm version --allow-same-version` rewrites the files identically and produces no diff — the step skips, and HEAD stays on whatever unrelated commit was there before. The new lockfile-refresh step would then `git commit --amend` into that commit, rewriting someone else's history. Guard by checking that HEAD's subject starts with "chore(release):" (the prefix produced by `Commit version bumps`). If it doesn't, skip the amend with a workflow warning so the release proceeds normally — there's just no lockfile change to record because there was no version change to begin with. Caught by CodeRabbit on PR #404. https://claude.ai/code/session_01U68Cv49ey4EiLvCR2cikdE --- .github/workflows/publish.yml | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index bf0ee547..242d8f9d 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1149,8 +1149,19 @@ jobs: fi # The "Commit version bumps" step runs under the same condition - # as this step, so a release commit is guaranteed to exist at - # HEAD when we get here. Amend the lockfile into it. + # as this step, but it no-ops if `git diff --cached --quiet` is + # true (e.g. the heal step already brought every package to the + # release version, so `npm version --allow-same-version` rewrote + # the files identically). In that case HEAD is some unrelated + # prior commit, and amending the lockfile into it would rewrite + # someone else's history. Guard by requiring the HEAD subject to + # match the release commit format ("chore(release):") that the + # version commit step produces; bail out cleanly otherwise. + HEAD_SUBJECT=$(git log -1 --format=%s) + if [[ "$HEAD_SUBJECT" != chore\(release\):* ]]; then + echo "::warning title=Skipped lockfile amend::HEAD is not a release commit (\"$HEAD_SUBJECT\") — the version bump produced no diff to commit, so there is nothing to amend the lockfile into. Skipping." + exit 0 + fi git add pnpm-lock.yaml git commit --amend --no-edit