From 064b44417edd29f3b01fc9a965da0c633f482b2e Mon Sep 17 00:00:00 2001 From: finalerock44 <77282157+finalerock44@users.noreply.github.com> Date: Thu, 17 Sep 2026 15:50:10 +0100 Subject: [PATCH 1/2] ci: publish to npm via trusted publishing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit npm already has this repo + npm-publish.yml registered as the trusted publisher, but nothing in CI asked for an OIDC claim, so publishes still used NPM_TOKEN — which expired 90 days after it was last set and failed 5.6.0-beta.1 with a 404 on PUT. Three things were missing, not one: - `id-token: write` on the publish job, and again on the two jobs that call it: a reusable workflow cannot hold a permission its caller lacks, and release-please.yml grants only contents/pull-requests. - npm >= 11.5.1 to do the exchange. Node 22 ships npm 10.9, so the publish job moves to Node 24 (npm 11.19). - `npm publish` rather than `pnpm publish`: pnpm only gained the exchange in v11 and this repo pins 10.17. pnpm still installs and builds. NPM_TOKEN is now unused and can be deleted once a release has gone out this way. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/npm-publish.yml | 30 +++++++++++++++++++++------- .github/workflows/release-please.yml | 14 +++++++++++++ 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index feed479..8e1eef7 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -23,6 +23,13 @@ jobs: runs-on: ubuntu-latest permissions: contents: read + # Trusted publishing: npm is configured with this repo + this workflow as + # the publisher for @devicecloud.dev/dcd, and mints a short-lived token + # from the OIDC claim instead of a long-lived NPM_TOKEN. Without this + # permission the runner cannot request the claim at all, and npm falls + # back to the token -- which is what expired on 2026-09-17 and failed the + # publish with a 404 on PUT. + id-token: write steps: - uses: actions/checkout@v7 # Setup .npmrc file to publish to npm @@ -31,9 +38,13 @@ jobs: with: run_install: false + # Node 24 for its bundled npm 11: trusted publishing needs npm >= 11.5.1, + # and Node 22 ships npm 10.9. This is the publish job only -- what the CLI + # itself supports at runtime is set by tsconfig, not by the Node that + # builds it. - uses: actions/setup-node@v7 with: - node-version: '22.x' + node-version: '24.x' registry-url: 'https://registry.npmjs.org' cache: 'pnpm' cache-dependency-path: './pnpm-lock.yaml' @@ -74,14 +85,19 @@ jobs: fi echo "Version $VERSION is valid for beta release" + # `npm publish`, not `pnpm publish`: pnpm only learned the OIDC exchange + # in v11, and this repo pins pnpm 10.17 in packageManager. pnpm still does + # the install and the build above; only the upload differs. Safe here + # because this is a single package with no workspace: deps -- npm packs + # the same `files` list. + # + # No NODE_AUTH_TOKEN on either step: its presence would take precedence + # over the OIDC token and put us straight back on the expiring-secret + # path. - name: Publish Production Version if: ${{ inputs.release_type == 'prod' }} - run: pnpm publish --no-git-checks - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + run: npm publish - name: Publish Beta Version if: ${{ inputs.release_type == 'beta' }} - run: pnpm publish --tag beta --no-git-checks - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + run: npm publish --tag beta diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index 3913334..45f4d84 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -76,6 +76,13 @@ jobs: manifest-file: .release-please-manifest-beta.json publish-npm-prod: + # Must be granted here too: a reusable workflow can never hold a + # permission its caller does not, and this workflow's top-level + # block has none. Without it npm-publish's own id-token: write is + # silently dropped and trusted publishing falls back to a token. + permissions: + contents: read + id-token: write needs: release-please-prod if: needs.release-please-prod.outputs.release_created == 'true' uses: ./.github/workflows/npm-publish.yml @@ -92,6 +99,13 @@ jobs: secrets: inherit publish-npm-beta: + # Must be granted here too: a reusable workflow can never hold a + # permission its caller does not, and this workflow's top-level + # block has none. Without it npm-publish's own id-token: write is + # silently dropped and trusted publishing falls back to a token. + permissions: + contents: read + id-token: write needs: release-please-beta if: needs.release-please-beta.outputs.release_created == 'true' uses: ./.github/workflows/npm-publish.yml From 3fb9d1701282ff4473a352ff53003e3b5e91e90c Mon Sep 17 00:00:00 2001 From: finalerock44 <77282157+finalerock44@users.noreply.github.com> Date: Thu, 17 Sep 2026 16:08:05 +0100 Subject: [PATCH 2/2] fix: point repository.url at the source repo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It was https://devicecloud.dev — the marketing site, not a git remote. Trusted publishing auto-enables provenance (npm's oidc.js sets it whenever the provenance config is at its default, the OIDC claim says the repo is public and the package is public — all true here), and the registry checks the generated provenance, which names GITHUB_SERVER_URL/GITHUB_REPOSITORY, against this field. A mismatch is a 422 at publish time; a dry run never sends provenance, so it would not have shown up until the real upload. homepage keeps pointing at devicecloud.dev. Co-Authored-By: Claude Opus 5 (1M context) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a290de6..d4b42bf 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ }, "repository": { "type": "git", - "url": "https://devicecloud.dev" + "url": "git+https://github.com/devicecloud-dev/dcd-cli.git" }, "scripts": { "dcd": "tsx src/index.ts",