Skip to content

Commit ec4f687

Browse files
VickyXAI1bcMax
andauthored
ci(publish): stage releases on npm and approve them with 2FA (#244)
The package's trusted publisher on npmjs.com only allows staged publishing, so CI's plain npm publish has been rejected since 3.49.25 (E403 OIDC permission denied for this action). Keep that restriction: CI now runs npm stage publish, and a version goes live only after a maintainer approves it with 2FA. A compromised workflow or release permission can no longer ship a build to users on its own. Drop the NPM_TOKEN fallback from the publish step; trusted publishing needs no long-lived token. Co-authored-by: 1bcMax <viewitter@gmail.com>
1 parent 48d8b68 commit ec4f687

2 files changed

Lines changed: 34 additions & 13 deletions

File tree

‎.github/workflows/publish.yml‎

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,27 @@ jobs:
3939
echo "already_published=false" >> "$GITHUB_OUTPUT"
4040
fi
4141
42-
# npm trusted publishing (OIDC): with this package's Trusted Publisher set
43-
# to this repo + publish.yml on npmjs.com, npm >= 11.5.1 exchanges the
44-
# job's id-token for a one-off publish credential, so no long-lived
45-
# NPM_TOKEN is needed. The token stays below as a fallback until every
46-
# package is switched over.
47-
- name: Upgrade npm for trusted publishing
48-
run: npm install -g npm@^11.5.1
49-
- name: Publish to npm
42+
# npm trusted publishing (OIDC) with staged publishing. The package's
43+
# Trusted Publisher on npmjs.com (BlockRunAI/Franklin + publish.yml) is
44+
# allowed to *stage* only, never to publish. This job uploads the version
45+
# in a staged state that nobody can install; it goes live only when a
46+
# maintainer runs `npm stage approve <stage-id>` (or approves on
47+
# npmjs.com), which requires 2FA. A compromised workflow or a stolen
48+
# release permission therefore cannot ship a Franklin build to users.
49+
# No long-lived npm token is used: npm exchanges the job's id-token.
50+
- name: Upgrade npm for staged trusted publishing
51+
run: npm install -g npm@^11.20.0
52+
- name: Stage for publishing
5053
if: steps.check.outputs.already_published != 'true'
51-
run: npm publish --provenance --access public
52-
env:
53-
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
54+
run: |
55+
npm stage publish --provenance --access public
56+
{
57+
echo "### Staged, not yet live"
58+
echo ""
59+
echo "Approve with 2FA to publish:"
60+
echo ""
61+
echo '```'
62+
echo "npx -y npm@11 stage list @blockrun/franklin"
63+
echo "npx -y npm@11 stage approve <stage-id>"
64+
echo '```'
65+
} >> "$GITHUB_STEP_SUMMARY"

‎CONTRIBUTING.md‎

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,17 @@ and the ADRs under `docs/adr/`.
7777
Patches land on `main` and ship via `chore(release): X.Y.Z — …`
7878
commits that bump `package.json` and prepend a `CHANGELOG.md` entry.
7979
A matching narrative goes under `docs/release-notes/YYYY-MM-DD-*.md`.
80-
The `npm publish` step is currently driven manually by the maintainer;
81-
the GitHub Release tag is the canonical record.
80+
Publishing a GitHub Release (tag `vX.Y.Z`) runs `.github/workflows/publish.yml`,
81+
which **stages** the version on npm through trusted publishing. A staged
82+
version is not installable until a maintainer approves it with 2FA:
83+
84+
```bash
85+
npx -y npm@11 stage list @blockrun/franklin
86+
npx -y npm@11 stage approve <stage-id>
87+
```
88+
89+
Check the workflow run before approving: the tag must point at the commit you
90+
meant to ship. The GitHub Release tag is the canonical record.
8291

8392
## License
8493

0 commit comments

Comments
 (0)