ci(release): publish to npm with trusted publishing - #444
Conversation
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
|
Warning Review the following alerts detected in dependencies. According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.
|
|
View your CI Pipeline Execution ↗ for commit b06cca7
💡 Verify your cache is correct by running tasks in a sandbox. Read docs ↗ ☁️ Nx Cloud last updated this comment at |
npm revoked classic tokens, so every release on main has failed at verifyConditions with EINVALIDNPMTOKEN since 2025-04. The release job now authenticates with GitHub's OIDC token instead of an NPM_TOKEN secret, which also makes npm attach provenance to each package. @semantic-release/npm gained trusted publishing in 13.1.0, which needs semantic-release 25 and multi-semantic-release 3.1.0. @semrel-extra/npm is dropped: it only memoized the npm whoami check that does not run under OIDC, and it deep-imports a path that @semantic-release/npm 13 no longer exports. semantic-release 25 pulls @semantic-release/github 12, whose ESM-only mime 4 would otherwise hoist to the root, where @types/send resolves it and tsc fails with TS1479. Pinning mime ^3 at the root keeps the CommonJS build @types/send expects; @semantic-release/github gets its own nested copy. Each package still needs a trusted publisher configured on npmjs.com (org code-like-a-carpenter, repo workbench, workflow push.yml) before the job can succeed.
b06cca7 to
a6f135a
Compare
Jest 29 shells out to ts-node for a .ts config file. ts-node was only present because a stale lockfile entry kept it around; nothing declares it, and regenerating the tree for the release tooling upgrade dropped it, which made every jest run fail before collecting tests. A .mjs config loads natively and carries the same settings.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #444 +/- ##
==========================================
+ Coverage 60.78% 60.90% +0.12%
==========================================
Files 29 29
Lines 1614 1614
Branches 360 360
==========================================
+ Hits 981 983 +2
+ Misses 629 627 -2
Partials 4 4 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
| "lodash": "^4.17.21", | ||
| "markdown-toc": "^1.2.0", | ||
| "multi-semantic-release": "^3.0.1", | ||
| "mime": "^3.0.0", |
There was a problem hiding this comment.
It keeps the type build green after the semantic-release upgrade.
semantic-release 25 depends on @semantic-release/github 12, which depends on mime 4. mime 4 is ESM-only. With nothing else asking for mime, npm hoists it to node_modules/mime, and @types/send (via @types/express) does import * as m from "mime" from a CommonJS declaration file, so tool-stack:build:types fails with TS1479. That was the first CI failure on this PR (run 34667894814).
On main, node_modules/mime is 3.0.0, a CommonJS build, only because the semantic-release 19 tree happened to depend on mime 3. Declaring mime@^3 at the root restores that layout: root gets 3.0.0 and @semantic-release/github gets its own nested 4.1.0.
Alternatives I looked at: newer @types/send (0.17.6) has the same import, and an overrides entry can't target @types/send because it doesn't declare mime as a dependency. Happy to switch to something else if you'd rather not carry the pin.
Fixes CLC-1155.
npm revoked classic tokens, so the
releasejob has failed atverifyConditionswithEINVALIDNPMTOKENon every push tomainsince the last successful release in 2025-04. This switches the job to npm trusted publishing: it authenticates with GitHub's OIDC token, needs noNPM_TOKENsecret, and npm attaches provenance to every published package.Changes
push.yml: thereleasejob getspermissions: {contents: write, id-token: write}and no longer receivesNPM_TOKEN. Declaringpermissionsdrops every unlisted scope, socontents: writeis explicit to keep tag pushes working.@semantic-release/npm^13.1.5,semantic-release^25.0.9,multi-semantic-release^3.1.0. Trusted publishing landed in@semantic-release/npm13.1.0, which requires semantic-release 25; multi-semantic-release 3.1.0 is the release that supports it. The bundled npm CLI is 11.19.1, above the 11.5.1 floor for OIDC.@semrel-extra/npm. It memoizednpm whoamito dodge registry throttling, but that check does not run under OIDC, and it deep-imports@semantic-release/npm/lib/get-pkg, which v13's export map blocks.mime^3 at the root. semantic-release 25 pulls@semantic-release/github12, whose ESM-onlymime4 otherwise hoists to the root, where@types/sendresolves it andtool-stack:build:typesfails with TS1479. The pin restores the treemainhas;@semantic-release/githubgets a nestedmime4.jest.config.tsbecomesjest.config.mjs. Jest 29 needsts-nodefor a.tsconfig, and ts-node was only installed because of a stale lockfile entry that the regenerated tree dropped. The.mjsconfig loads natively with the same settings.Verified locally
npx multi-semantic-release --dry-run --deps.release=inherit --ignore-private-packagesloads all 32 public packages through semantic-release 25.0.9 and exits 0.npm publish . --dry-runwith npm 11.19.1 from a workspace package directory packs and resolves the registry correctly.tsc --project packages/@code-like-a-carpenter/tool-stack/tsconfig.jsonno longer reports the@types/senderror.npm test -- --selectProjects 'Unit Tests': 16 suites, 142 tests pass.prettier --checkandeslinton the changed files pass.Before merging: configure trusted publishers on npmjs.com
For each of the 32
@code-like-a-carpenter/*packages, open Package settings > Trusted publisher > GitHub Actions and enter:code-like-a-carpenterworkbenchpush.ymlPackages: assert, aws-env-loader, cli, cli-core, cli-plugin-example, contract-tests, dotenv, env, errors, exception, foundation-intermediate-representation, foundation-parser, foundation-plugin-cloudformation, foundation-plugin-typescript, foundation-runtime, foundation-transform-environment, graphql-codegen-helpers, interact, lambda-handlers, logger, parallel, sentry, telemetry, tool-deps, tool-foundation, tool-inliner, tool-json-schema, tool-stack, tool-tool, tooling-common, wait-for, workbench-config.
A package without a trusted publisher fails
verifyConditionswithENONPMTOKEN, which fails the whole run, so all 32 need it before the first release.After the first successful release, the
NPM_TOKENrepository secret can be deleted, and each package's publishing access can be set to "Require two-factor authentication and disallow tokens".Residual risk
The registry validates provenance against
repositoryin each package.json, which here isgit@github.com:code-like-a-carpenter/workbench.git. npm normalizes that togit+ssh://git@github.com/...and the registry compares host, owner, and repo, so it should match. If a publish is rejected on that check, setNPM_CONFIG_PROVENANCE: falseon the release step as a stopgap and fix the URLs in a follow-up.