From 76a1cfe4894921402a463a3012485ab7fd1897fe Mon Sep 17 00:00:00 2001 From: Flc Date: Fri, 26 Jun 2026 08:52:02 +0800 Subject: [PATCH 1/3] ci(release): use npm trusted publishing --- .github/workflows/release.yml | 14 +----- CONTRIBUTING.md | 80 +++++++++++++++-------------------- 2 files changed, 36 insertions(+), 58 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 89f61f6..71d981f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -45,15 +45,6 @@ jobs: exit 1 fi - - name: Check npm token - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - run: | - if [ -z "$NODE_AUTH_TOKEN" ]; then - echo "NPM_TOKEN secret is required to publish the npm package." - exit 1 - fi - - name: Run GoReleaser uses: goreleaser/goreleaser-action@v7 with: @@ -69,11 +60,10 @@ jobs: with: node-version: '24' registry-url: https://registry.npmjs.org + package-manager-cache: false - name: Publish npm package working-directory: npm - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} run: | node scripts/set-version.js "$GITHUB_REF_NAME" - npm publish --access public --provenance + npm publish --access public diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e0cd35d..2dd8d1e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,23 +9,24 @@ make lint make test ``` -## Release Secrets +## Release Credentials The release workflow publishes three outputs when a `v*` tag is pushed: - GitHub Release assets, using the repository `GITHUB_TOKEN` - Homebrew formula updates, using `TAP_GITHUB_TOKEN` -- npm package updates, using `NPM_TOKEN` +- npm package updates, using npm Trusted Publishing with GitHub Actions OIDC -`GITHUB_TOKEN` is created automatically by GitHub Actions. The other two tokens -must be created manually and saved as repository secrets in `go-tapd/cli`. +`GITHUB_TOKEN` is created automatically by GitHub Actions. `TAP_GITHUB_TOKEN` +must be created manually and saved as a repository secret in `go-tapd/cli`. +npm publishing does not use a long-lived repository secret; it depends on a +trusted publisher configuration on npmjs.com. -### Required Secrets +### Required Repository Secrets | Secret | Used for | Minimum access | | --- | --- | --- | | `TAP_GITHUB_TOKEN` | Commit the generated Homebrew formula to `go-tapd/homebrew-tap` | GitHub fine-grained personal access token with `Contents: Read and write` on `go-tapd/homebrew-tap` | -| `NPM_TOKEN` | Publish `@go-tapd/tapd` to npm | npm granular access token with `Read and write` access to the `@go-tapd` package scope | ### Configure `TAP_GITHUB_TOKEN` @@ -58,52 +59,38 @@ write to the tap repository. The token does not need access to issues, pull requests, actions, administration, or any repository other than `go-tapd/homebrew-tap`. -### Configure `NPM_TOKEN` - -Create this token from npm: - -1. Open `https://www.npmjs.com/settings/flc1125/tokens`. -2. Click `Generate New Token`. -3. Use a descriptive name, such as `go-tapd-cli-release`. -4. Add a description, such as `Publish @go-tapd/tapd from GitHub Actions`. -5. If npm asks for token type, choose a granular access token. -6. In `Packages and scopes`, set permission to `Read and write`. -7. Select the `@go-tapd` scope. If the scope cannot be selected before the - first package exists, temporarily select all packages, publish once, then - replace the token with a narrower `@go-tapd` token. -8. Do not rely on organization permissions alone. Organization access controls - teams and settings, but package publishing requires package or scope access. -9. Set an expiration date and note it somewhere you will check before the next - release. -10. If the account or package requires two-factor authentication for publish - actions, enable `Bypass two-factor authentication` for this CI token. -11. Generate the token and copy it immediately. +### Configure npm Trusted Publishing -Save it in the CLI repository: +Configure trusted publishing from npm: -1. Open `https://github.com/go-tapd/cli/settings/secrets/actions`. -2. Click `New repository secret`. -3. Set `Name` to `NPM_TOKEN`. -4. Paste the token into `Secret`. -5. Save the secret. +1. Open `https://www.npmjs.com/package/@go-tapd/tapd`. +2. Open the package settings. +3. Find the Trusted Publishing or Trusted Publisher section. +4. Choose GitHub Actions as the publisher. +5. Set organization or user to `go-tapd`. +6. Set repository to `cli`. +7. Set workflow filename to `release.yml`. +8. Leave environment empty unless the release workflow starts using a GitHub + environment. +9. Allow the `npm publish` action. +10. Save the trusted publisher configuration. -Why this permission is needed: the release workflow runs -`npm publish --access public --provenance` from the `npm/` package directory. -The token must be able to publish `@go-tapd/tapd`. It does not need access to -private packages or unrelated npm scopes. +Why this permission is needed: the release workflow runs `npm publish --access +public` from the `npm/` package directory. npm exchanges the GitHub Actions OIDC +identity for a short-lived publish token, so no long-lived npm repository secret +is required. -### Verify Secret Configuration +### Verify Release Configuration -Check that both GitHub repository secrets exist: +Check that the required GitHub repository secret exists: ```bash gh secret list ``` -Expected names: +Expected name: ```text -NPM_TOKEN TAP_GITHUB_TOKEN ``` @@ -124,19 +111,19 @@ Expected results: - `npm publish --access public --dry-run` prints the package contents and ends with `+ @go-tapd/tapd@...`. -The dry-run does not prove that `NPM_TOKEN` itself has the correct access. It -only proves the local npm account and package metadata are valid. The token is -exercised by the GitHub Actions release workflow. +The dry-run does not prove that the trusted publisher is configured correctly. +It only proves the local npm account and package metadata are valid. The trusted +publisher is exercised by the GitHub Actions release workflow. ### Rotate Expired Tokens -When a token is close to expiration: +When `TAP_GITHUB_TOKEN` is close to expiration: 1. Create a replacement token using the same permissions listed above. -2. Update the matching repository secret in +2. Update the repository secret in `https://github.com/go-tapd/cli/settings/secrets/actions`. 3. Re-run the verification commands. -4. Revoke the old token from GitHub or npm after the new secret is saved. +4. Revoke the old token from GitHub after the new secret is saved. Do not commit tokens, `.npmrc` files containing tokens, or screenshots that show token values. @@ -147,3 +134,4 @@ show token values. - npm Docs: [Creating and viewing access tokens](https://docs.npmjs.com/creating-and-viewing-access-tokens/) - npm Docs: [Requiring 2FA for package publishing and settings modification](https://docs.npmjs.com/requiring-2fa-for-package-publishing-and-settings-modification/) - npm Docs: [Generating provenance statements](https://docs.npmjs.com/generating-provenance-statements/) +- npm Docs: [Trusted publishing for npm packages](https://docs.npmjs.com/trusted-publishers/) From a15c08b09c96509782a46001f13e2691a32895be Mon Sep 17 00:00:00 2001 From: Flc Date: Fri, 26 Jun 2026 08:58:41 +0800 Subject: [PATCH 2/3] docs(release): clarify npm trusted publishing setup --- CONTRIBUTING.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2dd8d1e..caf18ba 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -72,13 +72,15 @@ Configure trusted publishing from npm: 7. Set workflow filename to `release.yml`. 8. Leave environment empty unless the release workflow starts using a GitHub environment. -9. Allow the `npm publish` action. -10. Save the trusted publisher configuration. +9. Under Allowed actions, select `npm publish`. +10. Click Add publisher to save the trusted publisher configuration. Why this permission is needed: the release workflow runs `npm publish --access public` from the `npm/` package directory. npm exchanges the GitHub Actions OIDC identity for a short-lived publish token, so no long-lived npm repository secret -is required. +is required. Trusted publishing automatically generates npm provenance +attestations from GitHub Actions, so the workflow does not need to pass +`--provenance`. ### Verify Release Configuration @@ -115,7 +117,7 @@ The dry-run does not prove that the trusted publisher is configured correctly. It only proves the local npm account and package metadata are valid. The trusted publisher is exercised by the GitHub Actions release workflow. -### Rotate Expired Tokens +### Rotate TAP_GITHUB_TOKEN When `TAP_GITHUB_TOKEN` is close to expiration: From 501edf67b7076a25b71367d3b3a2e6f0a51057e5 Mon Sep 17 00:00:00 2001 From: Flc Date: Fri, 26 Jun 2026 08:59:25 +0800 Subject: [PATCH 3/3] docs(release): add trusted publisher verification --- CONTRIBUTING.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index caf18ba..106e3e7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -113,6 +113,10 @@ Expected results: - `npm publish --access public --dry-run` prints the package contents and ends with `+ @go-tapd/tapd@...`. +Manually re-open the npm package settings and confirm the trusted publisher +entry still targets `go-tapd/cli`, workflow `release.yml`, and the `npm publish` +allowed action. + The dry-run does not prove that the trusted publisher is configured correctly. It only proves the local npm account and package metadata are valid. The trusted publisher is exercised by the GitHub Actions release workflow.