Skip to content

ci(release): publish to npm with trusted publishing - #444

Merged
ianwremmel merged 2 commits into
mainfrom
clc-1155-release-job-on-main-fails-npm-rejects-npm_token
Sep 12, 2026
Merged

ci(release): publish to npm with trusted publishing#444
ianwremmel merged 2 commits into
mainfrom
clc-1155-release-job-on-main-fails-npm-rejects-npm_token

Conversation

@ianwremmel-ai-agent

@ianwremmel-ai-agent ianwremmel-ai-agent commented Sep 12, 2026

Copy link
Copy Markdown
Collaborator

Fixes CLC-1155.

npm revoked classic tokens, so the release job has failed at verifyConditions with EINVALIDNPMTOKEN on every push to main since the last successful release in 2025-04. This switches the job to npm trusted publishing: it authenticates with GitHub's OIDC token, needs no NPM_TOKEN secret, and npm attaches provenance to every published package.

Changes

  • push.yml: the release job gets permissions: {contents: write, id-token: write} and no longer receives NPM_TOKEN. Declaring permissions drops every unlisted scope, so contents: write is 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/npm 13.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.
  • Dropped @semrel-extra/npm. It memoized npm whoami to 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.
  • Pinned mime ^3 at the root. semantic-release 25 pulls @semantic-release/github 12, whose ESM-only mime 4 otherwise hoists to the root, where @types/send resolves it and tool-stack:build:types fails with TS1479. The pin restores the tree main has; @semantic-release/github gets a nested mime 4.
  • jest.config.ts becomes jest.config.mjs. Jest 29 needs ts-node for a .ts config, and ts-node was only installed because of a stale lockfile entry that the regenerated tree dropped. The .mjs config loads natively with the same settings.

Verified locally

  • npx multi-semantic-release --dry-run --deps.release=inherit --ignore-private-packages loads all 32 public packages through semantic-release 25.0.9 and exits 0.
  • npm publish . --dry-run with 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.json no longer reports the @types/send error.
  • npm test -- --selectProjects 'Unit Tests': 16 suites, 142 tests pass.
  • prettier --check and eslint on 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:

Field Value
Organization or user code-like-a-carpenter
Repository workbench
Workflow filename push.yml
Environment name leave blank

Packages: 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 verifyConditions with ENONPMTOKEN, which fails the whole run, so all 32 need it before the first release.

After the first successful release, the NPM_TOKEN repository 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 repository in each package.json, which here is git@github.com:code-like-a-carpenter/workbench.git. npm normalizes that to git+ssh://git@github.com/... and the registry compares host, owner, and repo, so it should match. If a publish is rejected on that check, set NPM_CONFIG_PROVENANCE: false on the release step as a stopgap and fix the URLs in a follow-up.

@linear-code

linear-code Bot commented Sep 12, 2026

Copy link
Copy Markdown

CLC-1155

@socket-security

socket-security Bot commented Sep 12, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Updatedmulti-semantic-release@​3.0.2 ⏵ 3.1.086100100 +183 +3100
Updated@​semantic-release/​npm@​9.0.2 ⏵ 13.1.59810010090100
Updatedsemantic-release@​19.0.5 ⏵ 25.0.997 +110010096 +1100

View full report

@socket-security

socket-security Bot commented Sep 12, 2026

Copy link
Copy Markdown

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.

Action Severity Alert  (click "▶" to expand/collapse)
Warn High
Obfuscated code: npm highlight.js is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: package-lock.jsonnpm/semantic-release@25.0.9npm/highlight.js@10.7.3

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/highlight.js@10.7.3. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

View full report

@nx-cloud

nx-cloud Bot commented Sep 12, 2026

Copy link
Copy Markdown

View your CI Pipeline Execution ↗ for commit b06cca7

Command Status Duration Result
nx run-many --target build ✅ Succeeded 32s View ↗

💡 Verify your cache is correct by running tasks in a sandbox. Read docs ↗


☁️ Nx Cloud last updated this comment at 2026-09-12 02:59:50 UTC

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.
@ianwremmel-ai-agent
ianwremmel-ai-agent force-pushed the clc-1155-release-job-on-main-fails-npm-rejects-npm_token branch from b06cca7 to a6f135a Compare September 12, 2026 02:41
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

codecov Bot commented Sep 12, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 60.90%. Comparing base (7426f4b) to head (906025b).

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.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment thread package.json
"lodash": "^4.17.21",
"markdown-toc": "^1.2.0",
"multi-semantic-release": "^3.0.1",
"mime": "^3.0.0",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why did you add mime?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@ianwremmel
ianwremmel merged commit 56ca1f1 into main Sep 12, 2026
20 checks passed
@ianwremmel
ianwremmel deleted the clc-1155-release-job-on-main-fails-npm-rejects-npm_token branch September 12, 2026 04:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants