Skip to content

Commit f036634

Browse files
[FSSDK-12891] Switch npm publishing to OIDC trusted publishing
Replace long-lived NPM_PUBLISH_TOKEN with OIDC trusted publishing for more secure, tokenless CI publishing to npm. Workflow changes: - Add id-token: write permission for OIDC token generation - Move permissions to workflow level (add packages: write for GHR) - Add GitHub environment gate (npm) for deployment protection - Upgrade setup-node to v4 and Node to 22 (npm 11.5.1+ requirement) - Add npm upgrade step to ensure OIDC support - Remove NPM_PUBLISH_TOKEN from npm publish step (OIDC replaces it) publish.sh changes: - Make NODE_AUTH_TOKEN optional in curl lookup (public packages don't need auth for registry reads) - Add --provenance flag for npm registry publishes (supply-chain attestations, not supported by GHR) GHR publish step is unchanged — still uses GITHUB_TOKEN. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 0105135 commit f036634

2 files changed

Lines changed: 28 additions & 10 deletions

File tree

.github/workflows/release.yml

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,31 @@ on:
55
types: [published, edited]
66
workflow_dispatch: {}
77

8+
permissions:
9+
id-token: write
10+
contents: read
11+
packages: write
12+
813
jobs:
914
publish:
1015
name: Publish to NPM and GitHub Package Registry
1116
runs-on: ubuntu-latest
1217
if: ${{ github.event_name == 'workflow_dispatch' || !github.event.release.draft }}
13-
permissions:
14-
contents: read
15-
packages: write
18+
environment: npm
1619
steps:
1720
- name: Checkout branch
1821
uses: actions/checkout@v4
1922
with:
2023
persist-credentials: false
2124

2225
- name: Setup Node
23-
uses: actions/setup-node@v3
26+
uses: actions/setup-node@v4
2427
with:
25-
node-version: 18
28+
node-version: 22
2629
registry-url: "https://registry.npmjs.org/"
27-
always-auth: "true"
30+
31+
- name: Upgrade npm for OIDC support
32+
run: npm install -g npm@latest
2833

2934
# Add auth for the GitHub Package Registry publish. npm expands
3035
# ${NODE_AUTH_TOKEN} at runtime per-step, so each publish step uses the
@@ -55,10 +60,12 @@ jobs:
5560
# current latest, otherwise v<major>-last-published; beta/alpha/rc for
5661
# pre-releases. npm publishes first; if it fails the job stops before GHR,
5762
# keeping the registries in sync.
63+
#
64+
# npm publish uses OIDC trusted publishing (no NPM_PUBLISH_TOKEN needed).
65+
# publish.sh adds --provenance for supply-chain attestations.
5866
- id: release
5967
name: Publish to NPM
6068
env:
61-
NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
6269
TARBALL: ${{ steps.pack.outputs.tarball }}
6370
DRY_RUN: ${{ github.event_name == 'workflow_dispatch' }}
6471
run: scripts/publish.sh "https://registry.npmjs.org" "$TARBALL"

scripts/publish.sh

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,12 @@ trap 'rm -f "$body_file"' EXIT
8787
# curl -sS (no --fail) returns 0 for any HTTP response, non-zero only on
8888
# network/protocol errors — so we can cleanly separate "server answered" from
8989
# "could not reach server".
90+
curl_auth_args=()
91+
if [[ -n "${NODE_AUTH_TOKEN:-}" ]]; then
92+
curl_auth_args=(-H "Authorization: Bearer ${NODE_AUTH_TOKEN}")
93+
fi
9094
if ! http_code=$(curl -sS -m 30 -o "$body_file" -w '%{http_code}' \
91-
-H "Authorization: Bearer ${NODE_AUTH_TOKEN:-}" "$packument_url"); then
95+
"${curl_auth_args[@]}" "$packument_url"); then
9296
echo "ERROR: request to ${packument_url} failed (network/timeout); refusing to publish on an ambiguous result." >&2
9397
exit 1
9498
fi
@@ -134,11 +138,18 @@ if [[ "$dry_run" == "true" ]]; then
134138
exit 0
135139
fi
136140

141+
# Add --provenance for npm registry (OIDC trusted publishing generates
142+
# supply-chain attestations). Not supported by GitHub Package Registry.
143+
provenance_flag=""
144+
if [[ "$registry" == *"registry.npmjs.org"* ]]; then
145+
provenance_flag="--provenance"
146+
fi
147+
137148
echo "Publishing ${pkg}@${version} (tag: ${tag}) to ${registry}"
138149
if [[ -n "$tarball" ]]; then
139150
# The tarball is a prebuilt artifact; skip lifecycle scripts (prepublishOnly
140151
# = test + build) that would otherwise run from the current package.json.
141-
npm publish "$tarball" --registry "$registry" --tag "$tag" --ignore-scripts
152+
npm publish "$tarball" --registry "$registry" --tag "$tag" --ignore-scripts $provenance_flag
142153
else
143-
npm publish --registry "$registry" --tag "$tag"
154+
npm publish --registry "$registry" --tag "$tag" $provenance_flag
144155
fi

0 commit comments

Comments
 (0)