Skip to content

Commit ccc2cf4

Browse files
authored
Merge pull request #233 from Gitlawb/ci/npm-trusted-publishing
ci: switch npm publishing to trusted publishing (OIDC) + manual backfill
2 parents 2f622ae + 1fddc81 commit ccc2cf4

1 file changed

Lines changed: 59 additions & 23 deletions

File tree

‎.github/workflows/release.yml‎

Lines changed: 59 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,16 @@ on:
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

818
concurrency:
919
group: release-${{ github.ref }}
@@ -14,7 +24,10 @@ permissions: {}
1424

1525
jobs:
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

Comments
 (0)