diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index ead5039..4353c94 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -22,6 +22,12 @@ jobs: publish: name: publish runs-on: ubuntu-latest + # NPM_TOKEN is an environment secret, and an environment's secrets are only + # visible to a job that declares it. Without this line secrets.NPM_TOKEN is + # empty and npm refuses the publish. Declaring it also means the + # environment's protection rules — reviewers, wait timers, which branches + # and tags may deploy — apply to releases. + environment: production steps: - uses: actions/checkout@v7 @@ -51,6 +57,23 @@ jobs: fi echo "publishing ${pkg}" + # A missing token otherwise surfaces as an npm 403 after the whole gate has + # run, which reads like a permissions problem with the account rather than + # a secret that is not reaching the job. + - name: Check the npm token is present + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + run: | + if [ -n "$NODE_AUTH_TOKEN" ]; then + echo "NPM_TOKEN is reaching this job" + exit 0 + fi + if [ "${{ github.event_name }}" = "release" ]; then + echo "::error::NPM_TOKEN is empty. It must be a secret of the environment this job declares, or a repository secret." + exit 1 + fi + echo "::warning::NPM_TOKEN is empty, so this rehearsal cannot check authentication." + # CI already ran these on the merge commit, but a publish cannot be undone # — npm only allows unpublishing within 72 hours, and never a republish of # the same version. Cheap insurance against releasing from a tag that was diff --git a/README.md b/README.md index bf08b34..33f8630 100644 --- a/README.md +++ b/README.md @@ -273,8 +273,11 @@ is the mistake that otherwise ships a version under the wrong release. Running passes `--dry-run`, so it can never publish; provenance is left to real releases, since a dry run has nothing to attest. -Publishing needs an `NPM_TOKEN` repository secret — an npm **automation** token, -since a classic token fails against an account that requires 2FA for publishing. +Publishing needs an `NPM_TOKEN` secret on the **`production`** environment — an +npm **automation** token, since a classic token fails against an account that +requires 2FA for publishing. The job declares that environment, so its +protection rules apply: restricting *Deployment branches and tags* to `v*` means +only a release tag can ever publish. ### Trying a local build in an app