This document describes the release process for mxdev.
mxdev uses automated versioning from git tags via hatch-vcs. The version number is automatically derived from git tags during build time, eliminating the need for manual version bumps in code.
- Commit access to the repository
- PyPI publishing permissions (handled via GitHub Actions)
- All tests passing on main branch
# Make sure you're on main and up to date
git checkout main
git pull origin main
# Verify all tests pass
uvx --with tox-uv tox
# Check current status
git status # Should be clean# See what's changed since last tag
git log $(git describe --tags --abbrev=0)..HEAD --oneline
# Or view in GitHub
# https://github.com/mxstack/mxdev/compare/v4.1.0...mainEdit CHANGES.md to finalize the release notes:
Before release:
## 4.1.1 (unreleased)
- Modernize release method with hatchling. See RELEASE.md [jensens]
- Modernize tox setup. [jensens]
- Modernize Github workflows. [jensens]After editing (change unreleased to release date and add new unreleased section):
## Changes
## 4.1.2 (unreleased)
<!-- Add future changes here -->
## 4.1.1 (2025-10-20)
- Modernize release method with hatchling. See RELEASE.md [jensens]
- Modernize tox setup. [jensens]
- Modernize Github workflows. [jensens]Important notes:
- The version number in CHANGES.md is manual (you edit it)
- The package version comes automatically from the git tag via hatch-vcs
- Keep the format:
## X.Y.Z (YYYY-MM-DD)for released versions - Add
[author]at the end of each change entry
Commit the changes:
git add CHANGES.md
git commit -m "Prepare release 4.1.1"
git push origin main- Go to https://github.com/mxstack/mxdev/releases/new
- Click "Choose a tag" and type the new version:
v4.2.0(withvprefix!) - Click "Create new tag: v4.2.0 on publish"
- Set release title:
v4.2.0orVersion 4.2.0 - Copy relevant section from CHANGES.md into release description
- Click "Publish release"
The GitHub Actions workflow will automatically:
- Run all tests across Python 3.8-3.12 on Ubuntu, Windows, macOS
- Build the package with version
4.2.0(from the tag) - Publish to PyPI if tests pass
- Watch the GitHub Actions workflow: https://github.com/mxstack/mxdev/actions
- Verify tests pass (usually ~5-10 minutes)
- Check PyPI once published: https://pypi.org/project/mxdev/
After the release is published on PyPI, you may want to:
-
Update CHANGES.md to add a new unreleased section for future changes (if not done in step 3):
# Edit CHANGES.md to add: # ## X.Y.Z (unreleased) # # <!-- Add future changes here --> git add CHANGES.md git commit -m "Start development of next version" git push origin main
-
Announce the release (optional):
- Post on relevant mailing lists or forums
- Update documentation if needed
- Notify users of significant changes
mxdev follows Semantic Versioning:
- MAJOR.MINOR.PATCH (e.g.,
4.2.1) - MAJOR: Breaking changes, incompatible API changes
- MINOR: New features, backwards-compatible
- PATCH: Bug fixes, backwards-compatible
- Git tags:
vMAJOR.MINOR.PATCH(e.g.,v4.2.0) - Package version:
MAJOR.MINOR.PATCH(e.g.,4.2.0)
The v prefix in tags is required and automatically stripped by hatch-vcs.
Between releases, development builds automatically get versions like:
4.2.0.dev3+g1234abc
Where:
4.2.0= Next release version (from last tag)dev3= 3 commits since last tagg1234abc= Git commit hash
This happens automatically via hatch-vcs - no manual intervention needed.
For urgent fixes to a released version:
-
Create a branch from the tag:
git checkout -b hotfix-4.2.1 v4.2.0
-
Make and commit the fix:
# Make changes git add . git commit -m "Fix critical bug X" git push origin hotfix-4.2.1
-
Create pull request to main
-
After merge, follow normal release process with version
v4.2.1
To test the release process without publishing to production PyPI:
# Create a test tag
git tag v4.2.0-rc1
# Build the package
python -m build
# Check the version in built artifacts
unzip -p dist/mxdev-*.whl mxdev/_version.py
# Should show: __version__ = "4.2.0rc1"
# Clean up test tag
git tag -d v4.2.0-rc1# Install twine if needed
pip install twine
# Upload to TestPyPI
twine upload --repository testpypi dist/*
# Test installation from TestPyPI
pip install --index-url https://test.pypi.org/simple/ mxdevUse this checklist for each release:
- All tests passing on main branch
- CHANGES.md updated with release date (changed from
unreleased) - New unreleased section added to CHANGES.md (for next version)
- Changes committed and pushed to main
- GitHub release created with correct tag (format:
vX.Y.Z) - GitHub Actions workflow completed successfully
- Package visible on PyPI with correct version
- Release announced (if applicable)
Cause: Building outside of a git repository or without tags.
Solution: Ensure you're in a git checkout with tags fetched:
git fetch --tags
python -m buildCause: Uncommitted changes or wrong tag checked out.
Solution: Ensure clean checkout at the tagged commit:
git checkout v4.2.0
git status # Should show "HEAD detached at v4.2.0"
python -m buildCause: Usually authentication issues or PyPI permissions.
Solution:
- Check GitHub Actions workflow logs
- Verify PyPI trusted publisher configuration
- Contact repository maintainers
Cause: Markdown formatting issue or missing files.
Solution:
- Test locally:
python -m build && twine check dist/* - Upload to TestPyPI first
- Fix formatting in README.md, CONTRIBUTING.md, CHANGES.md, or LICENSE.md
This project uses GitHub Actions OIDC for PyPI publishing (no API tokens needed).
Configuration in PyPI:
- Publisher: GitHub
- Owner: mxstack
- Repository: mxdev
- Workflow: release.yaml
- Environment: release
The release environment in GitHub requires:
- Approval from maintainers (optional, can be configured)
- Runs only on release events
mxdev uses a manual changelog approach where version numbers and dates in CHANGES.md are updated by hand.
The changelog follows this simple format:
## Changes
## X.Y.Z (unreleased)
- Description of change [author]
## X.Y.Z (YYYY-MM-DD)
- Description of change [author]
- Another change [author]- Version in CHANGES.md: Manual - you edit the version number and date
- Package version: Automatic - comes from git tag via hatch-vcs
- During development: Changes are added under
(unreleased) - Before release: Change
(unreleased)to the actual date - After release: Add new
(unreleased)section for next version
With hatch-vcs, the package version is determined by git tags at build time. This means:
- You maintain a human-readable changelog in CHANGES.md
- The build system automatically gets the correct version from tags
- No need to keep version numbers in sync between files
For larger teams or to avoid merge conflicts, consider using:
- Scriv: Fragment-based changelog management
- Towncrier: Popular in the Python ecosystem
- git-cliff: Generate from commit messages
See the project's issue tracker or maintainers if you'd like to adopt automated changelog tools.
For emergency situations where GitHub Actions is unavailable:
# 1. Checkout the tag
git checkout v4.2.0
# 2. Build
python -m build
# 3. Upload (requires PyPI credentials)
twine upload dist/*Note: This bypasses CI checks and is not recommended for normal releases.
# From git tags
git describe --tags
# From installed package
python -c "import mxdev; print(mxdev.__version__)"
# From built package
python -m build
unzip -p dist/mxdev-*.whl mxdev/_version.py# Git tags
git tag --list 'v*' --sort=-version:refname | head
# PyPI releases
pip index versions mxdev