From 9500c19b16c3ab10d1ba2bcdea9530b40ae688b4 Mon Sep 17 00:00:00 2001 From: Mao Nakamoto <41178744+maonakamoto@users.noreply.github.com> Date: Fri, 21 Aug 2026 01:19:24 +0200 Subject: [PATCH] =?UTF-8?q?chore(release):=200.1.2=20=E2=80=94=20publish?= =?UTF-8?q?=20via=20trusted=20publishing,=20no=20token?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The package's Trusted Publisher on npm is now pinned to this repo and publish.yml, so the bootstrap NPM_TOKEN auth goes away: the publish step authenticates via OIDC alone, and token-health.yml is deleted because there is no token left whose expiry could block a release. The version bump makes the reconciler prove this end-to-end: on merge it will find 0.1.2 missing from the registry and publish it with OIDC. Co-Authored-By: Claude Fable 5 --- .github/workflows/publish.yml | 9 ++-- .github/workflows/token-health.yml | 83 ------------------------------ package-lock.json | 4 +- package.json | 2 +- 4 files changed, 6 insertions(+), 92 deletions(-) delete mode 100644 .github/workflows/token-health.yml diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index ca33121..2eca57c 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -64,14 +64,11 @@ jobs: - if: steps.check.outputs.publish == 'true' run: npm run verify - # Bootstrap auth. Trusted publishing (OIDC) needs no token and is the - # destination; until it is configured on the package, NPM_TOKEN is what - # authenticates. The token expires — token-health.yml warns before it does, - # rather than letting a release be the thing that discovers it. + # Auth is trusted publishing (OIDC): npm accepts this job's identity token + # because the package's Trusted Publisher is pinned to exactly this repo + # and workflow. No npm token exists anywhere, so none can leak or expire. - if: steps.check.outputs.publish == 'true' run: npm publish - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - name: Tag the released commit if: steps.check.outputs.publish == 'true' diff --git a/.github/workflows/token-health.yml b/.github/workflows/token-health.yml deleted file mode 100644 index c5f4b66..0000000 --- a/.github/workflows/token-health.yml +++ /dev/null @@ -1,83 +0,0 @@ -name: Token health - -# NPM_TOKEN expires. Without this, the thing that discovers that fact is a -# release failing months from now, at which point someone has to work out why — -# an npm auth error does not say "your token expired", it says 403 or ENEEDAUTH, -# which reads like a permissions problem. -# -# So the token is checked on a schedule and the failure is turned into an issue -# with the fix written in it, instead of a surprise during a release. -on: - schedule: - # Weekly, Monday 06:00 UTC. - - cron: '0 6 * * 1' - workflow_dispatch: - -jobs: - check: - runs-on: ubuntu-latest - permissions: - issues: write - steps: - - uses: actions/setup-node@v4 - with: - node-version: '24' - registry-url: 'https://registry.npmjs.org' - - - name: Can the token still authenticate? - id: probe - continue-on-error: true - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - run: | - # `npm whoami` is the cheapest call that proves the credential is live. - # It does not publish, and it does not need a package to exist. - if who=$(npm whoami 2>&1); then - echo "→ token is valid (authenticated as $who)" - echo "ok=true" >> "$GITHUB_OUTPUT" - else - echo "→ token did NOT authenticate: $who" - echo "ok=false" >> "$GITHUB_OUTPUT" - fi - - - name: Open an issue if the token is dead - if: steps.probe.outputs.ok != 'true' - uses: actions/github-script@v7 - with: - script: | - const title = 'NPM_TOKEN cannot authenticate — releases are blocked'; - // One open issue, not one per week. - const existing = await github.rest.issues.listForRepo({ - owner: context.repo.owner, repo: context.repo.repo, - state: 'open', labels: 'release-blocked', - }); - if (existing.data.some(i => i.title === title)) { - core.info('issue already open'); - return; - } - await github.rest.issues.create({ - owner: context.repo.owner, repo: context.repo.repo, - title, - labels: ['release-blocked'], - body: [ - '`npm whoami` failed with the `NPM_TOKEN` secret, so **publishing is broken**.', - 'Nothing is wrong with the package — the credential is.', - '', - 'Most likely the token expired. npm tokens are created with an expiry,', - 'and an expired one fails with `403`/`ENEEDAUTH`, which reads like a', - 'permissions problem rather than an expiry.', - '', - '**Two ways to fix it, cheapest first:**', - '', - '1. **Configure trusted publishing and delete the token entirely.**', - ' npmjs.com → this package → Settings → Trusted Publisher →', - ' GitHub Actions → this org/repo → `publish.yml` → allow `npm publish`.', - ' Then remove the `NODE_AUTH_TOKEN` line from `publish.yml`.', - ' Tokens stop existing, so they stop expiring.', - ' https://docs.npmjs.com/trusted-publishers', - '', - '2. Create a new granular token and re-set the secret:', - ' `gh secret set NPM_TOKEN --repo ' + context.repo.owner + '/' + context.repo.repo + '`', - ' (the secret NAME is `NPM_TOKEN`; the token itself is pasted at the prompt)', - ].join('\n'), - }); diff --git a/package-lock.json b/package-lock.json index 29ec28a..9df2ca9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ai-forms", - "version": "0.1.1", + "version": "0.1.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ai-forms", - "version": "0.1.1", + "version": "0.1.2", "license": "MIT", "devDependencies": { "@eslint/js": "^9.39.5", diff --git a/package.json b/package.json index 10ba7e2..927e4fc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ai-forms", - "version": "0.1.1", + "version": "0.1.2", "description": "Headless AI form filling and conversational refinement. Provider-agnostic core, React hook, and a server route factory.", "license": "MIT", "author": "Mao Nakamoto",