Skip to content

Commit 562baff

Browse files
authored
fix release: migrate npm publish to OIDC trusted publishing (#197)
* fix release: migrate npm publish to OIDC trusted publishing The 2.1.0 release never reached npm: the release job created the git tag before publishing, so ncipollo/release-action failed with 422 already_exists (tag_name) and the job aborted before npm publish ran. npm is still stuck at 2.0.0. Align the workflow with react-apple-signin-auth's working setup: - migrate to npm OIDC trusted publishing (id-token: write, bare npm publish, drop NODE_AUTH_TOKEN/NPM_TOKEN) - bump Node to 24.x (ships npm >= 11.5.1, required for OIDC) - guard the GitHub release and npm publish on whether the version tag already exists (check_tag), so re-runs no longer fail - fetch-depth: 0 and multiline COMMIT_LOG for correct changelogs - scope triggers to main pushes + PRs, add workflow_dispatch - remove obsolete CircleCI config (no longer used) * address review: least-privilege perms, decoupled recovery gates - move contents/id-token write to the publish job; default read-only - gate npm publish on npm version existence and GitHub release on release existence (decoupled), so reruns recover when a tag/release exists but the npm version is missing (the #196 failure mode), matching the pattern in pi-autoresearch/turbovector - pin the release tag to the built commit (github.sha) instead of main - scope pull_request trigger to main; add concurrency guard * review: clarify package-metadata step quoting Capture version/name into variables with single-quoted node -e args instead of nested double quotes. Functionally equivalent (bash $() handles nested quotes) but clearer and matches the pi-autoresearch idiom.
1 parent e016197 commit 562baff

2 files changed

Lines changed: 63 additions & 51 deletions

File tree

.circleci/config.yml

Lines changed: 0 additions & 17 deletions
This file was deleted.

.github/workflows/test-publish.yml

Lines changed: 63 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
11
name: Test Release Publish
22

3-
on: [push]
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
workflow_dispatch:
49

10+
# Least privilege by default; the publish job elevates for itself only.
511
permissions:
6-
contents: write
7-
pull-requests: write
8-
issues: write
12+
contents: read
13+
14+
concurrency:
15+
group: publish-${{ github.ref }}
16+
cancel-in-progress: false
917

1018
jobs:
1119
test:
@@ -20,15 +28,15 @@ jobs:
2028
- uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
2129
with:
2230
path: ${{ steps.yarn-cache.outputs.dir }}
23-
key: ubuntu-latest-node-20.18-yarn-${{ hashFiles('**/yarn.lock') }}
31+
key: ubuntu-latest-node-24.x-yarn-${{ hashFiles('**/yarn.lock') }}
2432
restore-keys: |
25-
ubuntu-latest-node-20.18-yarn-
33+
ubuntu-latest-node-24.x-yarn-
2634
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
2735
with:
28-
node-version: '20.18'
36+
node-version: '24.x'
2937
registry-url: https://registry.npmjs.org/
3038
- name: Install
31-
run: yarn install
39+
run: yarn install --frozen-lockfile
3240
- name: Test
3341
run: yarn test --ci --bail
3442
- name: Test TypeScript Type Definitions
@@ -41,42 +49,63 @@ jobs:
4149
runs-on: ubuntu-latest
4250
needs: test
4351
if: success() && github.ref == 'refs/heads/main'
52+
permissions:
53+
contents: write # create GitHub release + tag
54+
id-token: write # OIDC token for npm trusted publishing
4455

4556
steps:
4657
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
47-
- name: Get yarn cache
48-
id: yarn-cache
49-
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
50-
- uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
5158
with:
52-
path: ${{ steps.yarn-cache.outputs.dir }}
53-
key: ubuntu-latest-node-20.18-yarn-${{ hashFiles('**/yarn.lock') }}
54-
restore-keys: |
55-
ubuntu-latest-node-20.18-yarn-
59+
fetch-depth: 0 # full history so git describe/changelog works
5660
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
5761
with:
58-
node-version: '20.18'
62+
node-version: '24.x' # ships npm >= 11.5.1, required for OIDC trusted publishing
5963
registry-url: https://registry.npmjs.org/
64+
package-manager-cache: false # never cache in release builds
6065
- name: Install
61-
run: yarn install
66+
run: yarn install --frozen-lockfile
6267
- name: Build
6368
run: NODE_ENV=production yarn build
64-
- name: Setup env vars
65-
id: ownEnvVars
69+
- name: Read package metadata
70+
id: pkg
71+
run: |
72+
VERSION="$(node -p -e 'require("./package.json").version')"
73+
NAME="$(node -p -e 'require("./package.json").name')"
74+
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
75+
echo "name=$NAME" >> "$GITHUB_OUTPUT"
76+
- name: Build changelog
77+
id: changelog
78+
run: |
79+
echo "body<<EOF" >> "$GITHUB_OUTPUT"
80+
git log $(git describe --tags --abbrev=0 2>/dev/null || git rev-list --max-parents=0 HEAD)..HEAD --format='- %s (%an)' >> "$GITHUB_OUTPUT"
81+
echo "EOF" >> "$GITHUB_OUTPUT"
82+
# Decoupled gates so a rerun recovers the exact failure in #196
83+
# (release/tag exists but npm version is missing).
84+
- name: Check if version already published to npm
85+
id: check_npm
6686
run: |
67-
# Set PACKAGE_VERSION
68-
PACKAGE_VERSION=$(node -p -e "require('./package.json').version")
69-
echo "PACKAGE_VERSION=$PACKAGE_VERSION" >> $GITHUB_ENV
70-
# Set COMMIT_LOG
71-
COMMIT_LOG=`git log $(git describe --tags --abbrev=0)..HEAD --format='%s - %an'`
72-
echo "COMMIT_LOG=$COMMIT_LOG" >> $GITHUB_ENV
73-
- uses: ncipollo/release-action@339a81892b84b4eeb0f6e744e4574d79d0d9b8dd # v1
87+
if npm view "${{ steps.pkg.outputs.name }}@${{ steps.pkg.outputs.version }}" version >/dev/null 2>&1; then
88+
echo "publish=false" >> "$GITHUB_OUTPUT"
89+
else
90+
echo "publish=true" >> "$GITHUB_OUTPUT"
91+
fi
92+
- name: Check if GitHub release already exists
93+
id: check_release
94+
env:
95+
GH_TOKEN: ${{ github.token }}
96+
run: |
97+
if gh release view "${{ steps.pkg.outputs.version }}" >/dev/null 2>&1; then
98+
echo "create=false" >> "$GITHUB_OUTPUT"
99+
else
100+
echo "create=true" >> "$GITHUB_OUTPUT"
101+
fi
102+
- name: Create GitHub release
103+
if: steps.check_release.outputs.create == 'true'
104+
uses: ncipollo/release-action@339a81892b84b4eeb0f6e744e4574d79d0d9b8dd # v1
74105
with:
75-
# token: ${{ secrets.GITHUB_TOKEN }} # no need for personal token
76-
tag: ${{ env.PACKAGE_VERSION }}
77-
commit: main
78-
body: ${{ env.COMMIT_LOG }}
106+
tag: ${{ steps.pkg.outputs.version }}
107+
commit: ${{ github.sha }} # pin the tag to the built commit, not the current tip of main
108+
body: ${{ steps.changelog.outputs.body }}
79109
- name: npm publish
80-
run: npm publish
81-
env:
82-
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
110+
if: steps.check_npm.outputs.publish == 'true'
111+
run: npm publish # OIDC trusted publishing — no NODE_AUTH_TOKEN needed

0 commit comments

Comments
 (0)