1
1
# HOW THIS WORKFLOW WORKS
2
- # This workflow automates creating a GitHub Release and publishing your Python package to PyPI.
2
+ # This workflow automates creating a GitHub Release and publishing the Python package to PyPI.
3
3
4
4
# --- Manual Steps ---
5
5
# 1. Edit `pyproject.toml` to set the new version (e.g., `version = "1.2.3"` or `version = "1.2.4-rc1"` for pre-releases).
8
8
# 4. Use the justfile: `just release` to trigger the process.
9
9
10
10
# --- Automation ---
11
- # The workflow then automatically:
12
11
# - runs .github/scripts/tag_release.py to create a new tag based on the version in `pyproject.toml`.
13
- # - Pushes the new tag to github which triggers this workflow.
12
+ # - Pushes the new tag to github which triggers this workflow file .
14
13
# - Checks that the tag matches the version in `pyproject.toml`.
15
14
# - Builds the sdist and wheel.
16
- # - Publishes the package to PyPI.
17
- # - Creates a new GitHub Release based on the tag with logic for marking pre-releases.
15
+ # - Publishes the package to PyPI using trusted publishing.
16
+ # - Reads the release notes from your CHANGELOG.md.
17
+ # - Creates a new GitHub Release, marking it as a pre-release if necessary.
18
+
18
19
19
20
name : Create Release and Publish to PyPI
20
21
21
22
on :
22
23
push :
23
24
tags :
24
- - " v*" # Runs on any tag starting with "v", e.g., v1.2.3
25
+ - " v*" # Runs on any tag starting with "v", e.g., v1.2.3 or v1.2.3-rc1
25
26
workflow_dispatch :
26
27
inputs :
27
28
tag_name :
28
- description : ' Tag name to create release for (e.g., v1.2.3)'
29
+ description : ' Tag to create release for (e.g., v1.2.3). Must start with "v". '
29
30
required : true
30
31
type : string
31
32
32
33
jobs :
33
34
build-and-publish :
34
35
name : Build and Publish
35
36
runs-on : ubuntu-latest
36
- # These permissions are required for the actions below.
37
37
permissions :
38
38
id-token : write # Required for Trusted Publishing with PyPI (OIDC).
39
39
contents : write # Required to create the GitHub Release.
40
40
41
41
steps :
42
+ # If manually triggered, checkout the specific tag. Otherwise, it checks out the tag that triggered the workflow.
43
+ # Fetch all history for all tags so the changelog-reader can find previous tags.
42
44
- name : Check out code
43
45
uses : actions/checkout@v4
44
46
with :
45
- # If manually triggered, checkout the specific tag
46
- ref : ${{ github.event_name == 'workflow_dispatch' && inputs.tag_name || github.ref }}
47
+ ref : ${{ github.event.inputs.tag_name || github.ref }}
48
+ fetch-depth : 0
47
49
48
50
- name : Set up Python
49
51
uses : actions/setup-python@v5
@@ -53,51 +55,45 @@ jobs:
53
55
- name : Install required python packages
54
56
run : python -m pip install --upgrade build tomli
55
57
56
- - name : Set tag name
57
- id : tag
58
- # github.ref_name is the name of the tag that triggered this workflow,
59
- # if it was triggered by a tag push and not manually.
60
- # inputs.tag_name is used when the workflow is manually triggered.
58
+ # Use github.ref_name which reliably gives the tag name (e.g., "v1.2.3")
59
+ # Create a step output named 'version' that contains the tag name without the 'v'
60
+ - name : Verify tag matches pyproject.toml version
61
61
run : |
62
- if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
63
- TAG_NAME="${{ inputs.tag_name }}"
64
- else
65
- TAG_NAME="${{ github.ref_name }}"
66
- fi
67
- echo "tag_name=$TAG_NAME" >> $GITHUB_OUTPUT
68
- echo "Using tag: $TAG_NAME"
69
-
70
- - name : Verify tag matches version
71
- run : |
72
- TAG_VERSION=${{ steps.tag.outputs.tag_name }}
62
+ TAG_NAME="${{ github.ref_name }}"
73
63
PYPROJECT_VERSION=$(python -c "import tomli; print(tomli.load(open('pyproject.toml', 'rb'))['project']['version'])")
74
- if [ "v$PYPROJECT_VERSION" != "$TAG_VERSION" ]; then
75
- echo "Error: Tag $TAG_VERSION does not match pyproject.toml version v$PYPROJECT_VERSION"
64
+
65
+ if [ "v$PYPROJECT_VERSION" != "$TAG_NAME" ]; then
66
+ echo "Error: Tag '$TAG_NAME' does not match pyproject.toml version 'v$PYPROJECT_VERSION'"
76
67
exit 1
77
- fi
68
+ fi
69
+
70
+ echo "Tag and pyproject.toml version match: $TAG_NAME"
71
+ echo "version=${TAG_NAME#v}" >> $GITHUB_OUTPUT
78
72
79
73
- name : Build package
80
74
run : python -m build
81
75
82
76
- name : Publish to PyPI
83
- # This action uses Trusted Publishing, which is configured in your PyPI project settings.
84
- # It avoids the need for storing API tokens as secrets.
85
77
uses : pypa/gh-action-pypi-publish@release/v1
86
78
87
- - name : Extract changelog for release
88
- id : changelog
89
- run : |
90
- VERSION=${{ steps.tag.outputs.tag_name }}
91
- VERSION=${VERSION#v} # Strip leading "v"
92
- awk "/## \[${VERSION//./\\.}\]/,/^## \[/" CHANGELOG.md | head -n -1 > body.md
79
+ - name : Get Changelog Entry
80
+ id : changelog_reader
81
+ uses : mindsers/changelog-reader-action@v2
82
+ with :
83
+ validation_level : warn
84
+ version : ${{ steps.version_check.outputs.version }}
85
+ path : ./CHANGELOG.md
93
86
94
87
- name : Create GitHub Release
95
- uses : softprops/action-gh- release@v2
88
+ uses : ncipollo/ release-action@v1
96
89
with :
97
- tag_name : ${{ steps.tag.outputs.tag_name }}
98
- # The release title will be "Release vX.X.X" or "Pre-release vX.X.X-rc1"
99
- # depending on whether a hyphen is present in the tag name.
100
- release_name : ${{ startsWith(steps.tag.outputs.tag_name, 'v') && contains(steps.tag.outputs.tag_name, '-') && format('Pre-release {0}', steps.tag.outputs.tag_name) || format('Release {0}', steps.tag.outputs.tag_name) }}
101
- # Marks the release as a "pre-release" on GitHub if the tag contains a hyphen (e.g., "-rc1").
102
- prerelease : ${{ contains(steps.tag.outputs.tag_name, '-') }}
103
- body_path : body.md
90
+ # Use the tag name that triggered the workflow
91
+ tag : ${{ github.ref_name }}
92
+ # The release title will be, e.g., "Release v1.2.3"
93
+ name : Release ${{ github.ref_name }}
94
+ # The body of the release is the changelog entry from the previous step
95
+ body : ${{ steps.changelog_reader.outputs.changes }}
96
+ # Automatically mark as pre-release if the tag contains a hyphen (e.g., v1.2.3-rc1)
97
+ prerelease : ${{ contains(github.ref_name, '-') }}
98
+ # This allows the action to update a release if it already exists
99
+ allowUpdates : true
0 commit comments