1- # Release workflow: publishes to PyPI via OIDC trusted publishing.
1+ # Automated release: python-semantic-release analyzes the conventional
2+ # commits on main, and when a release is due it updates CHANGELOG.md,
3+ # commits, tags vX.Y.Z, pushes and creates the GitHub release. hatch-vcs
4+ # derives the package version from that tag, so no version file is bumped.
5+ # Publishing happens in this same run: pushes made with GITHUB_TOKEN do
6+ # not retrigger workflows, so a tag-triggered publish would never fire.
27#
3- # Filename and environment name are load-bearing: the trusted publisher
4- # configured on PyPI references workflow "on-release-main.yml" and
5- # environment "pypi" (no token/secret involved).
6- #
7- # Docs: dev builds are deployed from main by docs.yml; deploy-docs below
8- # publishes the versioned per-release docs (mike -> gh-pages branch).
8+ # Filename and environment name are load-bearing: the PyPI trusted
9+ # publisher (OIDC) references workflow "on-release-main.yml" and
10+ # environment "pypi".
911
1012name : release-main
1113
1214on :
1315 push :
14- tags : ['v[0-9]*']
16+ branches : [main]
17+ workflow_dispatch :
18+
19+ # One release run at a time: python-semantic-release pushes commits/tags
20+ # to main, so overlapping runs would race each other.
21+ concurrency :
22+ group : release
23+ cancel-in-progress : false
1524
1625jobs :
17- publish :
26+ release :
1827 runs-on : ubuntu-latest
1928 environment :
2029 name : pypi
2130 permissions :
22- contents : read
2331 # required for PyPI trusted publishing (OIDC)
2432 id-token : write
33+ # push the release commit/tag and create the GitHub release (PSR)
34+ contents : write
35+ outputs :
36+ released : ${{ steps.psr.outputs.released }}
37+ version : ${{ steps.psr.outputs.version }}
2538 steps :
26- - uses : actions/checkout@v4
39+ - name : Check out
40+ uses : actions/checkout@v4
2741 with :
28- # deep clone incl. tags so hatch-vcs derives the version from the tag
42+ # full history: PSR analyzes commits, hatch-vcs needs the tags
2943 fetch-depth : 0
44+ # latest main, not the triggering SHA: runs queued behind the
45+ # concurrency group must include release commits pushed by the
46+ # previous run, or their own push is rejected
47+ ref : main
3048
3149 - name : Set up the environment
3250 uses : ./.github/actions/setup-python-env
3351
52+ - name : Version, tag and changelog with python-semantic-release
53+ id : psr
54+ env :
55+ GH_TOKEN : ${{ github.token }}
56+ run : |
57+ git config user.name "github-actions[bot]"
58+ git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
59+ before=$(git rev-parse HEAD)
60+ uv run semantic-release version
61+ after=$(git rev-parse HEAD)
62+ if [ "$before" != "$after" ]; then
63+ ver=$(git tag --points-at HEAD | grep -E '^v[0-9]' | head -1)
64+ echo "released=true" >> "$GITHUB_OUTPUT"
65+ echo "version=${ver#v}" >> "$GITHUB_OUTPUT"
66+ echo "released $ver"
67+ else
68+ echo "released=" >> "$GITHUB_OUTPUT"
69+ echo "no release necessary"
70+ fi
71+
3472 - name : Build package distribution files
73+ if : steps.psr.outputs.released != ''
3574 run : uv build
3675
3776 - name : Check package metadata
77+ if : steps.psr.outputs.released != ''
3878 run : uvx twine check dist/*
3979
4080 - name : Publish package
81+ if : steps.psr.outputs.released != ''
4182 run : uv publish --trusted-publishing always
4283
4384 deploy-docs :
44- needs : publish
85+ needs : release
86+ if : needs.release.outputs.released != ''
4587 runs-on : ubuntu-latest
4688 permissions :
4789 # mike commits the built site to the gh-pages branch
@@ -52,23 +94,23 @@ jobs:
5294 steps :
5395 - uses : actions/checkout@v4
5496 with :
55- # deep clone incl. tags: hatch-vcs needs them and mike needs gh-pages
97+ # deep clone incl. the release commit and tags pushed by PSR
5698 fetch-depth : 0
99+ ref : main
57100
58101 - name : Set up the environment
59102 uses : ./.github/actions/setup-python-env
60103
61104 - name : Configure git identity
62105 run : |
63106 git config user.name 'github-actions[bot]'
64- git config user.email 'github-actions[bot]@users.noreply.github.com'
107+ git config user.email '41898282+ github-actions[bot]@users.noreply.github.com'
65108
66109 - name : Deploy versioned documentation
67110 env :
68- TAG : ${{ github.ref_name }}
111+ VERSION : ${{ needs.release.outputs.version }}
69112 run : |
70- ver="${TAG#v}"
71113 # --alias-type=copy keeps /latest/ a real directory instead of a
72114 # redirect, so machine-readable files stay fetchable there
73- uv run mike deploy --push --alias-type=copy --update-aliases "$ver " latest
115+ uv run mike deploy --push --alias-type=copy --update-aliases "$VERSION " latest
74116 uv run mike set-default --push latest
0 commit comments