From 69ace1f712cc1e8fa675e1329c30f4fda204c03f Mon Sep 17 00:00:00 2001 From: Kaitlyn Date: Fri, 6 Nov 2026 02:44:51 -0500 Subject: [PATCH] ci(release): let npm publish through Trusted publishing Node 24 brings npm 11, which publishes with the workflow's OIDC identity when npm Trusted publishing trusts release.yml, so the expiring NPM_TOKEN is no longer needed. npm falls back to the token until that is set up, and check-secrets now treats the token as optional. --- .github/workflows/check-secrets.yml | 7 ++++--- .github/workflows/release.yml | 7 +++++-- CONTRIBUTING.md | 11 ++++++++++- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/.github/workflows/check-secrets.yml b/.github/workflows/check-secrets.yml index 219227c..1d7796b 100644 --- a/.github/workflows/check-secrets.yml +++ b/.github/workflows/check-secrets.yml @@ -5,7 +5,8 @@ # the credential works and can reach what the release needs: # - HOMEBREW_TAP_TOKEN must be able to write to dpologdvinity/homebrew-gitwhy (the Homebrew tap) # - GPG_PRIVATE_KEY must import as a secret key that can sign -# NPM_TOKEN, WINGET_TOKEN and GPG_PASSPHRASE are only checked for presence. +# NPM_TOKEN, WINGET_TOKEN and GPG_PASSPHRASE are only checked for presence. NPM_TOKEN is optional +# once npm Trusted publishing is set up for release.yml. name: check-secrets on: @@ -29,10 +30,10 @@ jobs: - name: Which secrets are set run: | failed=0 - for name in HOMEBREW_TAP_TOKEN GPG_PRIVATE_KEY NPM_TOKEN; do + for name in HOMEBREW_TAP_TOKEN GPG_PRIVATE_KEY; do if [ -n "${!name}" ]; then echo "ok: $name is set"; else echo "MISSING: $name (required)"; failed=1; fi done - for name in GPG_PASSPHRASE WINGET_TOKEN; do + for name in GPG_PASSPHRASE WINGET_TOKEN NPM_TOKEN; do if [ -n "${!name}" ]; then echo "ok: $name is set"; else echo "not set: $name (optional)"; fi done exit "$failed" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f9d069a..541e754 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -125,14 +125,17 @@ jobs: timeout-minutes: 10 permissions: contents: read - id-token: write # lets npm record where the package was built (provenance) + id-token: write # trusted publishing, and provenance: where the package was built steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false + # Node 24 brings npm 11, which publishes through npm Trusted publishing (OIDC, using the + # id-token permission above) when it is set up for this workflow on npmjs.com. Until then, or + # if it is ever removed, npm falls back to NPM_TOKEN. - uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 with: - node-version: 22 + node-version: 24 registry-url: https://registry.npmjs.org - run: cp LICENSE THIRD_PARTY_LICENSES README.md npm/ - run: npm version "${GITHUB_REF_NAME#v}" --no-git-tag-version diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index aba934d..02a0b2e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -154,9 +154,18 @@ Repository secrets the release uses (Settings -> Secrets and variables -> Action | `HOMEBREW_TAP_TOKEN` | pushes the Homebrew cask and Scoop manifest to `dpologdvinity/homebrew-gitwhy` (needs Contents read and write on that repository) | yes | | `GPG_PRIVATE_KEY` | signs the apt and dnf repositories | yes | | `GPG_PASSPHRASE` | unlocks that key | only if the key has a passphrase | -| `NPM_TOKEN` | publishes `@kaitlynbassford/gitwhy` | yes | +| `NPM_TOKEN` | publishes `@kaitlynbassford/gitwhy` | only until npm Trusted publishing is set up (see below) | | `WINGET_TOKEN` | opens the winget-pkgs pull request | no (the step is skipped without it) | +npm publishing works without a token once npm Trusted publishing trusts this repository's +`release.yml`. A maintainer with two-factor authentication sets it up once: + +```sh +npm login +npm trust github @kaitlynbassford/gitwhy --repo dpologdvinity/gitwhy --file release.yml +gh secret delete NPM_TOKEN -R dpologdvinity/gitwhy # once npmjs.com shows the next version came from the trusted publisher +``` + Before tagging, run the `check-secrets` workflow from the Actions tab: it reports which secrets are set, and checks that the tap token can write to the tap and that the signing key can sign. It never prints a secret. The `verify-install`