44 push :
55 branches :
66 - main
7+ # Manual backfill: publish an already-cut release to npm (e.g. after the
8+ # npm side of trusted publishing is configured, or a publish was missed).
9+ # Only the npm-publish job runs; binaries are downloaded from the existing
10+ # GitHub release for the given tag.
11+ workflow_dispatch :
12+ inputs :
13+ npm_backfill_tag :
14+ description : " Existing release tag to publish to npm (e.g. v0.6.0)"
15+ required : true
16+ type : string
717
818concurrency :
919 group : release-${{ github.ref }}
@@ -14,7 +24,10 @@ permissions: {}
1424
1525jobs :
1626 release-please :
17- if : ${{ github.repository == 'Gitlawb/node' }}
27+ # Skipped on manual dispatch: a backfill must never create or advance a
28+ # release PR as a side effect (npm-publish tolerates the skipped need via
29+ # !cancelled(), and every other job gates on release_created == 'true').
30+ if : ${{ github.repository == 'Gitlawb/node' && github.event_name != 'workflow_dispatch' }}
1831 name : Release Please
1932 runs-on : ubuntu-latest
2033 permissions :
@@ -248,37 +261,62 @@ jobs:
248261 npm-publish :
249262 name : Publish to npm
250263 needs : [release-please, release-binaries]
251- if : ${{ needs.release-please.outputs.release_created == 'true' }}
264+ # Runs on a fresh release, or on manual dispatch for an existing tag
265+ # (release-binaries is skipped on dispatch, hence !cancelled()). Publishing
266+ # auth is npm Trusted Publishing (GitHub OIDC) — no NPM_TOKEN. Each
267+ # @gitlawb package must have this repo + workflow (release.yml) configured
268+ # as its trusted publisher on npmjs.com, or publish fails loudly here —
269+ # deliberately loud: the silent secret-guard skip is how 0.4.x–0.6.0
270+ # never reached npm.
271+ if : >-
272+ ${{ !cancelled() && (
273+ (needs.release-please.outputs.release_created == 'true' && needs.release-binaries.result == 'success') ||
274+ (github.event_name == 'workflow_dispatch' && inputs.npm_backfill_tag != '')
275+ ) }}
252276 runs-on : ubuntu-latest
253277 permissions :
254278 contents : read
255- id-token : write # npm provenance
279+ id-token : write # npm trusted publishing (OIDC) + provenance
256280 steps :
257- - name : Guard on secret
258- id : guard
281+ - name : Resolve release tag
282+ id : rel
259283 env :
260- NPM_TOKEN : ${{ secrets.NPM_TOKEN }}
284+ DISPATCH_TAG : ${{ inputs.npm_backfill_tag }}
285+ RELEASE_TAG : ${{ needs.release-please.outputs.tag_name }}
261286 run : |
262- if [ -z "${NPM_TOKEN:-}" ]; then
263- echo "::warning::NPM_TOKEN is not set — skipping npm publish."
264- echo "enabled=false" >> "$GITHUB_OUTPUT"
265- else
266- echo "enabled=true" >> "$GITHUB_OUTPUT"
267- fi
287+ set -euo pipefail
288+ TAG="${DISPATCH_TAG:-$RELEASE_TAG}"
289+ case "$TAG" in
290+ v[0-9]*) ;;
291+ *) echo "::error::'$TAG' does not look like a release tag (vX.Y.Z)"; exit 1 ;;
292+ esac
293+ echo "tag=$TAG" >> "$GITHUB_OUTPUT"
294+ echo "version=${TAG#v}" >> "$GITHUB_OUTPUT"
268295
269296 - name : Checkout release tag
270- if : ${{ steps.guard.outputs.enabled == 'true' }}
271297 uses : actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
272298 with :
273- ref : ${{ needs.release-please .outputs.tag_name }}
299+ ref : ${{ steps.rel .outputs.tag }}
274300 persist-credentials : false
275301
302+ # npm >= 11.5.1 performs the OIDC token exchange automatically when the
303+ # package has a trusted publisher configured; older npm silently falls
304+ # back to (absent) token auth and fails confusingly.
305+ - name : Set up Node 24 + OIDC-capable npm
306+ uses : actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
307+ with :
308+ node-version : " 24"
309+ - name : Ensure npm supports trusted publishing
310+ run : |
311+ set -euo pipefail
312+ npm install -g npm@^11.5.1
313+ npm --version
314+
276315 - name : Lay in release binaries
277- if : ${{ steps.guard.outputs.enabled == 'true' }}
278316 env :
279317 GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
280- VERSION : ${{ needs.release-please .outputs.version }}
281- TAG : ${{ needs.release-please .outputs.tag_name }}
318+ VERSION : ${{ steps.rel .outputs.version }}
319+ TAG : ${{ steps.rel .outputs.tag }}
282320 run : |
283321 set -euo pipefail
284322 # npm platform package -> Rust target triple (unix only; Windows is not
@@ -305,9 +343,8 @@ jobs:
305343 done
306344
307345 - name : Set versions
308- if : ${{ steps.guard.outputs.enabled == 'true' }}
309346 env :
310- VERSION : ${{ needs.release-please .outputs.version }}
347+ VERSION : ${{ steps.rel .outputs.version }}
311348 run : |
312349 set -euo pipefail
313350 node -e '
@@ -327,13 +364,12 @@ jobs:
327364 '
328365
329366 - name : Publish
330- if : ${{ steps.guard.outputs.enabled == 'true' }}
331367 env :
332- NPM_TOKEN : ${{ secrets.NPM_TOKEN }}
333- VERSION : ${{ needs.release-please.outputs.version }}
368+ VERSION : ${{ steps.rel.outputs.version }}
334369 run : |
335370 set -euo pipefail
336- echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc
371+ # No token: npm exchanges this job's GitHub OIDC identity with the
372+ # registry (trusted publishing); provenance is attested automatically.
337373 # Platform packages first, then the wrapper (so its optionalDependencies resolve).
338374 # Skip versions already on the registry so a rerun after a partial publish
339375 # is idempotent instead of erroring on the first existing package.
0 commit comments