fix(release): heal package-lock on partial semantic-release failure#1774
Open
rainer-friederich wants to merge 1 commit into
Open
fix(release): heal package-lock on partial semantic-release failure#1774rainer-friederich wants to merge 1 commit into
rainer-friederich wants to merge 1 commit into
Conversation
The Release job's "Sync package-lock.json after release" step was gated `if: success()`, so when semantic-release published + pushed a package's release commit and then failed later in the same `-ws` run (e.g. a subsequent workspace whose npm OIDC exchange 404s — a new, not-yet-published package with no trusted-publisher binding), the heal was SKIPPED. main was left with a drifted root package-lock.json and every open PR went red at `Verify package-lock.json is in sync` — the exact scenario that most needs the re-sync. - Change the gate to `if: success() || failure()` (keep continue-on-error) so a partial SR failure still triggers the lock re-sync. Cancellation is excluded so a job cancelled mid-push cannot race in a commit. - Add a HEAD-is-release-commit guard so the failure() path only commits when a `chore(release): ...` commit was actually pushed this run; on an early failure (e.g. npm ci) it no-ops instead of committing a spurious lock diff. - Extend RELEASE-RUNBOOK Failure mode 7 with the partial-SR-failure variant, the HEAD guard, and the root cause (new publishable package without an npm trusted-publisher binding; the `reading 'map'` TypeError is secondary noise). Real example: data-access@4.0.0 published, then the run failed at ticket-client (#1701, never published, absent from setup PACKAGES). Fixes #1773 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
1. Abstract
Makes the Release job's
Sync package-lock.json after releaseself-heal fire on a partialSemantic Releasefailure, not only on a fully green release, so a release that publishes one package and then fails on a later one no longer leavesmain's rootpackage-lock.jsondrifted. Also extends RELEASE-RUNBOOK Failure mode 7 with this variant and its root cause.2. Reasoning
The
4.0.0release ofspacecat-shared-data-accesspublished and pushed itschore(release)commit, then the same run failed later atspacecat-shared-ticket-client(404 - package not foundon the OIDC exchange →ENONPMTOKEN). Because the sync step was gatedif: success(), the upstreamSemantic Releasefailure short-circuited it — the step was skipped, not run. Somainwas left pinningdata-access@3.81.0in the lock whilepackage.jsonsaid4.0.0, and every open PR went red atVerify package-lock.json is in syncwith a diff it did not author. The single scenario that most needs the re-sync (a release commit was pushed, then SR failed) was exactly the one thesuccess()gate missed.3. High-level overview of the changes
Behaviour delta in
.github/workflows/main.yaml(Release job, sync step):if: success()→if: success() || failure()(keepscontinue-on-error: true). A partialSemantic Releasefailure now still triggers the lockfile re-sync. Cancellation stays excluded (notalways()), so a job cancelled mid-push cannot race in a commit.chore(release): …commit (the subject semantic-release pushes). On thefailure()path this ensures it only commits when a release commit was actually pushed this run; on a failure before any release was committed (e.g.npm cifailed) HEAD is the triggering feat/fix commit, so the step no-ops instead of committing a spurious lock diff. The existinggit diff --quietdrift check is unchanged.Net effect across cases: clean-release-with-drift still heals (unchanged); the partial-failure case from the linked issue now heals (the fix); every other case (nothing released, early failure, cancellation) safely no-ops or is skipped.
Docs (
docs/RELEASE-RUNBOOK.md, Failure mode 7): documents thesuccess() || failure()gate + HEAD guard, and adds an "Underlying cause" subsection describing the real trigger — a newly-added publishable package with no npm trusted-publisher binding (OIDC404→ENONPMTOKEN), noting the accompanyingreading 'map'TypeError is secondary noise, and pointing to Failure mode 1 for the binding bootstrap.4. Required information
6. Additional information outside the code
28669792110(Actions logs): confirmeddata-access@4.0.0published + pushed its release commit at 15:45, then the-wsloop failed atspacecat-shared-ticket-client'sverifyConditionsat 15:46 withOIDC token exchange failed: 404 - package not found→ENONPMTOKEN No npm token specified. TheTypeError: ... reading 'map'originates insemantic-release-monorepo/src/only-package-commits.js:26and is a secondary crash, not the cause.npm view @adobe/spacecat-shared-ticket-clientreturnsE404(never published), and it is absent fromPACKAGESinscripts/setup-npm-trusted-publishers.sh. The bootstrap of that binding is deliberately out of scope for this PR (tracked separately); until it is done, releases keep half-failing — but with this fixmain's lock auto-heals each time so PRs are no longer taxed.success() || failure(); the embedded shell (HEAD guard + regen) was checked against the full scenario matrix (clean / no-drift / nothing-released / partial-failure / early-failure / cancelled).7. Test plan
main, so it cannot be exercised on a branch. Verified statically instead — parsed the workflow, confirmed the resolved gate, and walked the step's guard logic through each release outcome (see section 6).main, next release): on the next merge that releases a package, confirm theSync package-lock.json after releasestep runs. To exercise the fix specifically, after any partialSemantic Releasefailure that already pushed achore(release)commit, confirm the step runs (green ✓) and pushes achore(release): sync package-lock.json after release [skip ci]commit, and that subsequent unrelated PRs stay green atVerify package-lock.json is in sync(i.e. no manual FM-7 lockfile PR needed).🤖 Generated with Claude Code