@@ -53,26 +53,64 @@ jobs:
5353 # release-please cannot update Cargo.lock, so without this job every release
5454 # merge ships a tag whose lockfile disagrees with its manifests (a --locked
5555 # build from the tag fails, and pr-checks builds --locked). Sync the lock on
56- # the release branch so the tagged tree is internally consistent. Runs only
57- # while a release PR is open (the pr output is unset on the merge push that
58- # creates the release).
56+ # the release branch so the tagged tree is internally consistent.
57+ #
58+ # Runs on every push-triggered release run, not only when release-please
59+ # reports the PR: the `pr` output is set only on runs that CREATE or UPDATE
60+ # the release PR, so gating on it would leave an already-open release PR
61+ # (opened before this job existed, or untouched by a non-releasable push)
62+ # permanently without the sync and without check runs. The resolve step
63+ # discovers any open release-please PR itself and no-ops when there is none.
5964 sync-release-lock :
6065 name : Sync Cargo.lock on the release PR
6166 needs : release-please
62- if : ${{ needs.release-please.outputs.pr }}
67+ if : ${{ !cancelled() && github.repository == 'Gitlawb/node' && github.event_name != 'workflow_dispatch' }}
6368 runs-on : ubuntu-latest
6469 permissions :
6570 contents : write
71+ pull-requests : read
72+ # actions: write lets the final step dispatch pr-checks on the release
73+ # branch. Note this grant is repo-wide dispatch rights (GitHub cannot
74+ # scope it to one workflow); acceptable here because reaching this job's
75+ # steps already requires write to main. Needed because every push to the
76+ # release branch (release-please's and the lock sync below) is made with
77+ # GITHUB_TOKEN, which triggers no workflow runs, so without an explicit
78+ # dispatch the release PR's head carries no check runs and the --locked
79+ # gate never validates it.
80+ actions : write
6681 steps :
82+ # Prefer the fresh `pr` output when this run just created/updated the
83+ # release PR; otherwise fall back to querying for an open release-please
84+ # branch (prefix match: the branch carries `--components--` suffixes).
85+ - name : Resolve the open release PR branch
86+ id : resolve
87+ env :
88+ GH_TOKEN : ${{ github.token }}
89+ BRANCH_FROM_OUTPUT : ${{ needs.release-please.outputs.pr && fromJSON(needs.release-please.outputs.pr).headBranchName || '' }}
90+ run : |
91+ branch="$BRANCH_FROM_OUTPUT"
92+ if [ -z "$branch" ]; then
93+ branch=$(gh pr list --repo "$GITHUB_REPOSITORY" --state open \
94+ --json headRefName \
95+ --jq '[.[].headRefName | select(startswith("release-please--"))][0] // empty')
96+ fi
97+ if [ -z "$branch" ]; then
98+ echo "no open release PR; nothing to sync"
99+ fi
100+ echo "branch=$branch" >> "$GITHUB_OUTPUT"
101+
67102 - name : Checkout release branch
103+ if : ${{ steps.resolve.outputs.branch != '' }}
68104 uses : actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
69105 with :
70- ref : ${{ fromJSON(needs.release-please .outputs.pr).headBranchName }}
106+ ref : ${{ steps.resolve .outputs.branch }}
71107
72108 - name : Sync workspace crate versions into Cargo.lock
109+ if : ${{ steps.resolve.outputs.branch != '' }}
73110 run : cargo update --workspace
74111
75112 - name : Commit and push when the lock changed
113+ if : ${{ steps.resolve.outputs.branch != '' }}
76114 run : |
77115 if git diff --quiet Cargo.lock; then
78116 echo "Cargo.lock already in sync"
@@ -84,6 +122,40 @@ jobs:
84122 git commit -m "chore: sync Cargo.lock with the release version bump"
85123 git push
86124
125+ # Every push to the release branch is made with GITHUB_TOKEN (release-
126+ # please's own pushes and the lock sync above), and events created by
127+ # GITHUB_TOKEN start no workflow runs. workflow_dispatch is the documented
128+ # exception GITHUB_TOKEN may trigger, so dispatch pr-checks at the release
129+ # branch to attach check runs to its current head. Unconditional (also on
130+ # the already-in-sync path) because the branch's ORIGINAL head has the
131+ # same no-runs problem. Loud on failure by design: a silent fallback
132+ # would recreate the checkless-head problem, and a job rerun is safe
133+ # (sync no-ops, dispatch retries). Loop-safe: pr-checks is read-only and
134+ # dispatches nothing. Runs after the push ack, so the dispatched ref tip
135+ # is the sync commit. Merge-queue note: this validates the branch tip;
136+ # the enforced merge queue's merge_group run still re-validates the true
137+ # merged result before landing.
138+ - name : Dispatch PR checks on the release branch
139+ if : ${{ steps.resolve.outputs.branch != '' }}
140+ env :
141+ GH_TOKEN : ${{ github.token }}
142+ BRANCH : ${{ steps.resolve.outputs.branch }}
143+ run : |
144+ # A release branch cut before pr-checks gained its workflow_dispatch
145+ # trigger 422s ("does not have a workflow_dispatch trigger" reported
146+ # against the dispatched ref's copy of the file). That is the one
147+ # tolerated failure: it self-heals when release-please next rewrites
148+ # the branch from current main. Anything else fails the job loudly.
149+ out=$(gh api "repos/${GITHUB_REPOSITORY}/actions/workflows/pr-checks.yml/dispatches" \
150+ -f "ref=$BRANCH" 2>&1) && exit 0
151+ if echo "$out" | grep -qi "does not have a workflow_dispatch trigger"; then
152+ echo "release branch predates the dispatch trigger; checks attach after release-please next rebases it"
153+ echo "$out"
154+ exit 0
155+ fi
156+ echo "$out"
157+ exit 1
158+
87159 # Each architecture builds NATIVELY (amd64 on ubuntu-latest, arm64 on
88160 # ubuntu-24.04-arm) and pushes by digest; docker-manifest below stitches the
89161 # digests into the tagged multi-arch image. No QEMU: emulating the arm64
0 commit comments