Skip to content

Commit dca5d45

Browse files
publish: diagnose OIDC claims + consolidate approval gate (#181)
Two changes: 1. Added a "Diagnose OIDC token claims" step to publish-ts that fetches the OIDC token with audience=npm:registry.npmjs.org and decodes the payload, printing sub/repository/job_workflow_ref/ environment/ref. This tells us exactly what claims npm sees and whether they match the trusted-publisher config. Background: v3.8.2 retry on npm still ENEEDAUTH'd even with npm 11.16.0 (well above 11.5.1 OIDC minimum). Hypothesis: when publish.yml runs via workflow_call from auto-release-on-version-bump.yml, the job_workflow_ref claim may point at the calling workflow rather than publish.yml, and npm's trusted-publisher is exact-match on filename. 2. Removed the standalone `approval` job. It had environment: release which triggered a separate approval prompt in addition to the prompts on publish-py/publish-ts. Now publish-py and publish-ts gate themselves on the env (one prompt covers both since they're parallel), and each logs the actual approver as its first step using /actions/runs/{id}/approvals (which requires actions: read permission, added). Both jobs' needs changed from [preflight, approval] to [preflight].
1 parent 757f91b commit dca5d45

1 file changed

Lines changed: 56 additions & 28 deletions

File tree

.github/workflows/publish.yml

Lines changed: 56 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -131,19 +131,25 @@ jobs:
131131
132132
echo "::notice::Both registries confirm ${VERSION} is unpublished. Proceeding."
133133
134-
approval:
135-
name: Release approval gate
136-
needs: preflight
137-
runs-on: ubuntu-latest
134+
publish-py:
135+
name: Publish Python SDK to PyPI
136+
needs: [preflight]
138137
environment: release
138+
runs-on: ubuntu-latest
139139
permissions:
140-
actions: read
140+
id-token: write # required for PyPI trusted publishing OIDC
141+
contents: read
142+
actions: read # required for /actions/runs/{id}/approvals lookup
141143
steps:
142-
- name: Record approver from deployment review log
144+
- name: Checkout
145+
uses: actions/checkout@v4
146+
with:
147+
ref: ${{ inputs.tag || github.event.release.tag_name }}
148+
149+
- name: Record approver
143150
env:
144151
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
145-
RELEASE_TAG: ${{ inputs.tag || github.event.release.tag_name }}
146-
TARGET_VERSION: ${{ needs.preflight.outputs.version }}
152+
TARGET_TAG: ${{ inputs.tag || github.event.release.tag_name }}
147153
run: |
148154
set -euo pipefail
149155
APPROVER="$(gh api \
@@ -153,25 +159,7 @@ jobs:
153159
if [ -z "$APPROVER" ] || [ "$APPROVER" = "null" ]; then
154160
APPROVER="(approver lookup failed; check Actions UI)"
155161
fi
156-
echo "::notice::Release approved by @${APPROVER} for release ${RELEASE_TAG} (v${TARGET_VERSION}). Triggered by @${GITHUB_ACTOR}. Run ${GITHUB_RUN_ID}."
157-
echo "Approver: @${APPROVER}"
158-
echo "Triggered by: @${GITHUB_ACTOR}"
159-
echo "Release tag: ${RELEASE_TAG}"
160-
echo "Version: ${TARGET_VERSION}"
161-
162-
publish-py:
163-
name: Publish Python SDK to PyPI
164-
needs: [preflight, approval]
165-
environment: release
166-
runs-on: ubuntu-latest
167-
permissions:
168-
id-token: write # required for PyPI trusted publishing OIDC
169-
contents: read
170-
steps:
171-
- name: Checkout
172-
uses: actions/checkout@v4
173-
with:
174-
ref: ${{ inputs.tag || github.event.release.tag_name }}
162+
echo "::notice::Release ${TARGET_TAG} approved by @${APPROVER}, triggered by @${GITHUB_ACTOR}, run ${GITHUB_RUN_ID}."
175163
176164
- name: Install uv
177165
uses: astral-sh/setup-uv@v6
@@ -191,18 +179,34 @@ jobs:
191179

192180
publish-ts:
193181
name: Publish TypeScript SDK to npm
194-
needs: [preflight, approval]
182+
needs: [preflight]
195183
environment: release
196184
runs-on: ubuntu-latest
197185
permissions:
198186
id-token: write # required for npm OIDC trusted publishing
199187
contents: read
188+
actions: read # required for /actions/runs/{id}/approvals lookup
200189
steps:
201190
- name: Checkout
202191
uses: actions/checkout@v4
203192
with:
204193
ref: ${{ inputs.tag || github.event.release.tag_name }}
205194

195+
- name: Record approver
196+
env:
197+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
198+
TARGET_TAG: ${{ inputs.tag || github.event.release.tag_name }}
199+
run: |
200+
set -euo pipefail
201+
APPROVER="$(gh api \
202+
"/repos/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}/approvals" \
203+
--jq '[.[] | select(.environments[]?.name=="release") | .user.login] | last' \
204+
2>/dev/null || echo "")"
205+
if [ -z "$APPROVER" ] || [ "$APPROVER" = "null" ]; then
206+
APPROVER="(approver lookup failed; check Actions UI)"
207+
fi
208+
echo "::notice::Release ${TARGET_TAG} approved by @${APPROVER}, triggered by @${GITHUB_ACTOR}, run ${GITHUB_RUN_ID}."
209+
206210
- name: Setup pnpm
207211
uses: pnpm/action-setup@v4
208212
with:
@@ -223,6 +227,30 @@ jobs:
223227
working-directory: browser-use-node
224228
run: pnpm build
225229

230+
- name: Diagnose OIDC token claims
231+
env:
232+
ACTIONS_ID_TOKEN_REQUEST_TOKEN: ${{ env.ACTIONS_ID_TOKEN_REQUEST_TOKEN }}
233+
ACTIONS_ID_TOKEN_REQUEST_URL: ${{ env.ACTIONS_ID_TOKEN_REQUEST_URL }}
234+
run: |
235+
set -euo pipefail
236+
# Fetch an OIDC token with audience=npm:registry.npmjs.org (matches what npm uses).
237+
TOKEN_JSON="$(curl -fsSL \
238+
-H "Authorization: Bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
239+
"$ACTIONS_ID_TOKEN_REQUEST_URL&audience=npm:registry.npmjs.org")"
240+
TOKEN="$(echo "$TOKEN_JSON" | python3 -c 'import sys,json; print(json.load(sys.stdin)["value"])')"
241+
# Decode the payload (middle segment) — strip any URL-safe padding issues.
242+
PAYLOAD="$(echo "$TOKEN" | cut -d. -f2 | tr '_-' '/+' | base64 -d 2>/dev/null || echo "$TOKEN" | cut -d. -f2 | python3 -c 'import sys,base64,json; s=sys.stdin.read().strip(); s+="="*(-len(s)%4); print(base64.urlsafe_b64decode(s).decode())')"
243+
echo "::group::OIDC token claims (audience=npm:registry.npmjs.org)"
244+
echo "$PAYLOAD" | python3 -m json.tool 2>/dev/null || echo "$PAYLOAD"
245+
echo "::endgroup::"
246+
# Highlight the fields npm checks against the trusted-publisher config.
247+
echo "Key fields:"
248+
echo " sub: $(echo "$PAYLOAD" | python3 -c 'import sys,json; print(json.load(sys.stdin).get("sub"))')"
249+
echo " repository: $(echo "$PAYLOAD" | python3 -c 'import sys,json; print(json.load(sys.stdin).get("repository"))')"
250+
echo " job_workflow_ref: $(echo "$PAYLOAD" | python3 -c 'import sys,json; print(json.load(sys.stdin).get("job_workflow_ref"))')"
251+
echo " environment: $(echo "$PAYLOAD" | python3 -c 'import sys,json; print(json.load(sys.stdin).get("environment"))')"
252+
echo " ref: $(echo "$PAYLOAD" | python3 -c 'import sys,json; print(json.load(sys.stdin).get("ref"))')"
253+
226254
- name: Publish to npm (OIDC trusted publishing)
227255
working-directory: browser-use-node
228256
run: |

0 commit comments

Comments
 (0)