This guide provides step-by-step instructions for releasing a new version of django-forms-workflows to PyPI.
Before releasing, ensure you have:
- ✅ All changes committed and pushed to the main branch
- ✅ All tests passing in CI
- ✅ Documentation updated
- ✅ CHANGELOG.md updated with all changes
- ✅ PyPI API token configured in GitHub Secrets (see
.github/PYPI_SETUP.md)
Update the version in all of these files:
-
django_forms_workflows/__init__.py -
setup.py -
pyproject.toml
Example for version 0.2.0:
# django_forms_workflows/__init__.py
__version__ = '0.2.0'# setup.py
setup(
name='django-forms-workflows',
version='0.2.0',
...
)# pyproject.toml
[tool.poetry]
name = "django-forms-workflows"
version = "0.2.0"- Move changes from
[Unreleased]to a new version section - Add release date
- Update version comparison links at the bottom
Example:
## [0.2.0] - 2025-11-01
### Added
- Configurable prefill sources with PrefillSource model
- Post-submission actions for updating external systems
- Database, LDAP, and API handlers
### Enhanced
- Comprehensive documentation
- Farm demo with examples
[0.2.0]: https://github.com/opensensor/django-forms-workflows/compare/v0.1.0...v0.2.0- Review and update README.md
- Ensure all new features are documented
- Update any version-specific references
- Check all links work
# Clean previous builds
rm -rf dist/ build/ *.egg-info
# Build with poetry
poetry build
# Or build with setuptools
python -m build
# Verify the build
ls -lh dist/You should see:
django_forms_workflows-X.Y.Z-py3-none-any.whldjango_forms_workflows-X.Y.Z.tar.gz
# Create a test virtual environment
python -m venv test_env
source test_env/bin/activate # On Windows: test_env\Scripts\activate
# Install the built package
pip install dist/django_forms_workflows-X.Y.Z-py3-none-any.whl
# Test import
python -c "import django_forms_workflows; print(django_forms_workflows.__version__)"
# Deactivate and clean up
deactivate
rm -rf test_env# Add all version changes
git add django_forms_workflows/__init__.py setup.py pyproject.toml CHANGELOG.md
# Commit with a clear message
git commit -m "Bump version to 0.2.0"
# Push to main
git push origin main# Create an annotated tag
git tag -a v0.2.0 -m "Release version 0.2.0
Major changes:
- Configurable prefill sources
- Post-submission actions
- Enhanced documentation
"
# Verify the tag
git tag -l -n9 v0.2.0
# Push the tag to GitHub
git push origin v0.2.0- Go to https://github.com/opensensor/django-forms-workflows/actions
- Find the "Publish to PyPI" workflow run
- Monitor the progress
- Verify all steps complete successfully:
- ✅ Checkout code
- ✅ Set up Python
- ✅ Verify version matches tag
- ✅ Build distribution packages
- ✅ Check distribution packages
- ✅ Publish to PyPI
- ✅ Create GitHub Release
- Check PyPI: https://pypi.org/project/django-forms-workflows/
- Verify the new version is listed
- Check the package metadata
- Test installation:
pip install django-forms-workflows==0.2.0- Go to https://github.com/opensensor/django-forms-workflows/releases
- Verify the new release is created
- Check the release notes
- Verify distribution files are attached
- Update project documentation site (if applicable)
- Post announcement on social media (if applicable)
- Notify users via mailing list (if applicable)
- Update any dependent projects
Tag format: v0.2.0, v1.0.0, etc.
Publishes to: PyPI + GitHub Release
git tag -a v0.2.0 -m "Release version 0.2.0"
git push origin v0.2.0Tag format: v0.2.0-rc1, v1.0.0-rc2, etc.
Publishes to: Test PyPI only
git tag -a v0.2.0-rc1 -m "Release candidate 0.2.0-rc1"
git push origin v0.2.0-rc1Tag format: v0.2.0-beta1, v1.0.0-beta2, etc.
Publishes to: Test PyPI only
git tag -a v0.2.0-beta1 -m "Beta release 0.2.0-beta1"
git push origin v0.2.0-beta1Follow Semantic Versioning:
Format: MAJOR.MINOR.PATCH
- MAJOR (1.0.0) - Incompatible API changes
- MINOR (0.2.0) - New features, backward compatible
- PATCH (0.2.1) - Bug fixes, backward compatible
Examples:
0.1.0→0.2.0- Added new features (prefill sources, post-submission actions)0.2.0→0.2.1- Bug fix in database handler0.2.1→0.3.0- Added REST API support0.9.0→1.0.0- First stable release, removed deprecated features
If you need to rollback a release:
# Delete local tag
git tag -d v0.2.0
# Delete remote tag
git push origin :refs/tags/v0.2.0- Go to https://github.com/opensensor/django-forms-workflows/releases
- Find the release
- Click "Delete"
Important: You cannot delete or overwrite a version on PyPI once published.
Options:
- Yank the release - Mark it as unavailable (users can still install with
==version) - Publish a patch - Release a new version (e.g., 0.2.1) with fixes
To yank a release on PyPI:
- Log in to https://pypi.org
- Go to the project page
- Click "Manage" → "Releases"
- Find the version and click "Options" → "Yank"
Error: "Tag version does not match package version"
Solution:
- Verify all version files are updated
- Ensure versions match exactly (no extra spaces, etc.)
- Delete the tag and recreate after fixing
Error: Build fails during GitHub Actions
Solution:
- Check the error logs in GitHub Actions
- Test the build locally:
poetry build - Fix any issues and push changes
- Delete and recreate the tag
Error: "Invalid credentials" or "Package already exists"
Solution:
- Verify
PYPI_API_TOKENsecret is correct - Check if version already exists on PyPI
- Increment version if needed
Error: Files missing from the built package
Solution:
- Check
MANIFEST.inincludes the files - Check
pyproject.tomlincludes the files - Rebuild and verify:
tar -tzf dist/*.tar.gz
After a successful release:
- Update the
[Unreleased]section in CHANGELOG.md for future changes - Create a new milestone for the next version (if using GitHub milestones)
- Close the current milestone
- Update project roadmap
- Monitor for bug reports
- Respond to user feedback
- Test Thoroughly - Always test locally before releasing
- Use Release Candidates - Test with Test PyPI first for major releases
- Document Everything - Keep CHANGELOG.md up to date
- Semantic Versioning - Follow semver strictly
- Communicate - Announce breaking changes clearly
- Monitor - Watch for issues after release
- Respond Quickly - Fix critical bugs with patch releases
# Patch release (0.2.0 → 0.2.1)
# Update versions in files, then:
git commit -am "Bump version to 0.2.1"
git tag -a v0.2.1 -m "Release version 0.2.1"
git push origin main v0.2.1
# Minor release (0.2.1 → 0.3.0)
# Update versions in files, then:
git commit -am "Bump version to 0.3.0"
git tag -a v0.3.0 -m "Release version 0.3.0"
git push origin main v0.3.0
# Major release (0.3.0 → 1.0.0)
# Update versions in files, then:
git commit -am "Bump version to 1.0.0"
git tag -a v1.0.0 -m "Release version 1.0.0"
git push origin main v1.0.0# For critical bugs in production
# 1. Create hotfix branch from tag
git checkout -b hotfix/0.2.1 v0.2.0
# 2. Fix the bug and update version
# ... make changes ...
git commit -am "Fix critical bug"
# 3. Update version to 0.2.1
# ... update version files ...
git commit -am "Bump version to 0.2.1"
# 4. Merge to main
git checkout main
git merge hotfix/0.2.1
# 5. Tag and push
git tag -a v0.2.1 -m "Hotfix release 0.2.1"
git push origin main v0.2.1
# 6. Delete hotfix branch
git branch -d hotfix/0.2.1