Keeping versions aligned across setup.py
, pyproject.toml
, and GitHub tags is critical for maintaining a healthy Python project. It prevents mismatches, enables CI/CD automation, and ensures seamless releases.
In this guide, you’ll learn best practices for versioning Python packages and syncing metadata with GitHub release tags using bump-my-version
, GitHub Actions, and automation scripts.
Table of Contents
- Why Versioning is Crucial
- The Right Way to Define Your Version
- Aligning Versions with GitHub Tags
- Keeping Dependencies in Sync
- Using
bump-my-version
for Automated Versioning - Validating Versions in CI/CD
- Mistakes to Avoid
- Final Checklist
- FAQs
Why Versioning is Crucial
Imagine deploying a package and realizing later that the version in setup.py
differs from pyproject.toml
. 🤦️ This breaks automation, confuses users, and complicates debugging.
When you keep versions in sync, you:
- Ensure smooth CI/CD deployments
- Reduce version conflicts in dependencies
- Automate and streamline release workflows
Following best practices prevents the dreaded “version mismatch” error and keeps your project organized.
The Right Way to Define Your Version
A common pitfall is defining the version in multiple places. Instead, define it in a single source of truth.
1️⃣ Store Version in __version__.py
Create a __version__.py
file inside your package:
<span># my_package/__version__.py </span><span>__version__</span> <span>=</span> <span>"</span><span>1.2.3</span><span>"</span><span># my_package/__version__.py </span><span>__version__</span> <span>=</span> <span>"</span><span>1.2.3</span><span>"</span># my_package/__version__.py __version__ = "1.2.3"
Enter fullscreen mode Exit fullscreen mode
2️⃣ Use It in setup.py
Instead of manually entering a version, import it dynamically:
<span>from</span> <span>my_package.__version__</span> <span>import</span> <span>__version__</span><span>setup</span><span>(</span><span>name</span><span>=</span><span>"</span><span>my_package</span><span>"</span><span>,</span><span>version</span><span>=</span><span>__version__</span><span>,</span><span>...</span><span>)</span><span>from</span> <span>my_package.__version__</span> <span>import</span> <span>__version__</span> <span>setup</span><span>(</span> <span>name</span><span>=</span><span>"</span><span>my_package</span><span>"</span><span>,</span> <span>version</span><span>=</span><span>__version__</span><span>,</span> <span>...</span> <span>)</span>from my_package.__version__ import __version__ setup( name="my_package", version=__version__, ... )
Enter fullscreen mode Exit fullscreen mode
3️⃣ Sync with pyproject.toml
(for Poetry)
If you’re using Poetry, manually update pyproject.toml
:
<span>[tool.poetry]</span><span>name</span> <span>=</span> <span>"my-package"</span><span>version</span> <span>=</span> <span>"1.2.3"</span><span>[tool.poetry]</span> <span>name</span> <span>=</span> <span>"my-package"</span> <span>version</span> <span>=</span> <span>"1.2.3"</span>[tool.poetry] name = "my-package" version = "1.2.3"
Enter fullscreen mode Exit fullscreen mode
Poetry does not support dynamic version imports—so keeping this updated manually (or via automation) is necessary.
Aligning Versions with GitHub Tags
To ensure GitHub releases match your code, follow this release process:
- Update the version in
__version__.py
andpyproject.toml
. - Commit the change:
git commit <span>-am</span> <span>"Release version 1.2.3"</span>git commit <span>-am</span> <span>"Release version 1.2.3"</span>git commit -am "Release version 1.2.3"
Enter fullscreen mode Exit fullscreen mode
- Create a tag matching the version:
git tag v1.2.3git push origin main <span>--tags</span>git tag v1.2.3 git push origin main <span>--tags</span>git tag v1.2.3 git push origin main --tags
Enter fullscreen mode Exit fullscreen mode
- Ensure the tag and package version match before deploying.
If the tag and package version don’t match, CI/CD should catch the issue and stop the release.
Keeping Dependencies in Sync
Beyond versioning, managing dependencies properly prevents unexpected failures.
Lock Dependencies in requirements.txt
For reproducible builds, lock dependencies:
pip freeze <span>></span> requirements.txtpip freeze <span>></span> requirements.txtpip freeze > requirements.txt
Enter fullscreen mode Exit fullscreen mode
Separate Dev Dependencies
Use a separate file for development dependencies:
pip <span>install</span> <span>-r</span> requirements-dev.txtpip <span>install</span> <span>-r</span> requirements-dev.txtpip install -r requirements-dev.txt
Enter fullscreen mode Exit fullscreen mode
Alternatively, if using Poetry, do:
poetry add pytest <span>--dev</span>poetry add pytest <span>--dev</span>poetry add pytest --dev
Enter fullscreen mode Exit fullscreen mode
This ensures production installs don’t pull unnecessary dev dependencies.
Using bump-my-version
for Automated Versioning
What is bump-my-version
?
bump-my-version
is the modern replacement for bump2version
(which is no longer maintained).
It updates version numbers across all necessary files (e.g., __version__.py
, setup.py
, pyproject.toml
).
How to Install It
pip <span>install </span>bump-my-versionpip <span>install </span>bump-my-versionpip install bump-my-version
Enter fullscreen mode Exit fullscreen mode
How to Use It
Increment version numbers automatically:
bump-my-version patch <span># Updates 1.2.3 → 1.2.4</span>bump-my-version minor <span># Updates 1.2.3 → 1.3.0</span>bump-my-version major <span># Updates 1.2.3 → 2.0.0</span>bump-my-version patch <span># Updates 1.2.3 → 1.2.4</span> bump-my-version minor <span># Updates 1.2.3 → 1.3.0</span> bump-my-version major <span># Updates 1.2.3 → 2.0.0</span>bump-my-version patch # Updates 1.2.3 → 1.2.4 bump-my-version minor # Updates 1.2.3 → 1.3.0 bump-my-version major # Updates 1.2.3 → 2.0.0
Enter fullscreen mode Exit fullscreen mode
This ensures versioning consistency, preventing human errors in updates.
Validating Versions in CI/CD
To prevent mismatched versions between GitHub tags and your package metadata, add a validation step to GitHub Actions.
CI Workflow to Validate Versions
Create .github/workflows/version-check.yml
:
<span>name</span><span>:</span> <span>Version Check</span><span>on</span><span>:</span><span>push</span><span>:</span><span>tags</span><span>:</span><span>-</span> <span>'</span><span>v*'</span> <span># Runs only on version tags</span><span>jobs</span><span>:</span><span>check-version</span><span>:</span><span>runs-on</span><span>:</span> <span>ubuntu-latest</span><span>steps</span><span>:</span><span>-</span> <span>uses</span><span>:</span> <span>actions/checkout@v3</span><span>-</span> <span>name</span><span>:</span> <span>Validate package version consistency</span><span>run</span><span>:</span> <span>|</span><span>TAG_VERSION=${GITHUB_REF#refs/tags/v}</span><span>PACKAGE_VERSION=$(python -c "import my_package.__version__ as v; print(v.__version__)")</span><span>if [ "$TAG_VERSION" != "$PACKAGE_VERSION" ]; then</span><span>echo "Version mismatch! GitHub tag is $TAG_VERSION but package version is $PACKAGE_VERSION."</span><span>exit 1</span><span>fi</span><span>name</span><span>:</span> <span>Version Check</span> <span>on</span><span>:</span> <span>push</span><span>:</span> <span>tags</span><span>:</span> <span>-</span> <span>'</span><span>v*'</span> <span># Runs only on version tags</span> <span>jobs</span><span>:</span> <span>check-version</span><span>:</span> <span>runs-on</span><span>:</span> <span>ubuntu-latest</span> <span>steps</span><span>:</span> <span>-</span> <span>uses</span><span>:</span> <span>actions/checkout@v3</span> <span>-</span> <span>name</span><span>:</span> <span>Validate package version consistency</span> <span>run</span><span>:</span> <span>|</span> <span>TAG_VERSION=${GITHUB_REF#refs/tags/v}</span> <span>PACKAGE_VERSION=$(python -c "import my_package.__version__ as v; print(v.__version__)")</span> <span>if [ "$TAG_VERSION" != "$PACKAGE_VERSION" ]; then</span> <span>echo "Version mismatch! GitHub tag is $TAG_VERSION but package version is $PACKAGE_VERSION."</span> <span>exit 1</span> <span>fi</span>name: Version Check on: push: tags: - 'v*' # Runs only on version tags jobs: check-version: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Validate package version consistency run: | TAG_VERSION=${GITHUB_REF#refs/tags/v} PACKAGE_VERSION=$(python -c "import my_package.__version__ as v; print(v.__version__)") if [ "$TAG_VERSION" != "$PACKAGE_VERSION" ]; then echo "Version mismatch! GitHub tag is $TAG_VERSION but package version is $PACKAGE_VERSION." exit 1 fi
Enter fullscreen mode Exit fullscreen mode
If the versions don’t match, the pipeline will fail, preventing a broken release.
Mistakes to Avoid
Hardcoding Versions in Multiple Files – Instead, use __version__.py
Pushing GitHub Tags Without Updating Files First
Ignoring Dependency Locking (requirements.txt
)
Manual Version Updates Instead of Automation (bump-my-version
)
Avoid these, and your releases will be smooth!
Final Checklist
Keep version centralized in __version__.py
Always sync pyproject.toml
(for Poetry users)
Automate with bump-my-version
Validate version consistency in CI/CD
Lock dependencies for reliable builds
FAQs
1. Why is bump2version
no longer recommended?
bump2version
is no longer maintained. bump-my-version
is the modern alternative with active support.
2. How do I ensure my GitHub release matches my package version?
Use GitHub Actions to verify that the Git tag matches __version__.py
before releasing.
3. Should I use setup.py
or Poetry?
- If you use setuptools, update
setup.py
- If you use Poetry, manually update
pyproject.toml
4. Do I still need requirements.txt
if using Poetry?
No! Poetry manages dependencies internally, so requirements.txt
is unnecessary.
5. Is bump-my-version
required?
No, but it automates versioning, preventing human mistakes.
Conclusion
Keeping your Python packaging metadata in sync with GitHub release tags prevents deployment issues, enables automation, and ensures smooth releases.
By following best practices like centralized versioning, GitHub Actions validation, and automated version bumps, you’ll create a robust, foolproof versioning system!
Want to take it a step further? Integrate this workflow into your CI/CD pipeline today!
原文链接:How to Keep Your Python Package Metadata in Sync with GitHub Release Tags
暂无评论内容