From 89d07413124fa6203997ede8042e12571be4b5ed Mon Sep 17 00:00:00 2001 From: Farhan Anjum Date: Fri, 17 Jul 2026 22:02:37 +0600 Subject: [PATCH 1/5] [FSSDK-12891] Switch npm publishing to OIDC trusted publishing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- .github/workflows/release.yml | 30 +++++++++++++++++------------- scripts/publish.sh | 17 ++++++++++++++--- 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 29a13ace4..8b79c33b8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,14 +5,17 @@ on: types: [published, edited] workflow_dispatch: {} +permissions: + id-token: write + contents: read + packages: write + jobs: publish: name: Publish to NPM and GitHub Package Registry runs-on: ubuntu-latest if: ${{ github.event_name == 'workflow_dispatch' || !github.event.release.draft }} - permissions: - contents: read - packages: write + environment: npm steps: - name: Checkout branch uses: actions/checkout@v4 @@ -20,17 +23,16 @@ jobs: persist-credentials: false - name: Setup Node - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: - node-version: 18 - registry-url: "https://registry.npmjs.org/" - always-auth: "true" + node-version: 22 + + - name: Upgrade npm for OIDC support + run: npm install -g npm@latest - # Add auth for the GitHub Package Registry publish. npm expands - # ${NODE_AUTH_TOKEN} at runtime per-step, so each publish step uses the - # right token for its registry. We do NOT route the @optimizely scope to - # GHR, so `npm ci` still resolves dependencies from npm; publish.sh passes - # --registry explicitly to target GHR. + # Configure auth for GHR only. npm registry auth is handled by OIDC + # (no .npmrc token entry needed — omitting registry-url from setup-node + # ensures npm falls through to OIDC for registry.npmjs.org). - name: Configure GitHub Package Registry auth run: echo "//npm.pkg.github.com/:_authToken=\${NODE_AUTH_TOKEN}" >> ~/.npmrc @@ -55,10 +57,12 @@ jobs: # current latest, otherwise v-last-published; beta/alpha/rc for # pre-releases. npm publishes first; if it fails the job stops before GHR, # keeping the registries in sync. + # + # npm publish uses OIDC trusted publishing (no NPM_PUBLISH_TOKEN needed). + # publish.sh adds --provenance for supply-chain attestations. - id: release name: Publish to NPM env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }} TARBALL: ${{ steps.pack.outputs.tarball }} DRY_RUN: ${{ github.event_name == 'workflow_dispatch' }} run: scripts/publish.sh "https://registry.npmjs.org" "$TARBALL" diff --git a/scripts/publish.sh b/scripts/publish.sh index e9f874961..1bcbbb4ed 100755 --- a/scripts/publish.sh +++ b/scripts/publish.sh @@ -87,8 +87,12 @@ trap 'rm -f "$body_file"' EXIT # curl -sS (no --fail) returns 0 for any HTTP response, non-zero only on # network/protocol errors — so we can cleanly separate "server answered" from # "could not reach server". +curl_auth_args=() +if [[ -n "${NODE_AUTH_TOKEN:-}" ]]; then + curl_auth_args=(-H "Authorization: Bearer ${NODE_AUTH_TOKEN}") +fi if ! http_code=$(curl -sS -m 30 -o "$body_file" -w '%{http_code}' \ - -H "Authorization: Bearer ${NODE_AUTH_TOKEN:-}" "$packument_url"); then + "${curl_auth_args[@]}" "$packument_url"); then echo "ERROR: request to ${packument_url} failed (network/timeout); refusing to publish on an ambiguous result." >&2 exit 1 fi @@ -134,11 +138,18 @@ if [[ "$dry_run" == "true" ]]; then exit 0 fi +# Add --provenance for npm registry (OIDC trusted publishing generates +# supply-chain attestations). Not supported by GitHub Package Registry. +provenance_flag="" +if [[ "$registry" == *"registry.npmjs.org"* ]]; then + provenance_flag="--provenance" +fi + echo "Publishing ${pkg}@${version} (tag: ${tag}) to ${registry}" if [[ -n "$tarball" ]]; then # The tarball is a prebuilt artifact; skip lifecycle scripts (prepublishOnly # = test + build) that would otherwise run from the current package.json. - npm publish "$tarball" --registry "$registry" --tag "$tag" --ignore-scripts + npm publish "$tarball" --registry "$registry" --tag "$tag" --ignore-scripts $provenance_flag else - npm publish --registry "$registry" --tag "$tag" + npm publish --registry "$registry" --tag "$tag" $provenance_flag fi From 8ae9b36aa8f39c1996653ec43b6eef7f6d0eebdc Mon Sep 17 00:00:00 2001 From: Farhan Anjum Date: Fri, 17 Jul 2026 22:25:13 +0600 Subject: [PATCH 2/5] test: temp version bump for OIDC test (revert before merge) --- lib/utils/enums/index.ts | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/utils/enums/index.ts b/lib/utils/enums/index.ts index 65f511fe8..94610f709 100644 --- a/lib/utils/enums/index.ts +++ b/lib/utils/enums/index.ts @@ -44,7 +44,7 @@ export const CONTROL_ATTRIBUTES = { export const JAVASCRIPT_CLIENT_ENGINE = 'javascript-sdk'; export const NODE_CLIENT_ENGINE = 'node-sdk'; export const REACT_NATIVE_JS_CLIENT_ENGINE = 'react-native-js-sdk'; -export const CLIENT_VERSION = '6.5.0'; +export const CLIENT_VERSION = '6.5.1-test.0'; /* * Represents the source of a decision for feature management. When a feature diff --git a/package-lock.json b/package-lock.json index 7af8d336f..4c91d4f3a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@optimizely/optimizely-sdk", - "version": "6.5.0", + "version": "6.5.1-test.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@optimizely/optimizely-sdk", - "version": "6.5.0", + "version": "6.5.1-test.0", "license": "Apache-2.0", "dependencies": { "decompress-response": "^7.0.0", diff --git a/package.json b/package.json index 84ce000b4..d189686ca 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@optimizely/optimizely-sdk", - "version": "6.5.0", + "version": "6.5.1-test.0", "description": "JavaScript SDK for Optimizely Feature Experimentation, Optimizely Full Stack (legacy), and Optimizely Rollouts", "main": "./dist/index.node.min.js", "browser": "./dist/index.browser.es.min.js", From 5118b1f5b28f41caca5691f471a1846dd578f9b1 Mon Sep 17 00:00:00 2001 From: Farhan Anjum Date: Fri, 17 Jul 2026 22:35:27 +0600 Subject: [PATCH 3/5] fix: use string instead of array for optional curl auth header --- scripts/publish.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/publish.sh b/scripts/publish.sh index 1bcbbb4ed..a10f0d0b0 100755 --- a/scripts/publish.sh +++ b/scripts/publish.sh @@ -87,12 +87,12 @@ trap 'rm -f "$body_file"' EXIT # curl -sS (no --fail) returns 0 for any HTTP response, non-zero only on # network/protocol errors — so we can cleanly separate "server answered" from # "could not reach server". -curl_auth_args=() +auth_header="" if [[ -n "${NODE_AUTH_TOKEN:-}" ]]; then - curl_auth_args=(-H "Authorization: Bearer ${NODE_AUTH_TOKEN}") + auth_header="Authorization: Bearer ${NODE_AUTH_TOKEN}" fi if ! http_code=$(curl -sS -m 30 -o "$body_file" -w '%{http_code}' \ - "${curl_auth_args[@]}" "$packument_url"); then + ${auth_header:+-H "$auth_header"} "$packument_url"); then echo "ERROR: request to ${packument_url} failed (network/timeout); refusing to publish on an ambiguous result." >&2 exit 1 fi From deeda8dc6a0ae913489ab0f5ef7f8d07c433f8f4 Mon Sep 17 00:00:00 2001 From: Farhan Anjum Date: Fri, 17 Jul 2026 22:42:26 +0600 Subject: [PATCH 4/5] test: switch to rc.1 version for OIDC test --- lib/utils/enums/index.ts | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/utils/enums/index.ts b/lib/utils/enums/index.ts index 94610f709..a791c3a4b 100644 --- a/lib/utils/enums/index.ts +++ b/lib/utils/enums/index.ts @@ -44,7 +44,7 @@ export const CONTROL_ATTRIBUTES = { export const JAVASCRIPT_CLIENT_ENGINE = 'javascript-sdk'; export const NODE_CLIENT_ENGINE = 'node-sdk'; export const REACT_NATIVE_JS_CLIENT_ENGINE = 'react-native-js-sdk'; -export const CLIENT_VERSION = '6.5.1-test.0'; +export const CLIENT_VERSION = '6.5.1-rc.1'; /* * Represents the source of a decision for feature management. When a feature diff --git a/package-lock.json b/package-lock.json index 4c91d4f3a..ba7e79eb3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@optimizely/optimizely-sdk", - "version": "6.5.1-test.0", + "version": "6.5.1-rc.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@optimizely/optimizely-sdk", - "version": "6.5.1-test.0", + "version": "6.5.1-rc.1", "license": "Apache-2.0", "dependencies": { "decompress-response": "^7.0.0", diff --git a/package.json b/package.json index d189686ca..5e9ee6129 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@optimizely/optimizely-sdk", - "version": "6.5.1-test.0", + "version": "6.5.1-rc.1", "description": "JavaScript SDK for Optimizely Feature Experimentation, Optimizely Full Stack (legacy), and Optimizely Rollouts", "main": "./dist/index.node.min.js", "browser": "./dist/index.browser.es.min.js", From 7bbcfb0c5be9bc586440a8ae9c010406250b0535 Mon Sep 17 00:00:00 2001 From: Farhan Anjum Date: Fri, 17 Jul 2026 22:57:19 +0600 Subject: [PATCH 5/5] revert: remove temp version bump --- lib/utils/enums/index.ts | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/utils/enums/index.ts b/lib/utils/enums/index.ts index a791c3a4b..65f511fe8 100644 --- a/lib/utils/enums/index.ts +++ b/lib/utils/enums/index.ts @@ -44,7 +44,7 @@ export const CONTROL_ATTRIBUTES = { export const JAVASCRIPT_CLIENT_ENGINE = 'javascript-sdk'; export const NODE_CLIENT_ENGINE = 'node-sdk'; export const REACT_NATIVE_JS_CLIENT_ENGINE = 'react-native-js-sdk'; -export const CLIENT_VERSION = '6.5.1-rc.1'; +export const CLIENT_VERSION = '6.5.0'; /* * Represents the source of a decision for feature management. When a feature diff --git a/package-lock.json b/package-lock.json index ba7e79eb3..7af8d336f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@optimizely/optimizely-sdk", - "version": "6.5.1-rc.1", + "version": "6.5.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@optimizely/optimizely-sdk", - "version": "6.5.1-rc.1", + "version": "6.5.0", "license": "Apache-2.0", "dependencies": { "decompress-response": "^7.0.0", diff --git a/package.json b/package.json index 5e9ee6129..84ce000b4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@optimizely/optimizely-sdk", - "version": "6.5.1-rc.1", + "version": "6.5.0", "description": "JavaScript SDK for Optimizely Feature Experimentation, Optimizely Full Stack (legacy), and Optimizely Rollouts", "main": "./dist/index.node.min.js", "browser": "./dist/index.browser.es.min.js",