This document describes how to release a new version of easylimit to PyPI.
Releases are fully automated using GitHub Actions with PyPI Trusted Publishing. When you push a version tag, GitHub Actions will:
- Run all tests on Python 3.8-3.13
- Build the package
- Publish to PyPI (no token needed - uses Trusted Publishing)
- Create a GitHub Release with changelog
This needs to be done once before your first release:
- Go to https://pypi.org/manage/account/publishing/
- Click "Add a new pending publisher"
- Fill in:
- PyPI Project Name:
easylimit(this can be a project that doesn't exist yet) - Owner:
man8 - Repository name:
easylimit - Workflow name:
release.yml - Environment name:
pypi
- PyPI Project Name:
- Click "Add"
That's it! No API tokens needed. PyPI will trust releases from this specific GitHub Actions workflow.
Ensure you have:
- Pre-commit hooks installed (
pre-commit install) - All tests passing locally (
uv run pytest) - Clean working directory (
git status)
- All feature work is committed and pushed to
main - All tests pass (
uv run pytest) - Code quality checks pass (
uv run ruff check . && uv run ruff format --check .) - Type checking passes (
uv run mypy src/) - CI is green on
mainbranch
-
Update version number in
src/easylimit/__init__.py:__version__ = "0.3.3" # Update this
-
Update CHANGELOG.md following Keep a Changelog format:
- Move items from
[Unreleased]to a new version section - Use the current date in YYYY-MM-DD format
- Organize changes into: Added, Changed, Deprecated, Removed, Fixed, Security
Example:
## [Unreleased] ## [0.3.3] - 2025-11-07 ### Added - New feature description ### Fixed - Bug fix description
- Move items from
-
Commit the version bump:
git add src/easylimit/__init__.py CHANGELOG.md git commit -m "chore: bump version to 0.3.3" git push origin main
-
Create a version tag:
git tag v0.3.3 -m "Release version 0.3.3" -
Push the tag (this triggers the release workflow):
git push origin v0.3.3
-
Watch GitHub Actions:
- Go to https://github.com/man8/easylimit/actions
- Find the "Release" workflow run for your tag
- Monitor the progress:
- ✅ Test: Runs all tests on all Python versions
- ✅ Build: Builds the package
- ✅ Publish to PyPI: Publishes using Trusted Publishing
- ✅ Create GitHub Release: Creates release with changelog
-
Verify the release:
- Check PyPI: https://pypi.org/project/easylimit/
- Check GitHub Releases: https://github.com/man8/easylimit/releases
- Test installation:
pip install easylimit==0.3.3
- Announce the release (optional):
- Tweet/post about it
- Update any dependent projects
- Close related issues with a comment linking to the release
Symptom: Tests fail in GitHub Actions but pass locally
Solution:
- Check the test logs in GitHub Actions
- Fix the failing tests
- Push the fix to
main - Delete the tag:
git tag -d v0.3.3 && git push origin :refs/tags/v0.3.3 - Recreate the tag from the fixed commit
Symptom: "Upload failed" or "Authentication failed" during PyPI publish step
Solution:
- Verify Trusted Publishing is configured correctly on PyPI
- Check that the workflow name matches exactly:
release.yml - Check that the environment name matches exactly:
pypi - Ensure
id-token: writepermission is set in the workflow
Symptom: Published the wrong version number to PyPI
Solution:
- You cannot delete versions from PyPI (by design)
- You can "yank" the version to hide it from installers:
- Go to https://pypi.org/project/easylimit/
- Manage → Releases → Select version → Yank
- Publish a new corrected version (e.g., 0.3.4)
Symptom: Found a bug after releasing
Solution:
- Fix the bug on
main - Bump to next patch version (e.g., 0.3.3 → 0.3.4)
- Update CHANGELOG.md with the fix
- Follow normal release process
We follow Semantic Versioning:
- MAJOR version (1.0.0): Incompatible API changes
- MINOR version (0.3.0): New functionality, backwards-compatible
- PATCH version (0.3.3): Backwards-compatible bug fixes
Examples:
- New feature added: 0.3.2 → 0.4.0
- Bug fix: 0.3.2 → 0.3.3
- Breaking change: 0.3.2 → 1.0.0
If GitHub Actions is down or you need to release manually:
-
Build the package:
uv build
-
Publish to PyPI (requires PyPI API token):
uv publish
Or using twine:
pip install twine twine upload dist/* -
Create GitHub Release manually:
- Go to https://github.com/man8/easylimit/releases/new
- Select your tag
- Copy the changelog section
- Publish
Note: Manual releases should be avoided. They bypass automated testing and are error-prone.
Questions about the release process? Open an issue or contact opensource@man8.com.