From e5dde566c0d4ee1289ef0c25ef83758bda46cfbc Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Fri, 21 Aug 2026 17:41:15 -0300 Subject: [PATCH 01/10] chore: point published packages at the correct repository npm generates provenance attestations automatically when publishing with trusted publishing (OIDC), and rejects the publish when `repository.url` is missing or does not match the repository the workflow ran in. Six published packages had no `repository` field at all; `message-parser` and `peggy-loader` still pointed at `RocketChat/fuselage`. Co-Authored-By: Claude Opus 5 (1M context) --- packages/api-client/package.json | 5 +++++ packages/apps-engine/package.json | 3 ++- packages/core-typings/package.json | 5 +++++ packages/ddp-client/package.json | 5 +++++ packages/desktop-api/package.json | 5 +++++ packages/emitter/package.json | 3 ++- packages/eslint-config/package.json | 5 +++++ packages/livechat/package.json | 2 +- packages/message-parser/package.json | 2 +- packages/peggy-loader/package.json | 2 +- packages/rest-typings/package.json | 5 +++++ packages/ui-kit/package.json | 3 ++- 12 files changed, 39 insertions(+), 6 deletions(-) diff --git a/packages/api-client/package.json b/packages/api-client/package.json index d6df7399184c7..3bd7a80e66fe0 100644 --- a/packages/api-client/package.json +++ b/packages/api-client/package.json @@ -1,6 +1,11 @@ { "name": "@rocket.chat/api-client", "version": "0.2.56", + "repository": { + "type": "git", + "url": "git+https://github.com/RocketChat/Rocket.Chat.git", + "directory": "packages/api-client" + }, "main": "./dist/index.js", "typings": "./dist/index.d.ts", "files": [ diff --git a/packages/apps-engine/package.json b/packages/apps-engine/package.json index 731321e8c799c..df0cdbe48f55a 100644 --- a/packages/apps-engine/package.json +++ b/packages/apps-engine/package.json @@ -13,7 +13,8 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/RocketChat/Rocket.Chat.git" + "url": "git+https://github.com/RocketChat/Rocket.Chat.git", + "directory": "packages/apps-engine" }, "license": "MIT", "author": { diff --git a/packages/core-typings/package.json b/packages/core-typings/package.json index d50ba6a4bdce5..e0a6dde63d5eb 100644 --- a/packages/core-typings/package.json +++ b/packages/core-typings/package.json @@ -2,6 +2,11 @@ "$schema": "https://json.schemastore.org/package", "name": "@rocket.chat/core-typings", "version": "8.9.0-develop", + "repository": { + "type": "git", + "url": "git+https://github.com/RocketChat/Rocket.Chat.git", + "directory": "packages/core-typings" + }, "main": "./dist/index.js", "typings": "./dist/index.d.ts", "files": [ diff --git a/packages/ddp-client/package.json b/packages/ddp-client/package.json index 598f7f3946ea1..f28475382cd0b 100644 --- a/packages/ddp-client/package.json +++ b/packages/ddp-client/package.json @@ -1,6 +1,11 @@ { "name": "@rocket.chat/ddp-client", "version": "1.1.1", + "repository": { + "type": "git", + "url": "git+https://github.com/RocketChat/Rocket.Chat.git", + "directory": "packages/ddp-client" + }, "main": "./dist/index.js", "typings": "./dist/index.d.ts", "files": [ diff --git a/packages/desktop-api/package.json b/packages/desktop-api/package.json index 9c31bc638582a..fbbf457a4986d 100644 --- a/packages/desktop-api/package.json +++ b/packages/desktop-api/package.json @@ -2,6 +2,11 @@ "$schema": "https://www.schemastore.org/package", "name": "@rocket.chat/desktop-api", "version": "1.3.0", + "repository": { + "type": "git", + "url": "git+https://github.com/RocketChat/Rocket.Chat.git", + "directory": "packages/desktop-api" + }, "type": "module", "exports": { ".": { diff --git a/packages/emitter/package.json b/packages/emitter/package.json index 3a9cb36d7d3ce..47ce24fed45de 100644 --- a/packages/emitter/package.json +++ b/packages/emitter/package.json @@ -10,7 +10,8 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/RocketChat/Rocket.Chat.git" + "url": "git+https://github.com/RocketChat/Rocket.Chat.git", + "directory": "packages/emitter" }, "license": "MIT", "author": { diff --git a/packages/eslint-config/package.json b/packages/eslint-config/package.json index 37be7d9a7155f..0cd266db1ac5e 100644 --- a/packages/eslint-config/package.json +++ b/packages/eslint-config/package.json @@ -2,6 +2,11 @@ "name": "@rocket.chat/eslint-config", "version": "0.8.0", "description": "Rocket.Chat's JS/TS ESLint config", + "repository": { + "type": "git", + "url": "git+https://github.com/RocketChat/Rocket.Chat.git", + "directory": "packages/eslint-config" + }, "type": "module", "main": "./index.js", "exports": { diff --git a/packages/livechat/package.json b/packages/livechat/package.json index 475572affcd22..aeb0ebe5e2c87 100644 --- a/packages/livechat/package.json +++ b/packages/livechat/package.json @@ -4,7 +4,7 @@ "homepage": "https://rocket.chat", "repository": { "type": "git", - "url": "https://github.com/RocketChat/Rocket.Chat", + "url": "git+https://github.com/RocketChat/Rocket.Chat.git", "directory": "packages/livechat" }, "license": "MIT", diff --git a/packages/message-parser/package.json b/packages/message-parser/package.json index e825de9648617..e810b2f4581be 100644 --- a/packages/message-parser/package.json +++ b/packages/message-parser/package.json @@ -8,7 +8,7 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/RocketChat/fuselage.git", + "url": "git+https://github.com/RocketChat/Rocket.Chat.git", "directory": "packages/message-parser" }, "license": "MIT", diff --git a/packages/peggy-loader/package.json b/packages/peggy-loader/package.json index b537643d23029..ef28d4f916a10 100644 --- a/packages/peggy-loader/package.json +++ b/packages/peggy-loader/package.json @@ -13,7 +13,7 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/RocketChat/fuselage.git", + "url": "git+https://github.com/RocketChat/Rocket.Chat.git", "directory": "packages/peggy-loader" }, "license": "MIT", diff --git a/packages/rest-typings/package.json b/packages/rest-typings/package.json index bbeb9384a31a9..c1b777bd7c2e2 100644 --- a/packages/rest-typings/package.json +++ b/packages/rest-typings/package.json @@ -1,6 +1,11 @@ { "name": "@rocket.chat/rest-typings", "version": "8.9.0-develop", + "repository": { + "type": "git", + "url": "git+https://github.com/RocketChat/Rocket.Chat.git", + "directory": "packages/rest-typings" + }, "main": "./dist/index.js", "typings": "./dist/index.d.ts", "files": [ diff --git a/packages/ui-kit/package.json b/packages/ui-kit/package.json index 90cc64fb96426..bd572996c0412 100644 --- a/packages/ui-kit/package.json +++ b/packages/ui-kit/package.json @@ -8,7 +8,8 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/RocketChat/Rocket.Chat.git" + "url": "git+https://github.com/RocketChat/Rocket.Chat.git", + "directory": "packages/ui-kit" }, "license": "MIT", "author": { From 207236c18e96bdb8a3ee4c069b29c9192b5a34ab Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Fri, 21 Aug 2026 17:41:56 -0300 Subject: [PATCH 02/10] fix(release-action): don't write a bogus npm auth token `createNpmFile()` interpolated `process.env.NPM_TOKEN` unconditionally, so without the secret it wrote `//registry.npmjs.org/:_authToken=undefined`. npm honours that auth line instead of exchanging the OIDC token, failing with a misleading E401/E404. Co-Authored-By: Claude Opus 5 (1M context) --- packages/release-action/src/createNpmFile.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/release-action/src/createNpmFile.ts b/packages/release-action/src/createNpmFile.ts index 67c0112e526c4..ad46a231b61b3 100644 --- a/packages/release-action/src/createNpmFile.ts +++ b/packages/release-action/src/createNpmFile.ts @@ -4,6 +4,13 @@ import fsPromise from 'node:fs/promises'; import * as core from '@actions/core'; export async function createNpmFile() { + // With trusted publishing (OIDC) there is no token, and a bogus auth line in .npmrc + // takes precedence over the OIDC exchange, so leave the file alone. + if (!process.env.NPM_TOKEN) { + core.info('No NPM_TOKEN provided, relying on the registry authentication already in place'); + return; + } + const userNpmrcPath = `${process.env.HOME}/.npmrc`; if (fs.existsSync(userNpmrcPath)) { From 15002959611c17cc2ca96bc6e3e84cd5d27928e0 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Fri, 21 Aug 2026 17:43:51 -0300 Subject: [PATCH 03/10] ci: consolidate release workflows into a single entry point MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit npm allows one trusted publisher per package and validates the entry-point workflow filename (`workflow_ref`), which rules out `workflow_call` reusable workflows. Publishing happened from three files — publish-release.yml, new-release.yml and release-candidate.yml — so no single filename could be registered. Merge them into release.yml, keeping all three triggers and deriving the release-action `action` from the event. Also grant `id-token: write` and install npm >= 11.5.1, both required for the OIDC exchange (Node 22 bundles npm 10). Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/new-release.yml | 57 ------------------- .github/workflows/publish-release.yml | 46 --------------- .github/workflows/release-candidate.yml | 39 ------------- .github/workflows/release.yml | 75 +++++++++++++++++++++++++ 4 files changed, 75 insertions(+), 142 deletions(-) delete mode 100644 .github/workflows/new-release.yml delete mode 100644 .github/workflows/publish-release.yml delete mode 100644 .github/workflows/release-candidate.yml create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/new-release.yml b/.github/workflows/new-release.yml deleted file mode 100644 index 8d822eb7a73bc..0000000000000 --- a/.github/workflows/new-release.yml +++ /dev/null @@ -1,57 +0,0 @@ -name: Start new release - -on: - workflow_dispatch: - inputs: - name: - type: choice - description: Release type - default: next - required: true - options: - - next - - patch - - cut - base-ref: - description: Base version - default: develop - required: false - -env: - HUSKY: 0 - -permissions: {} - -jobs: - new-release: - runs-on: ubuntu-24.04 - permissions: - contents: read - - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - ref: ${{ github.event.inputs.base-ref }} - fetch-depth: 0 - token: ${{ secrets.CI_PAT }} - - - name: Setup NodeJS - uses: ./.github/actions/setup-node - with: - cache-modules: true - install: true - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - - - uses: rharkor/caching-for-turbo@2238fae6eb9a9936f92356f54cb3660200d105e7 # v2.5.1 - - - name: Build packages - run: yarn build - - - name: 'Start release: ${{ github.event.inputs.name }}' - uses: ./packages/release-action - with: - action: ${{ github.event.inputs.name }} - base-ref: ${{ github.event.inputs.base-ref }} - env: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - GITHUB_TOKEN: ${{ secrets.CI_PAT }} diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml deleted file mode 100644 index 902bd777f4f46..0000000000000 --- a/.github/workflows/publish-release.yml +++ /dev/null @@ -1,46 +0,0 @@ -name: Publish Final Release - -on: - push: - branches: - - master - -concurrency: ${{ github.workflow }}-${{ github.ref }} - -env: - HUSKY: 0 - -permissions: {} - -jobs: - release: - name: Release - runs-on: ubuntu-24.04 - permissions: - contents: read - steps: - - name: Checkout Repo - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - fetch-depth: 0 - token: ${{ secrets.CI_PAT }} - - - name: Setup NodeJS - uses: ./.github/actions/setup-node - with: - cache-modules: true - install: true - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - - - uses: rharkor/caching-for-turbo@2238fae6eb9a9936f92356f54cb3660200d105e7 # v2.5.1 - - - name: Build packages - run: yarn build - - - name: Publish final release - uses: ./packages/release-action - with: - action: publish-final - env: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - GITHUB_TOKEN: ${{ secrets.CI_PAT }} diff --git a/.github/workflows/release-candidate.yml b/.github/workflows/release-candidate.yml deleted file mode 100644 index 7b3e9103dbe98..0000000000000 --- a/.github/workflows/release-candidate.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: Release candidate cut -on: - schedule: - - cron: '28 21 20 * *' # run at minute 28 to avoid the chance of delay due to high load on GH - -permissions: {} - -jobs: - new-release: - runs-on: ubuntu-24.04 - permissions: - contents: read - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - ref: ${{ github.ref_name }} - fetch-depth: 0 - token: ${{ secrets.CI_PAT }} - - - name: Setup NodeJS - uses: ./.github/actions/setup-node - with: - cache-modules: true - install: true - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - - - uses: rharkor/caching-for-turbo@2238fae6eb9a9936f92356f54cb3660200d105e7 # v2.5.1 - - - name: Build packages - run: yarn build - - - name: 'Start release candidate' - uses: ./packages/release-action - with: - action: next - base-ref: ${{ github.ref_name }} - env: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - GITHUB_TOKEN: ${{ secrets.CI_PAT }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000000000..497430e047619 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,75 @@ +name: Release + +# Single entry point for every path that publishes to npm. npm allows one trusted +# publisher per package and validates the entry-point workflow filename, so `cut`, +# `next` and `publish-final` all have to run from this file. +on: + push: + branches: + - master + schedule: + - cron: '28 21 20 * *' # run at minute 28 to avoid the chance of delay due to high load on GH + workflow_dispatch: + inputs: + name: + type: choice + description: Release type + default: next + required: true + options: + - next + - patch + - cut + base-ref: + description: Base version + default: develop + required: false + +concurrency: ${{ github.workflow }}-${{ github.ref }} + +env: + HUSKY: 0 + +permissions: {} + +jobs: + release: + name: Release + runs-on: ubuntu-24.04 + permissions: + contents: read + id-token: write # npm trusted publishing (OIDC) + steps: + - name: Checkout Repo + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ github.event_name == 'schedule' && github.ref_name || github.event_name == 'workflow_dispatch' && inputs.base-ref || '' }} + fetch-depth: 0 + token: ${{ secrets.CI_PAT }} + + - name: Setup NodeJS + uses: ./.github/actions/setup-node + with: + cache-modules: true + install: true + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + + # Node 22 bundles npm 10, trusted publishing needs >= 11.5.1 + - name: Setup npm + run: | + npm install -g npm@^11.15.0 + npm --version + + - uses: rharkor/caching-for-turbo@2238fae6eb9a9936f92356f54cb3660200d105e7 # v2.5.1 + + - name: Build packages + run: yarn build + + - name: Release + uses: ./packages/release-action + with: + action: ${{ github.event_name == 'push' && 'publish-final' || github.event_name == 'schedule' && 'next' || inputs.name }} + base-ref: ${{ github.event_name == 'schedule' && github.ref_name || github.event_name == 'workflow_dispatch' && inputs.base-ref || '' }} + env: + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + GITHUB_TOKEN: ${{ secrets.CI_PAT }} From 38b1d7f8bc91d29a2b48a96aeb4b53b39b2d4614 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Fri, 21 Aug 2026 17:44:34 -0300 Subject: [PATCH 04/10] docs: document npm publishing and trusted publishing setup Covers what release.yml does, why publishing must live in a single workflow file, how to register a trusted publisher for a package, the repository-field requirement for provenance, and the common OIDC failure modes. Co-Authored-By: Claude Opus 5 (1M context) --- CLAUDE.md | 1 + docs/npm-publishing.md | 112 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 docs/npm-publishing.md diff --git a/CLAUDE.md b/CLAUDE.md index 1338ebb9f4a79..4f6a8d8d72282 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -33,6 +33,7 @@ Read the doc that matches the task instead of scanning `docs/` wholesale. - [docs/meteor-modern-stack.md](docs/meteor-modern-stack.md) — Meteor modern build stack, file-watching caveats - [docs/coverage.md](docs/coverage.md) — coverage instrumentation in build and CI +- [docs/npm-publishing.md](docs/npm-publishing.md) — how the public packages reach npm: `release.yml`, changesets, OIDC trusted publishing, provenance ### Other diff --git a/docs/npm-publishing.md b/docs/npm-publishing.md new file mode 100644 index 0000000000000..b20faa9ed48e6 --- /dev/null +++ b/docs/npm-publishing.md @@ -0,0 +1,112 @@ +# npm publishing + +The 15 public `@rocket.chat/*` packages under `packages/` are published to npm by +`.github/workflows/release.yml`, which delegates to the local `packages/release-action`. +Everything else in the workspace is `private: true` and never published. + +## How a publish happens + +`release.yml` is the **only** workflow that publishes. It carries three triggers and maps each one +to a `release-action` mode: + +| Trigger | `action` | Publishes? | dist-tag | +| ----------------------------- | --------------- | ---------- | --------------- | +| `push` to `master` | `publish-final` | yes | `latest` | +| `schedule` (monthly, 20th) | `next` | yes | `rc` | +| `workflow_dispatch` → `cut` | `cut` | yes | `latest` / `rc` | +| `workflow_dispatch` → `next` | `next` | yes | `rc` | +| `workflow_dispatch` → `patch` | `patch` | no | — | + +`publishRelease.ts` and `bumpNextVersion.ts` both end with `yarn changeset publish --no-git-tag`. +Changesets then spawns `npm publish --access public --tag ` from the repo root, so +**the npm CLI on `PATH` is what authenticates** — not yarn. + +## Trusted publishing (OIDC) + +Publishing authenticates via [npm trusted publishing](https://docs.npmjs.com/trusted-publishers/) +rather than a long-lived token. GitHub Actions mints a short-lived, job-scoped credential at publish +time, and npm attaches a provenance attestation to every published tarball. + +Two hard requirements, both handled in `release.yml`: + +- `permissions: id-token: write` on the publishing job — without it the runner never exposes + `ACTIONS_ID_TOKEN_REQUEST_URL` and the OIDC exchange cannot happen. +- **npm >= 11.5.1** (the `Setup npm` step installs it). `engines.node` is 22.x, which bundles npm 10. + +### Why there is only one release workflow + +npm allows **one trusted publisher per package**, and it validates the _entry-point_ workflow +filename — the `workflow_ref` OIDC claim. `workflow_call` reusable workflows inherit the caller's +`workflow_ref`, so splitting the publish step into a shared workflow does not help: every caller +would need its own registration. Hence the single `release.yml` with three triggers. + +**Renaming or moving `release.yml` breaks publishing** until every package's trusted publisher is +updated on npmjs.com. + +### Registering a newly published package + +npm cannot configure a trusted publisher for a package that does not exist yet, so a brand-new +package needs one manual first publish before it can be registered. + +Register from a machine with npm >= 11.15.0 and account-level 2FA (granular tokens with the +bypass-2FA option are rejected): + +```sh +npm trust github @rocket.chat/ \ + --file release.yml \ + --repo RocketChat/Rocket.Chat \ + --allow-publish + +npm trust list @rocket.chat/ +``` + +Or on `npmjs.com/package/@rocket.chat//access`: + +| Field | Value | +| ----------------- | -------------------------------------- | +| Provider | GitHub Actions | +| Organization | `RocketChat` | +| Repository | `Rocket.Chat` | +| Workflow filename | `release.yml` (filename only, no path) | +| Environment | _(blank)_ | +| Allowed actions | `npm publish` | + +No GitHub Environment is configured: the monthly RC cron and the push-to-master final release both +run unattended, and an environment with required reviewers would stall them. + +### Provenance requires a correct `repository` field + +npm generates provenance automatically for public packages published from a public repo via OIDC, +and **rejects the publish (422) when `repository.url` does not match the repository the workflow ran +in**. Every published package therefore needs: + +```json +"repository": { + "type": "git", + "url": "git+https://github.com/RocketChat/Rocket.Chat.git", + "directory": "packages/" +} +``` + +The org/repo casing (`RocketChat/Rocket.Chat`) has to match too. + +Verify after a release: + +```sh +npm view @rocket.chat/ --json dist.attestations +``` + +A non-null result is proof the publish went through OIDC — token-based publishes from this repo +never carried provenance. + +## Troubleshooting + +- **`E404` / `ENEEDAUTH` on `/-/npm/v1/oidc/token/exchange/package/…`** — almost always a + trusted-publisher mismatch: wrong repository, or the workflow filename entered with a path + (`.github/workflows/release.yml`) instead of bare `release.yml`. +- **`E401` despite a correct registration** — something wrote an auth line into `~/.npmrc`, which + takes precedence over the OIDC exchange. `createNpmFile()` skips writing when `NPM_TOKEN` is + unset; `.github/actions/setup-node` writes one whenever its `NPM_TOKEN` input is set. +- **422 on publish** — provenance mismatch; check the package's `repository` field. +- **Silent fallback to token auth** — if a valid token is present npm may authenticate with it + instead. Confirm via `dist.attestations` rather than the job log. From f55e9e712cdbc286c4244adea8f0c96a8ce2cafe Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Fri, 21 Aug 2026 17:58:46 -0300 Subject: [PATCH 05/10] chore: drop redundant publishConfig.access from packages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `.changeset/config.json` already sets `access: "public"` globally, which changesets turns into `--access public` on every `npm publish`. The nine per-package copies were inconsistent — the other six published packages never had one — and the copy in `fuselage-ui-kit` was dead, since that package is private. Co-Authored-By: Claude Opus 5 (1M context) --- packages/apps-engine/package.json | 3 --- packages/emitter/package.json | 3 --- packages/fuselage-ui-kit/package.json | 3 --- packages/media-signaling/package.json | 3 --- packages/memo/package.json | 3 --- packages/message-parser/package.json | 3 --- packages/mp3-encoder/package.json | 3 --- packages/peggy-loader/package.json | 3 --- packages/ui-kit/package.json | 3 --- 9 files changed, 27 deletions(-) diff --git a/packages/apps-engine/package.json b/packages/apps-engine/package.json index df0cdbe48f55a..7cd344f724bb7 100644 --- a/packages/apps-engine/package.json +++ b/packages/apps-engine/package.json @@ -77,9 +77,6 @@ "volta": { "extends": "../../package.json" }, - "publishConfig": { - "access": "public" - }, "installConfig": { "hoistingLimits": "workspaces" } diff --git a/packages/emitter/package.json b/packages/emitter/package.json index 47ce24fed45de..e81cba88a91ff 100644 --- a/packages/emitter/package.json +++ b/packages/emitter/package.json @@ -45,9 +45,6 @@ "ts-jest": "~29.4.11", "typescript": "~5.9.3" }, - "publishConfig": { - "access": "public" - }, "volta": { "extends": "../../package.json" } diff --git a/packages/fuselage-ui-kit/package.json b/packages/fuselage-ui-kit/package.json index 820a7cffb80c7..bad67fe1eae02 100644 --- a/packages/fuselage-ui-kit/package.json +++ b/packages/fuselage-ui-kit/package.json @@ -101,8 +101,5 @@ }, "volta": { "extends": "../../package.json" - }, - "publishConfig": { - "access": "public" } } diff --git a/packages/media-signaling/package.json b/packages/media-signaling/package.json index b2daac0c4212e..9eb638fd45123 100644 --- a/packages/media-signaling/package.json +++ b/packages/media-signaling/package.json @@ -12,9 +12,6 @@ "bugs": { "url": "https://github.com/RocketChat/Rocket.Chat/issues" }, - "publishConfig": { - "access": "public" - }, "main": "./dist/index.js", "typings": "./dist/index.d.ts", "files": [ diff --git a/packages/memo/package.json b/packages/memo/package.json index 5fef6aa09b862..31a5d664c9628 100644 --- a/packages/memo/package.json +++ b/packages/memo/package.json @@ -43,8 +43,5 @@ }, "volta": { "extends": "../../package.json" - }, - "publishConfig": { - "access": "public" } } diff --git a/packages/message-parser/package.json b/packages/message-parser/package.json index e810b2f4581be..5c214af061f86 100644 --- a/packages/message-parser/package.json +++ b/packages/message-parser/package.json @@ -71,8 +71,5 @@ }, "volta": { "extends": "../../package.json" - }, - "publishConfig": { - "access": "public" } } diff --git a/packages/mp3-encoder/package.json b/packages/mp3-encoder/package.json index cf91bcba38fd9..b82f77741f498 100644 --- a/packages/mp3-encoder/package.json +++ b/packages/mp3-encoder/package.json @@ -50,8 +50,5 @@ "ts-jest": "~29.4.12", "typedoc": "~0.28.20", "typescript": "~5.9.3" - }, - "publishConfig": { - "access": "public" } } diff --git a/packages/peggy-loader/package.json b/packages/peggy-loader/package.json index ef28d4f916a10..e257a5151c14a 100644 --- a/packages/peggy-loader/package.json +++ b/packages/peggy-loader/package.json @@ -49,8 +49,5 @@ }, "volta": { "extends": "../../package.json" - }, - "publishConfig": { - "access": "public" } } diff --git a/packages/ui-kit/package.json b/packages/ui-kit/package.json index bd572996c0412..8eb19e0e4d335 100644 --- a/packages/ui-kit/package.json +++ b/packages/ui-kit/package.json @@ -53,8 +53,5 @@ }, "volta": { "extends": "../../package.json" - }, - "publishConfig": { - "access": "public" } } From 281f112acecb3ca0a682e4dd7559e6610537f759 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Fri, 21 Aug 2026 18:07:27 -0300 Subject: [PATCH 06/10] chore(release-action): move the action runtime to node24 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `using: "node20"` is a deprecated GitHub Actions runtime. Bump the esbuild target alongside it so the bundle matches the runtime it executes on. This does not affect the OIDC exchange either way — the publish runs in a subprocess (`yarn changeset publish`) under the Node from setup-node, and inherits `ACTIONS_ID_TOKEN_REQUEST_*` from the job. Co-Authored-By: Claude Opus 5 (1M context) --- packages/release-action/action.yml | 2 +- packages/release-action/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/release-action/action.yml b/packages/release-action/action.yml index 23d7382aab6d8..34f0c93bf1897 100644 --- a/packages/release-action/action.yml +++ b/packages/release-action/action.yml @@ -10,7 +10,7 @@ inputs: required: false runs: - using: "node20" + using: "node24" main: "dist/index.js" branding: diff --git a/packages/release-action/package.json b/packages/release-action/package.json index 3045639607d77..666e6d5a226d0 100644 --- a/packages/release-action/package.json +++ b/packages/release-action/package.json @@ -4,7 +4,7 @@ "private": true, "main": "dist/index.js", "scripts": { - "build": "tsc --noEmit && esbuild src/index.ts --bundle --platform=node --target=node20 --format=cjs --outfile=dist/index.js", + "build": "tsc --noEmit && esbuild src/index.ts --bundle --platform=node --target=node24 --format=cjs --outfile=dist/index.js", "lint": "eslint .", "lint:fix": "eslint --fix src" }, From e61daeca72cd6848784562aec9ed7f84727a9d61 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Fri, 21 Aug 2026 18:08:55 -0300 Subject: [PATCH 07/10] chore(release-action): drop the drifted packageManager pin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It declared `yarn@4.12.0` while the root declares `4.18.0`. No other workspace package pins a package manager, and this one already inherits the toolchain via `volta.extends`, so remove the field rather than bump it — otherwise it just drifts again. Co-Authored-By: Claude Opus 5 (1M context) --- packages/release-action/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/release-action/package.json b/packages/release-action/package.json index 666e6d5a226d0..cf18f5663e598 100644 --- a/packages/release-action/package.json +++ b/packages/release-action/package.json @@ -26,7 +26,6 @@ "eslint": "~9.39.5", "typescript": "~5.9.3" }, - "packageManager": "yarn@4.12.0", "volta": { "extends": "../../package.json" } From 8f0c758070791df819ace5373c4e058d9c3f69ec Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Wed, 26 Aug 2026 12:54:10 -0300 Subject: [PATCH 08/10] remove count --- docs/npm-publishing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/npm-publishing.md b/docs/npm-publishing.md index b20faa9ed48e6..2e9b7111cdebc 100644 --- a/docs/npm-publishing.md +++ b/docs/npm-publishing.md @@ -1,6 +1,6 @@ # npm publishing -The 15 public `@rocket.chat/*` packages under `packages/` are published to npm by +The public `@rocket.chat/*` packages under `packages/` are published to npm by `.github/workflows/release.yml`, which delegates to the local `packages/release-action`. Everything else in the workspace is `private: true` and never published. From e2df8fd289536744df056fc22869dd6f911adb76 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Wed, 26 Aug 2026 16:06:26 -0300 Subject: [PATCH 09/10] fix all urls --- packages/fuselage-ui-kit/package.json | 6 +++--- packages/message-parser/package.json | 4 ++-- packages/peggy-loader/package.json | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/fuselage-ui-kit/package.json b/packages/fuselage-ui-kit/package.json index bad67fe1eae02..62fcf735ea9f3 100644 --- a/packages/fuselage-ui-kit/package.json +++ b/packages/fuselage-ui-kit/package.json @@ -3,13 +3,13 @@ "version": "33.0.0", "private": true, "description": "UiKit elements for Rocket.Chat Apps built under Fuselage design system", - "homepage": "https://rocketchat.github.io/Rocket.Chat.Fuselage/", + "homepage": "https://github.com/RocketChat/Rocket.Chat#readme", "bugs": { - "url": "https://github.com/RocketChat/fuselage/issues" + "url": "https://github.com/RocketChat/Rocket.Chat/issues" }, "repository": { "type": "git", - "url": "git+https://github.com/RocketChat/fuselage.git", + "url": "git+https://github.com/RocketChat/Rocket.Chat.git", "directory": "packages/fuselage-ui-kit" }, "license": "MIT", diff --git a/packages/message-parser/package.json b/packages/message-parser/package.json index 5c214af061f86..5b56fbc56934d 100644 --- a/packages/message-parser/package.json +++ b/packages/message-parser/package.json @@ -2,9 +2,9 @@ "name": "@rocket.chat/message-parser", "version": "0.32.0", "description": "Rocket.Chat parser for messages", - "homepage": "https://github.com/RocketChat/fuselage#readme", + "homepage": "https://github.com/RocketChat/Rocket.Chat#readme", "bugs": { - "url": "https://github.com/RocketChat/fuselage/issues" + "url": "https://github.com/RocketChat/Rocket.Chat/issues" }, "repository": { "type": "git", diff --git a/packages/peggy-loader/package.json b/packages/peggy-loader/package.json index e257a5151c14a..5296ce06d9df4 100644 --- a/packages/peggy-loader/package.json +++ b/packages/peggy-loader/package.json @@ -7,9 +7,9 @@ "loader", "webpack" ], - "homepage": "https://github.com/RocketChat/fuselage#readme", + "homepage": "https://github.com/RocketChat/Rocket.Chat#readme", "bugs": { - "url": "https://github.com/RocketChat/fuselage/issues" + "url": "https://github.com/RocketChat/Rocket.Chat/issues" }, "repository": { "type": "git", From d4ac55327a0d92d54ad0718bad8680045a39ea53 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Wed, 26 Aug 2026 16:06:39 -0300 Subject: [PATCH 10/10] document why NPM_TOKEN is still used --- .github/workflows/release.yml | 6 +++++- docs/npm-publishing.md | 38 ++++++++++++++++++++++++++++++++--- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 497430e047619..02ebc89660d14 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -52,6 +52,10 @@ jobs: with: cache-modules: true install: true + # Transitional: npm attempts the OIDC exchange first and overrides this token on + # success, so it only takes effect when the exchange fails. That keeps releases + # alive while packages are registered as trusted publishers one by one. Remove + # once every package reports a non-null dist.attestations — see docs/npm-publishing.md. NPM_TOKEN: ${{ secrets.NPM_TOKEN }} # Node 22 bundles npm 10, trusted publishing needs >= 11.5.1 @@ -71,5 +75,5 @@ jobs: action: ${{ github.event_name == 'push' && 'publish-final' || github.event_name == 'schedule' && 'next' || inputs.name }} base-ref: ${{ github.event_name == 'schedule' && github.ref_name || github.event_name == 'workflow_dispatch' && inputs.base-ref || '' }} env: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} # transitional, see the Setup NodeJS step GITHUB_TOKEN: ${{ secrets.CI_PAT }} diff --git a/docs/npm-publishing.md b/docs/npm-publishing.md index 2e9b7111cdebc..3fafde8d5ff2d 100644 --- a/docs/npm-publishing.md +++ b/docs/npm-publishing.md @@ -33,6 +33,38 @@ Two hard requirements, both handled in `release.yml`: `ACTIONS_ID_TOKEN_REQUEST_URL` and the OIDC exchange cannot happen. - **npm >= 11.5.1** (the `Setup npm` step installs it). `engines.node` is 22.x, which bundles npm 10. +### How `NPM_TOKEN` interacts with the exchange + +`npm publish` runs the OIDC exchange *before* it reads credentials, and on success it overrides the +`~/.npmrc` auth line with the freshly minted token. A present `NPM_TOKEN` therefore does **not** +prevent trusted publishing — the exchange still wins whenever it succeeds. + +The token matters only when the exchange fails. npm's `oidc()` is written to never throw: every +failure path (missing `id-token` permission, package not registered, exchange rejected) returns +quietly, and `publish` then proceeds with the npmrc token — no provenance, no error, exit code 0. + +That makes the token a **migration aid and a hazard at the same time**: + +- While packages are being registered one at a time, it keeps releases from hard-failing on the + ones that are not registered yet. +- Once everything is registered, it hides regressions — a broken registration keeps publishing + under token auth instead of failing. + +So `release.yml` keeps `NPM_TOKEN` only until every package publishes with provenance, then drops +both references (the `setup-node` input and the `Release` step's `env`). + +### Migrating packages incrementally + +npm allows one trusted publisher per package, so packages move over one at a time: + +1. Register the package (see below). Watch for packages previously released from another repo — + `@rocket.chat/emitter`'s last provenance came from `RocketChat/fuselage`'s `cd.yml`, and that + registration has to be re-pointed here before this workflow can publish it. +2. Let a release run. The job log is not evidence — a token fallback looks identical to success. +3. Confirm with `npm view @rocket.chat/ --json dist.attestations`; non-null means the package + is publishing through OIDC. +4. When all published packages are attested, remove `NPM_TOKEN` from `release.yml`. + ### Why there is only one release workflow npm allows **one trusted publisher per package**, and it validates the _entry-point_ workflow @@ -104,9 +136,9 @@ never carried provenance. - **`E404` / `ENEEDAUTH` on `/-/npm/v1/oidc/token/exchange/package/…`** — almost always a trusted-publisher mismatch: wrong repository, or the workflow filename entered with a path (`.github/workflows/release.yml`) instead of bare `release.yml`. -- **`E401` despite a correct registration** — something wrote an auth line into `~/.npmrc`, which - takes precedence over the OIDC exchange. `createNpmFile()` skips writing when `NPM_TOKEN` is - unset; `.github/actions/setup-node` writes one whenever its `NPM_TOKEN` input is set. +- **`E401` / `ENEEDAUTH` with no token configured** — the exchange failed and there was nothing to + fall back to. Run with `--loglevel verbose`: `oidc()` logs its reason (`Skipped because incorrect + permissions`, `Failed token exchange request…`) instead of throwing. - **422 on publish** — provenance mismatch; check the package's `repository` field. - **Silent fallback to token auth** — if a valid token is present npm may authenticate with it instead. Confirm via `dist.attestations` rather than the job log.