Skip to content

Commit a1c1f84

Browse files
committed
Migrate to hatch-vcs for automated versioning
- Remove hardcoded version from pyproject.toml - Add hatch-vcs to build system requirements - Configure version derivation from git tags - Add RELEASE.md with comprehensive release process documentation - Update CHANGES.md to 1.4.0 (unreleased) - Add _version.py to .gitignore The package version is now automatically derived from git tags at build time, following the same pattern as mxdev.
1 parent 5eed546 commit a1c1f84

File tree

4 files changed

+267
-4
lines changed

4 files changed

+267
-4
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@ __pycache__
1313
/dist/
1414
/docs/html/
1515
/requirements-mxdev.txt
16+
src/mxmake/_version.py

CHANGES.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
# Changelog
22

3-
## 1.3.1 (unreleased)
3+
## 1.4.0 (unreleased)
44

5+
- Chore: Migrate to hatch-vcs for automated versioning from git tags.
56
- Fix: theme for newer Sphinx 7.x.
67
- Fix: interactive uv venv, use --allow-existing instead.
78
- Feature: Add support for Python 3.14.

RELEASE.md

Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
# mxmake Release Process Documentation
2+
3+
## Overview
4+
5+
The mxmake project uses **automated versioning via hatch-vcs**, which derives the version number from git tags during build time, eliminating manual version bumps in code.
6+
7+
## Prerequisites
8+
9+
- Commit access to the repository
10+
- PyPI publishing permissions (handled via GitHub Actions)
11+
- All tests passing on the main branch
12+
13+
## Step-by-Step Release Process
14+
15+
### 1. Prepare the Main Branch
16+
17+
Ensure you're on the main branch with all tests passing:
18+
19+
```bash
20+
git checkout main
21+
git pull origin main
22+
make test
23+
make check
24+
make typecheck
25+
git status # Should be clean
26+
```
27+
28+
### 2. Review Changes
29+
30+
Check what's changed since the last release:
31+
32+
```bash
33+
git log $(git describe --tags --abbrev=0)..HEAD --oneline
34+
```
35+
36+
### 3. Update CHANGES.md
37+
38+
Edit the changelog to:
39+
- Change "(unreleased)" to the release date (format: YYYY-MM-DD)
40+
- Add a new unreleased section for future changes
41+
- Maintain format: "## X.Y.Z (YYYY-MM-DD)"
42+
- Optionally include "[author]" at the end of each entry
43+
44+
Commit these changes:
45+
46+
```bash
47+
git add CHANGES.md
48+
git commit -m "Prepare release X.Y.Z"
49+
git push origin main
50+
```
51+
52+
### 4. Create a GitHub Release
53+
54+
1. Navigate to https://github.com/mxstack/mxmake/releases/new
55+
2. Click "Choose a tag" and type the version: `vX.Y.Z` (with the "v" prefix)
56+
3. Click "Create new tag: vX.Y.Z on publish"
57+
4. Set release title: `vX.Y.Z` or `Version X.Y.Z`
58+
5. Copy the relevant CHANGES.md section into the description
59+
6. Click "Publish release"
60+
61+
### 5. Monitor the Release
62+
63+
The GitHub Actions workflow automatically:
64+
- Runs tests across Python 3.9-3.14 on multiple operating systems
65+
- Runs linting, type checking, and variant tests
66+
- Builds the package with the version from the git tag
67+
- Publishes to PyPI if all tests pass
68+
69+
Monitor at: https://github.com/mxstack/mxmake/actions
70+
71+
### 6. Post-Release Verification
72+
73+
- Verify the package appears on PyPI: https://pypi.org/project/mxmake/
74+
- Check the version is correct
75+
- Optionally announce the release
76+
77+
## Version Numbering
78+
79+
**Format**: MAJOR.MINOR.PATCH (e.g., 1.4.0)
80+
81+
- **Git tags**: vMAJOR.MINOR.PATCH (e.g., v1.4.0)
82+
- **Package version**: MAJOR.MINOR.PATCH (v prefix automatically stripped)
83+
- The "v" prefix in tags is **required**
84+
85+
The project follows Semantic Versioning:
86+
- **MAJOR**: Breaking changes
87+
- **MINOR**: New features (backwards-compatible)
88+
- **PATCH**: Bug fixes (backwards-compatible)
89+
90+
## Development Versions
91+
92+
Between releases, development builds automatically receive versions like:
93+
94+
```
95+
1.4.0.dev3+g1234abc
96+
```
97+
98+
Where:
99+
- `1.4.0` = next release version from last tag
100+
- `dev3` = 3 commits since last tag
101+
- `g1234abc` = git commit hash
102+
103+
This happens automatically via hatch-vcs with no manual intervention needed.
104+
105+
## Emergency Hotfix Release
106+
107+
For urgent fixes to a released version:
108+
109+
1. Create a branch from the tag:
110+
```bash
111+
git checkout -b hotfix-1.4.1 v1.4.0
112+
```
113+
114+
2. Make and commit the fix:
115+
```bash
116+
git add .
117+
git commit -m "Fix critical bug"
118+
git push origin hotfix-1.4.1
119+
```
120+
121+
3. Create a pull request to main
122+
4. After merge, follow the normal release process with version `v1.4.1`
123+
124+
## Testing a Release (TestPyPI)
125+
126+
### Build Locally from a Tag
127+
128+
```bash
129+
git tag v1.4.0-rc1
130+
python -m build
131+
unzip -p dist/mxmake-*.whl mxmake/_version.py
132+
git tag -d v1.4.0-rc1
133+
```
134+
135+
### Upload to TestPyPI
136+
137+
```bash
138+
pip install twine
139+
twine upload --repository testpypi dist/*
140+
pip install --index-url https://test.pypi.org/simple/ mxmake
141+
```
142+
143+
## Release Checklist
144+
145+
- [ ] All tests passing on main branch
146+
- [ ] CHANGES.md updated with release date
147+
- [ ] New unreleased section added to CHANGES.md
148+
- [ ] Changes committed and pushed
149+
- [ ] GitHub release created with correct tag (vX.Y.Z format)
150+
- [ ] GitHub Actions workflow completed successfully
151+
- [ ] Package visible on PyPI with correct version
152+
- [ ] Release announced (if applicable)
153+
154+
## Troubleshooting
155+
156+
**Build fails with "version not found"**
157+
- Ensure you're in a git repository with tags fetched
158+
- Run: `git fetch --tags && python -m build`
159+
160+
**Version is wrong in built package**
161+
- Ensure clean checkout at the tagged commit
162+
- Run: `git checkout v1.4.0 && git status && python -m build`
163+
164+
**CI fails to publish to PyPI**
165+
- Check GitHub Actions workflow logs
166+
- Verify PyPI trusted publisher configuration
167+
- Contact repository maintainers
168+
169+
**README doesn't render correctly on PyPI**
170+
- Test locally: `python -m build && twine check dist/*`
171+
- Upload to TestPyPI first
172+
- Fix markdown formatting in relevant files
173+
174+
## Maintainer Notes
175+
176+
### PyPI Trusted Publisher Setup
177+
178+
Uses GitHub Actions OIDC for publishing (no API tokens needed):
179+
- Publisher: GitHub
180+
- Owner: mxstack
181+
- Repository: mxmake
182+
- Workflow: release.yml
183+
- Environment: release-pypi
184+
185+
### GitHub Release Environments
186+
187+
The workflow uses two environments:
188+
- **release-test-pypi**: Auto-publishes development versions to test.pypi.org on every main branch commit
189+
- **release-pypi**: Publishes official releases to pypi.org on GitHub release events
190+
191+
## Changelog Management
192+
193+
### Format
194+
195+
```markdown
196+
## Changes
197+
198+
## X.Y.Z (unreleased)
199+
200+
- Description of change
201+
202+
## X.Y.Z (YYYY-MM-DD)
203+
204+
- Description of change
205+
```
206+
207+
### Key Points
208+
209+
- Version in CHANGES.md is **manual** (you edit it)
210+
- Package version is **automatic** (from git tag via hatch-vcs)
211+
- Format: "## X.Y.Z (YYYY-MM-DD)" for released versions
212+
- Optionally add "[author]" at the end of each change entry
213+
214+
### Why This Works
215+
216+
With hatch-vcs, the package version is determined at build time from git tags. This means:
217+
- Maintain a human-readable changelog manually
218+
- Build system automatically gets the correct version
219+
- No need to sync version numbers across multiple files
220+
221+
## Manual Release (Not Recommended)
222+
223+
For emergency situations only:
224+
225+
```bash
226+
git checkout v1.4.0
227+
python -m build
228+
twine upload dist/*
229+
```
230+
231+
This bypasses CI checks and is not recommended for normal releases.
232+
233+
## Version Management Tools
234+
235+
### Check Current Version
236+
237+
```bash
238+
git describe --tags
239+
python -c "import mxmake; print(mxmake.__version__)"
240+
python -m build && unzip -p dist/mxmake-*.whl mxmake/_version.py
241+
```
242+
243+
### List All Releases
244+
245+
```bash
246+
git tag --list 'v*' --sort=-version:refname | head
247+
pip index versions mxmake
248+
```
249+
250+
## Further Reading
251+
252+
- [Semantic Versioning](https://semver.org/)
253+
- [hatch-vcs Documentation](https://github.com/ofek/hatch-vcs)
254+
- [GitHub Releases](https://docs.github.com/en/repositories/releasing-projects-on-github)
255+
- [PyPI Trusted Publishers](https://docs.pypi.org/trusted-publishers/)
256+
- [Python Packaging Guide](https://packaging.python.org/)

pyproject.toml

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
[project]
22
name = "mxmake"
33
description = "Generates a Python project-specific Makefile by using an extensible library of configurable Makefile snippets."
4-
version = "1.4.0"
54
keywords = ["development", "deployment", "make"]
65
authors = [
76
{name = "MX Stack Developers", email = "dev@bluedynamics.com" }
@@ -28,7 +27,13 @@ dependencies = [
2827
"mxdev>=4.0.2",
2928
"pyyaml"
3029
]
31-
dynamic = ["readme"]
30+
dynamic = ["readme", "version"]
31+
32+
[tool.hatch.version]
33+
source = "vcs"
34+
35+
[tool.hatch.build.hooks.vcs]
36+
version-file = "src/mxmake/_version.py"
3237

3338
[tool.hatch.metadata.hooks.fancy-pypi-readme]
3439
content-type = "text/markdown"
@@ -74,7 +79,7 @@ applications = "mxmake.topics:applications"
7479
i18n = "mxmake.topics:i18n"
7580

7681
[build-system]
77-
requires = ["hatchling", "hatch-fancy-pypi-readme"]
82+
requires = ["hatchling", "hatch-vcs", "hatch-fancy-pypi-readme"]
7883
build-backend = "hatchling.build"
7984

8085
[tool.pytest.ini_options]

0 commit comments

Comments
 (0)