Skip to content

Commit 74c633a

Browse files
williabyclaude
andauthored
fix(release): apply PSR v10.5.3 bug mitigations to reusable workflow (#184)
PSR v10.5.3 has two bugs affecting the no-PyPI release pattern: 1. 422 on GitHub Release creation: github3 library serializes draft/prerelease as JSON strings rather than booleans. Fix: vcs_release: "false" + separate gh release create step that types booleans correctly. 2. Detached HEAD prevents branch matching: actions/checkout with ref: <sha> leaves HEAD detached; PSR needs an attached branch to match branches config. Fix: git checkout -B "$HEAD_BRANCH" after checkout. Also adds: - Fork verification step for workflow_run callers (S7631) - SHA-pinned checkout (ref: workflow_run.head_sha || github.sha) - commit: "false" so PSR only pushes tags (branch rulesets exempt tag refs) - publish-to-pypi default changed from true to false Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent e75a86b commit 74c633a

1 file changed

Lines changed: 41 additions & 6 deletions

File tree

.github/workflows/python-release.yml

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ on:
6666
description: 'Publish to PyPI after release'
6767
required: false
6868
type: boolean
69-
default: true
69+
default: false
7070
pypi-package-name:
7171
description: 'PyPI package name'
7272
required: false
@@ -249,6 +249,17 @@ jobs:
249249
with:
250250
egress-policy: audit
251251

252+
- name: Verify trusted workflow_run source
253+
if: github.event_name == 'workflow_run'
254+
env:
255+
HEAD_OWNER: ${{ github.event.workflow_run.head_repository.owner.login }}
256+
REPO_OWNER: ${{ github.repository_owner }}
257+
run: |
258+
if [ "$HEAD_OWNER" != "$REPO_OWNER" ]; then
259+
echo "::error::Refusing: triggering workflow originated from a fork (owner: $HEAD_OWNER)"
260+
exit 1
261+
fi
262+
252263
- name: Validate skip-tests opt-out
253264
if: ${{ !inputs.run-tests }}
254265
env:
@@ -266,6 +277,16 @@ jobs:
266277
with:
267278
fetch-depth: 0
268279
token: ${{ github.token }}
280+
ref: ${{ github.event.workflow_run.head_sha || github.sha }}
281+
282+
- name: Attach HEAD to branch
283+
if: github.event_name == 'workflow_run'
284+
env:
285+
HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }}
286+
run: |
287+
# Checking out a SHA leaves the repo in detached HEAD state.
288+
# PSR needs an attached branch to match against branches config.
289+
git checkout -B "$HEAD_BRANCH"
269290
270291
- name: Set up Python
271292
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
@@ -347,6 +368,15 @@ jobs:
347368
git_committer_name: "github-actions[bot]"
348369
git_committer_email: "github-actions[bot]@users.noreply.github.com"
349370
force_level: ${{ inputs.force-release }}
371+
# Branch ruleset blocks direct commits; tag refs are exempt. Skipping
372+
# the version-bump commit means PSR only pushes a tag, which succeeds
373+
# without any bypass actor.
374+
commit: "false"
375+
# PSR v10.5.3 serializes draft/prerelease as JSON strings ("false")
376+
# instead of booleans, which GitHub's Releases API rejects with 422.
377+
# Release creation is handled by the step below via gh, which types
378+
# booleans correctly.
379+
vcs_release: "false"
350380

351381
- name: Sign artifacts with Sigstore
352382
if: ${{ inputs.sign-artifacts && (steps.semantic-release.outputs.released == 'true' || !inputs.semantic-release) }}
@@ -355,12 +385,17 @@ jobs:
355385
inputs: ./dist/*.tar.gz ./dist/*.whl
356386
release-signing-artifacts: true
357387

358-
- name: Upload to GitHub Release (Semantic)
388+
- name: Create GitHub Release
359389
if: ${{ inputs.semantic-release && steps.semantic-release.outputs.released == 'true' }}
360-
uses: python-semantic-release/publish-action@310a9983a0ae878b29f3aac778d7c77c1db27378 # v10.5.3
361-
with:
362-
github_token: ${{ github.token }}
363-
tag: ${{ steps.semantic-release.outputs.tag }}
390+
env:
391+
GH_TOKEN: ${{ github.token }}
392+
TAG: ${{ steps.semantic-release.outputs.tag }}
393+
run: |
394+
gh release create "$TAG" \
395+
--title "$TAG" \
396+
--generate-notes \
397+
--repo "$GITHUB_REPOSITORY" \
398+
dist/*
364399
365400
# Manual Release Mode
366401
- name: Manual Release

0 commit comments

Comments
 (0)