fix: publish via npm trusted publishing - #616
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
decentraland-bot
left a comment
There was a problem hiding this comment.
✅ Approved — OIDC migration is correctly implemented
Clean, well-scoped PR that migrates npm publishing from NPM_TOKEN to OIDC trusted publishing. All three review dimensions (security, deployment correctness, architecture) checked out.
Verification summary
| Check | Status |
|---|---|
id-token: write only on release job |
✅ |
build scoped down to contents: read |
✅ |
deploy gets explicit contents: write |
✅ |
| SHA pins match tags (checkout v6.1.0, setup-node v6.4.0, cache v6.1.0) | ✅ Verified via API |
persist-credentials: false on release checkout |
✅ Correct for OIDC |
No registry-url (avoids .npmrc that breaks OIDC) |
✅ |
fetch-depth: 0 for semantic-release history analysis |
✅ |
NPM_TOKEN fully removed |
✅ |
repository.url → git+https:// for provenance |
✅ |
@semantic-release/npm@13.x auto-detects OIDC |
✅ Confirmed in plugin docs |
semantic-release v25 defaults release from master |
✅ |
ADR-6: PR title (fix: …) and branch (fix/…) |
✅ |
Workflow triggers only on trusted events (push to master/main) |
✅ Per playbook |
Findings
[P2 — CI] Audit workflow failure on this branch
The Audit workflow fails with npm ci errors about missing TypeScript 7.0.2 platform-specific packages in the regenerated lockfile. The build job passes fine. This appears to be a lockfile-strictness issue from the regeneration — the Audit workflow itself uses unpinned actions/checkout@v3 and a different npm/node version than the PR's workflow. Consider regenerating the lockfile with npm install --package-lock-only on a clean node 24 environment, or updating the Audit workflow to match the node version used in master.yml.
[P2 — Pre-existing] deploy job: token-in-URL anti-pattern
The deploy job embeds GITHUB_TOKEN in the git remote URL (https://git:${GITHUB_TOKEN}@...). This is not introduced by this PR, but it exposes the token in git config and potentially in error logs. A follow-up could use actions/checkout with persist-credentials: true for the deploy job instead.
[P2 — Pre-existing] deploy job: unpinned runtime dependency
npm install gh-pages --force --no-save installs an unpinned package at deploy time in a job with contents: write. Not introduced by this PR — consider pinning the version in a follow-up.
Prerequisite reminder
As noted in the PR description, the trusted publisher must be configured on npm for decentraland-ui → decentraland/ui + master.yml before merging, per the npm trusted publishing playbook. Without it, the first post-merge release will fail with ENEEDAUTH.
Reviewed by Jarvis 🤖 · Requested by charly-bg via GitHub
decentraland-bot
left a comment
There was a problem hiding this comment.
Review Summary
Clean, well-scoped migration from static NPM_TOKEN to npm OIDC trusted publishing. All changes are correct and security-improving.
Verified ✅
| Area | Status |
|---|---|
| Action SHA pins | All three verified against upstream tags — checkout → v6.1.0, setup-node → v6.4.0, cache → v6.1.0 |
| Permissions (least-privilege) | build: contents: read only. deploy: contents: write (gh-pages push). release: contents: write + id-token: write + issues/pull-requests: write — each justified and documented in inline comments |
| OIDC auth flow | NPM_TOKEN removed, id-token: write correctly placed on release (was misplaced on build), no registry-url set (would write a placeholder .npmrc that breaks OIDC), persist-credentials: false prevents credential helper conflicts |
| semantic-release 15 → 25 | Required for @semantic-release/npm 13.x OIDC support. Default branches config includes master — no .releaserc needed. fetch-depth: 0 ensures full commit history for changelog generation |
repository.url fix |
git+https:// form is required for npm provenance attestation |
| Audit workflow | Pinning Node 24 aligns npm ci with the npm 11 lockfile resolution — correct fix for the optional peer dep divergence described in the PR |
| Node version label | Old workflow had "Use Node.js 22.x" but already set node-version: 24 — PR fixes the label to match |
| CI status | All checks passing (build, audit, Vercel preview) |
Security Assessment
No security issues found.
- Moving from a long-lived static
NPM_TOKENto short-lived OIDC tokens is strictly more secure - SHA-pinned actions prevent supply-chain attacks via tag retargeting
persist-credentials: falselimits token exposure in the git configpublishConfig.provenance: truewill now actually produce attestations (old@semantic-release/npmbundled npm 6.14.1, which predates provenance)- Only
secrets.GITHUB_TOKEN(ephemeral, auto-scoped) remains — no custom PATs or static tokens
Prerequisite (not a code issue)
The npm registry must have the OIDC trusted publisher configured for this repo/package before the first release run on master. If that's not set up, npm publish will fail with an auth error.
Consumer Impact
No public API surface changes — this PR only modifies CI/CD workflows and build tooling. No downstream consumers are affected.
Reviewed by Jarvis 🤖 · Requested by charly-bg via GitHub
|
🎉 This PR is included in version 7.2.1 🎉 The release is available on: Your semantic-release bot 📦🚀 |
Problem
The
releasejob has failed since the org-wideNPM_TOKENrotation on 2026-06-10, withEINVALIDNPMTOKEN(run 32415817238, #615). Last green release run was 2026-05-31, before the rotation. This repo was never migrated to Trusted Publishing.Fix
NPM_TOKENfrom the semantic-release step; authenticate via npm OIDC trusted publishingid-token: writefrombuild(where it did nothing) torelease, pluscontents/issues/pull-requests: writesemantic-release15 -> 25; OIDC support lives in@semantic-release/npm13.xrepository.urlto thegit+https://form required for provenancefetch-depth: 0to the release checkout; bump actions to node24-runtime SHA pinsAuditworkflow, which had nosetup-nodeat allWhy the audit workflow needed pinning
Auditran on the runner default (Node 22.23.2 / npm 10.9.8) while every other job pins node 24.cosmiconfigdeclares an optional peertypescript >=4.9.5that the root's pinned 4.6.4 cannot satisfy; npm 10 installs that optional peer, npm 11 prunes it. With a second cosmiconfig arriving via semantic-release 25, npm 10 demandedtypescript@7.0.2plus 21 platform packages and failednpm ciwith EUSAGE. Verified against the same lockfile:Provenance
publishConfig.provenancehas been set since 2023 but never produced an attestation: the old plugin bundled npm 6.14.1, which predates provenance. The new plugin bundles npm 11.19.0, which honors it and hard-fails withoutid-token: write- so the permissions change above is what makes provenance actually work.