Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 44 additions & 13 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,56 +14,87 @@ jobs:

release:
needs: run_tests
if: github.ref_name == 'master'
runs-on: ubuntu-latest
if: github.ref_name == 'master'
concurrency:
group: ${{ github.workflow }}-release
group: ${{ github.workflow }}-release-${{ github.ref_name }}
cancel-in-progress: false

permissions:
contents: write

steps:
- name: Checkout
- name: Setup | Checkout Repository on Release Branch
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.ref_name }}

- name: Reset to triggered commit
run: git reset --hard ${{ github.sha }}
- name: Setup | Force release branch to be at workflow sha
run: |
git reset --hard ${{ github.sha }}

- name: Python Semantic Release
- name: Action | Semantic Version Release
id: release
# Adjust tag with desired version if applicable.
uses: python-semantic-release/python-semantic-release@39dd2052f2ce8282a5d932c31d58a2ca06d2550e # v10.6.1

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This correctly fixes the immutable-releases problem (matches the proven sample-plugin/event-tracking pattern), but there's a live, separate issue this doesn't address: GitPython 3.1.60 (uploaded 2026-08-25T18:33:44Z) removed Actor.name_email_regex, which PSR's config loader reads unconditionally. PSR's action builds its own Docker image fresh on every run and pins gitpython~=3.0 (unbounded), so any fresh build now crashes at config-load, before the release logic even runs -- regardless of which PSR version is pinned. Still open upstream: python-semantic-release/python-semantic-release#1476, fix in #1477 not merged/released.

We hit this for real on openedx/event-tracking and fixed it by running PSR via uv instead of the Docker action (pinning gitpython<3.1.60) and switching build_command to uv build (uv's ephemeral venvs don't bundle pip). See event-tracking#435/#436 for the exact diff -- happy to open the same fix here if useful.

with:
github_token: ${{ secrets.GITHUB_TOKEN }}
git_committer_name: "github-actions[bot]"
git_committer_email: "github-actions[bot]@users.noreply.github.com"
git_committer_name: "github-actions"
git_committer_email: "actions@users.noreply.github.com"
changelog: "false"
# Commit, tag, push and build, but don't create the GitHub release.
# We create it ourselves in the next step so that the distributions
# are attached before the release is published. See that step for why.
vcs_release: "false"

- name: Upload dist artifacts
# This repo has immutable releases enabled, which freezes a release's
# assets the moment it is published, so assets cannot be attached
# afterwards. `gh release create` handles this by creating the release as
# a draft, uploading the assets, and only then publishing it:
# https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/immutable-releases
- name: Publish | Create GitHub Release with Assets
if: steps.release.outputs.released == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
RELEASE_NOTES: ${{ steps.release.outputs.release_notes }}
TAG: ${{ steps.release.outputs.tag }}
run: |
printf '%s' "$RELEASE_NOTES" > "$RUNNER_TEMP/release_notes.md"
gh release create "$TAG" \
--verify-tag \
--title "$TAG" \
--notes-file "$RUNNER_TEMP/release_notes.md" \
dist/*

- name: Upload | Distribution Artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: steps.release.outputs.released == 'true'
with:
name: dist
path: dist/
if-no-files-found: error

outputs:
released: ${{ steps.release.outputs.released }}
released: ${{ steps.release.outputs.released || 'false' }}
version: ${{ steps.release.outputs.version }}

publish_to_pypi:
needs: release
if: needs.release.outputs.released == 'true'
# 1. Separate out the publish step from the github release step to run each step at
# the least amount of token privilege
# 2. Also, publishing can fail, and its better to have a separate job if you need to retry
# and it won't require reversing the release.
runs-on: ubuntu-latest
needs: release
if: github.ref_name == 'master' && needs.release.outputs.released == 'true'

permissions:
contents: read
id-token: write

steps:
- name: Download dist artifacts
- name: Setup | Download Build Artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
id: artifact-download
with:
name: dist
path: dist/
Expand Down
Loading