Skip to content

Commit 125bc2e

Browse files
committed
Automate release process with workflow_dispatch
Add release.yml workflow that handles the full release: prepare changelog, commit, tag, build, test, publish to PyPI, create GitHub release, and start next development cycle. Move publish jobs out of test.yml (CI only tests now). Update RELEASING.rst with instructions to run the workflow.
1 parent 47d046f commit 125bc2e

File tree

8 files changed

+344
-172
lines changed

8 files changed

+344
-172
lines changed

.github/workflows/release.yml

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
name: Release
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
bump:
7+
description: "Version bump (ignored if version is set)"
8+
required: false
9+
type: choice
10+
default: minor
11+
options:
12+
- major
13+
- minor
14+
- micro
15+
- post
16+
version:
17+
description: "Exact version (e.g. 1.0rc1). Overrides bump."
18+
required: false
19+
20+
jobs:
21+
release:
22+
runs-on: ubuntu-latest
23+
environment: release
24+
permissions:
25+
contents: write
26+
id-token: write
27+
steps:
28+
- uses: actions/checkout@v6
29+
with:
30+
fetch-depth: 0
31+
32+
- name: Configure git
33+
run: |
34+
git config user.name "github-actions[bot]"
35+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
36+
37+
- name: Prepare changelog
38+
id: version
39+
run: |
40+
if [ -n "${{ github.event.inputs.version }}" ]; then
41+
version=$(python scripts/make_changelog.py "${{ github.event.inputs.version }}")
42+
else
43+
version=$(python scripts/make_changelog.py --${{ github.event.inputs.bump }})
44+
fi
45+
echo "version=$version" >> "$GITHUB_OUTPUT"
46+
echo "tag=v$version" >> "$GITHUB_OUTPUT"
47+
48+
- name: Commit and tag
49+
run: |
50+
git commit -am "Prepare release ${{ steps.version.outputs.version }}"
51+
git tag "${{ steps.version.outputs.tag }}"
52+
git push origin "${{ steps.version.outputs.tag }}"
53+
54+
- name: Build
55+
id: build
56+
uses: hynek/build-and-inspect-python-package@v2.14
57+
with:
58+
upload-name-suffix: -release
59+
60+
- name: Create GitHub Release
61+
env:
62+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
63+
run: |
64+
python scripts/extract_changelog.py "${{ steps.version.outputs.version }}" --format gfm > /tmp/release-notes.md
65+
gh release create "${{ steps.version.outputs.tag }}" \
66+
--notes-file /tmp/release-notes.md \
67+
${{ steps.build.outputs.dist }}/*
68+
69+
- name: Publish to PyPI
70+
uses: pypa/gh-action-pypi-publish@v1.13.0
71+
with:
72+
attestations: true
73+
packages-dir: ${{ steps.build.outputs.dist }}
74+
75+
- name: Start next development cycle
76+
run: |
77+
python scripts/make_changelog.py UNRELEASED
78+
git commit -am "Start next development cycle"
79+
git push origin main

.github/workflows/test.yml

Lines changed: 2 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,13 @@ jobs:
2828

2929
- name: Check changelog (tagged release)
3030
if: github.ref_type == 'tag'
31-
run: python check_changelog.py --tag "${{ github.ref_name }}"
31+
run: python scripts/check_changelog.py --tag "${{ github.ref_name }}"
3232

3333
- name: Check changelog (PR has new entries)
3434
if: github.event_name == 'pull_request'
3535
run: |
3636
git fetch origin main --depth=1
37-
python check_changelog.py
37+
python scripts/check_changelog.py
3838
3939
package:
4040
runs-on: ubuntu-latest
@@ -79,45 +79,3 @@ jobs:
7979
shell: bash
8080
run: |
8181
tox run -e py --installpkg `find dist/*.tar.gz`
82-
83-
pypi-publish:
84-
if: github.ref_type == 'tag'
85-
needs: [changelog, package, test]
86-
runs-on: ubuntu-latest
87-
environment: release
88-
permissions:
89-
id-token: write
90-
91-
steps:
92-
- name: Download Package
93-
uses: actions/download-artifact@v8
94-
with:
95-
name: Packages
96-
path: dist
97-
98-
- name: Publish to PyPI
99-
uses: pypa/gh-action-pypi-publish@v1.13.0
100-
with:
101-
attestations: true
102-
103-
github-release:
104-
if: github.ref_type == 'tag'
105-
needs: [pypi-publish]
106-
runs-on: ubuntu-latest
107-
permissions:
108-
contents: write
109-
110-
steps:
111-
- uses: actions/checkout@v6
112-
113-
- name: Download Package
114-
uses: actions/download-artifact@v8
115-
with:
116-
name: Packages
117-
path: dist
118-
119-
- name: Create GitHub Release
120-
env:
121-
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
122-
run: |
123-
gh release create "${{ github.ref_name }}" --generate-notes dist/*

MANIFEST.in

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
exclude .gitignore
2-
exclude make_changelog.py
3-
exclude check_changelog.py
42
exclude DEVELOPER.rst
53
exclude RELEASING.rst
64
exclude tox.ini
75
prune .github
6+
prune scripts

RELEASING.rst

Lines changed: 21 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -8,59 +8,39 @@ Versions are derived automatically from git tags.
88
.. _setuptools-scm: https://github.com/pypa/setuptools-scm
99

1010

11-
Version
12-
-------
11+
Relative bump
12+
-------------
1313

14-
``main`` should always be green and a potential release candidate.
15-
``unittest2pytest`` follows semantic versioning, so given that the
16-
current version is ``X.Y.Z``, to find the next version number one
17-
needs to look at the ``CHANGELOG.rst`` file:
14+
Run the **Release** workflow, selecting the version bump type:
1815

19-
- If there any new feature, then we must make a new **minor** release:
20-
next release will be ``X.Y+1.0``.
16+
.. code-block:: console
2117
22-
- Otherwise it is just a **bug fix** release: ``X.Y.Z+1``.
18+
gh workflow run release.yml -R pytest-dev/unittest2pytest --field bump=minor
2319
20+
Options: ``major``, ``minor``, ``micro``, ``post``. The version is
21+
computed automatically from the latest git tag.
2422

25-
Steps
26-
-----
2723

28-
To publish a new release ``X.Y.Z``, the steps are as follows:
24+
Absolute version
25+
----------------
2926

30-
#. Update ``CHANGELOG.rst``:
27+
To release a specific version (e.g. a release candidate):
3128

32-
.. code-block:: console
29+
.. code-block:: console
3330
34-
python make_changelog.py X.Y.Z
31+
gh workflow run release.yml -R pytest-dev/unittest2pytest --field version=1.0rc1
3532
36-
This replaces the ``UNRELEASED`` section with a dated ``X.Y.Z``
37-
section. Review the result and add any missing entries before
38-
committing.
3933
40-
#. Commit and push:
34+
What the workflow does
35+
----------------------
4136

42-
.. code-block:: console
43-
44-
git commit -am "Prepare release X.Y.Z"
45-
git push origin main
46-
47-
#. Tag and push:
48-
49-
.. code-block:: console
50-
51-
git tag -s vX.Y.Z -m "unittest2pytest X.Y.Z"
52-
git push origin vX.Y.Z
53-
54-
Pushing the tag triggers the CI workflow, which builds, tests,
55-
publishes to PyPI, and creates a GitHub release.
56-
57-
#. Start the next development cycle:
58-
59-
.. code-block:: console
60-
61-
python make_changelog.py UNRELEASED
62-
git commit -am "Start next development cycle"
63-
git push origin main
37+
#. Runs ``scripts/make_changelog.py`` to replace the ``UNRELEASED`` section
38+
with the version number and today's date.
39+
#. Commits, tags, and pushes.
40+
#. Builds the package.
41+
#. Creates a GitHub Release with the built artifacts.
42+
#. Publishes to PyPI (requires the ``release`` environment).
43+
#. Runs ``scripts/make_changelog.py UNRELEASED`` and pushes a follow-up commit.
6444

6545

6646
How versioning works

make_changelog.py

Lines changed: 0 additions & 84 deletions
This file was deleted.
Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,17 @@
1515
import sys
1616
from pathlib import Path
1717

18-
CHANGELOG = Path(__file__).resolve().parent / "CHANGELOG.rst"
18+
def _find_changelog() -> Path:
19+
d = Path(__file__).resolve().parent
20+
while d != d.parent:
21+
p = d / "CHANGELOG.rst"
22+
if p.exists():
23+
return p
24+
d = d.parent
25+
raise FileNotFoundError("CHANGELOG.rst not found")
26+
27+
28+
CHANGELOG = _find_changelog()
1929
REPO_ROOT = CHANGELOG.parent
2030

2131

0 commit comments

Comments
 (0)