Release Process¶
This document describes GraphForge's professional versioning and release process.
Table of Contents¶
- Versioning Strategy
- Release Types
- Release Checklist
- Creating a Release
- Post-Release Tasks
- Hotfix Process
- Pre-release Versions
Versioning Strategy¶
GraphForge follows Semantic Versioning 2.0.0:
MAJOR.MINOR.PATCH
Example: 1.2.3
│ │ └─── PATCH: Backwards-compatible bug fixes
│ └───── MINOR: New features, backwards-compatible
└─────── MAJOR: Breaking changes
Version Increments¶
MAJOR version (1.0.0 → 2.0.0) when: - Breaking API changes - Incompatible changes to data formats - Removal of deprecated features - Changes requiring user migration
Examples:
- Changing column naming from col_0 to column_0
- Removing deprecated methods
- Changing SQLite schema in incompatible way
MINOR version (1.2.0 → 1.3.0) when: - New features (backwards-compatible) - New Cypher clauses (OPTIONAL MATCH, UNWIND) - Performance improvements - Deprecations (with backward compatibility)
Examples: - Adding OPTIONAL MATCH support - Adding variable-length paths - New aggregation functions - Enhanced error messages
PATCH version (1.2.3 → 1.2.4) when: - Bug fixes (backwards-compatible) - Documentation updates - Internal refactoring with no API changes - Security patches
Examples: - Fixing column naming regression - Fixing SKIP/LIMIT empty results - Documentation typos - Dependency security updates
Pre-1.0.0 Versions¶
GraphForge is currently in 0.x.x (pre-1.0.0): - 0.x.0: Minor features, may include breaking changes - 0.x.y: Bug fixes and patches - Breaking changes are allowed before 1.0.0
Version 1.0.0 will be released when: - API is stable and battle-tested - Core openCypher features complete - TCK compliance > 50% - Production-ready with documented migration path
Release Types¶
Regular Release (Minor/Patch)¶
Timeline: Every 2-4 weeks
Criteria: - All tests passing - No critical bugs - Documentation updated - CHANGELOG.md updated
Major Release¶
Timeline: When breaking changes are necessary
Additional criteria: - Migration guide written - Deprecation warnings in previous release - Community notification (if applicable) - Extended testing period
Hotfix Release¶
Timeline: As soon as possible
Criteria: - Critical bug or security issue - Fixes only, no new features - Fast-tracked review process
Release Checklist¶
Pre-Release (1-2 days before)¶
- All planned features merged to
main - All CI checks passing
- All tests passing (351 unit + integration tests)
- Code coverage ≥ 81% (current baseline)
- No open critical bugs
- Dependencies up to date
- Security scan clean (bandit)
Documentation¶
- Update
CHANGELOG.mdwith all changes since last release - Update version in
pyproject.toml - Update version references in docs if changed
- Update README.md if major changes
- Update
docs/project-status-and-roadmap.md - Review all documentation for accuracy
Testing¶
- Run full test suite locally:
pytest tests/ - Test on Python 3.10, 3.11, 3.12, 3.13
- Test on Ubuntu, macOS, Windows (if possible)
- Test installation from built package:
uv build && uv tool install dist/graphforge-*.whl - Manual smoke tests with examples
- Test upgrade path from previous version (if applicable)
Release¶
- Create release branch:
release/vX.Y.Z - Update version in
pyproject.toml - Update
CHANGELOG.md: Move [Unreleased] to [X.Y.Z] - Commit:
chore(release): bump version to X.Y.Z - Push release branch
- Create PR to
main - Get PR approval
- Merge to
main - Create Git tag:
git tag -a vX.Y.Z -m "Release version X.Y.Z" - Push tag:
git push origin vX.Y.Z - Create GitHub Release with release notes
- Verify PyPI publish workflow succeeds
- Verify package on PyPI: https://pypi.org/project/graphforge/
Post-Release¶
- Announce release (if applicable)
- Close milestone (if using milestones)
- Create next milestone
- Update project boards
- Tweet/blog post (if major release)
Creating a Release¶
Step-by-Step Guide¶
1. Prepare Release Branch¶
# Ensure main is up to date
git checkout main
git pull origin main
# Create release branch
git checkout -b release/v0.2.0
2. Update Version¶
Edit pyproject.toml:
3. Update CHANGELOG.md¶
Move unreleased changes to new version section:
## [Unreleased]
<!-- Empty or new features in development -->
## [0.2.0] - 2026-02-15
### Added
- Variable-length path support (`[*1..3]`)
- OPTIONAL MATCH for left outer joins
### Changed
- Improved error messages with error codes
### Fixed
- Edge case in aggregation handling
[Unreleased]: https://github.com/DecisionNerd/graphforge/compare/v0.2.0...HEAD
[0.2.0]: https://github.com/DecisionNerd/graphforge/compare/v0.1.1...v0.2.0
4. Commit and Push¶
# Commit changes
git add pyproject.toml CHANGELOG.md
git commit -m "chore(release): bump version to 0.2.0"
# Push release branch
git push -u origin release/v0.2.0
5. Create and Merge PR¶
# Create PR
gh pr create --title "Release v0.2.0" --body "Release version 0.2.0
See CHANGELOG.md for details."
# Wait for CI to pass and get approval
# Merge (squash not recommended for release PRs)
gh pr merge --merge
6. Create Git Tag¶
# Switch to main and pull
git checkout main
git pull origin main
# Create annotated tag
git tag -a v0.2.0 -m "Release version 0.2.0
## What's Changed
- Variable-length path support
- OPTIONAL MATCH clause
- Improved error messages
See CHANGELOG.md for full details."
# Push tag
git push origin v0.2.0
7. Create GitHub Release¶
# Create release using GitHub CLI
gh release create v0.2.0 \
--title "GraphForge v0.2.0" \
--notes-file <(cat <<'EOF'
## What's New in v0.2.0
### 🚀 New Features
- **Variable-Length Paths**: Query transitive relationships with `[*1..3]` syntax
- **OPTIONAL MATCH**: Left outer join semantics for optional patterns
### 🔧 Improvements
- Enhanced error messages with error codes
- Better TCK compliance (now at 25%)
### 🐛 Bug Fixes
- Fixed edge case in aggregation handling
### 📦 Installation
```bash
pip install graphforge==0.2.0
# or
uv add graphforge==0.2.0
📖 Documentation¶
See CHANGELOG.md for complete details.
🙏 Contributors¶
Thanks to all contributors who made this release possible! EOF )
Or use the GitHub web interface:
1. Go to https://github.com/DecisionNerd/graphforge/releases
2. Click "Draft a new release"
3. Choose tag: `v0.2.0`
4. Release title: `GraphForge v0.2.0`
5. Copy release notes from CHANGELOG
6. Check "Set as the latest release"
7. Click "Publish release"
#### 8. Verify Publication
```bash
# Wait for publish workflow to complete
gh run list --workflow=publish.yaml --limit 1
# Check PyPI
open https://pypi.org/project/graphforge/
# Test installation
pip install graphforge==0.2.0
Post-Release Tasks¶
1. Verify Package¶
# Install in fresh environment
uv venv test-env
source test-env/bin/activate
uv pip install graphforge==0.2.0
# Run quick test
python -c "from graphforge import GraphForge; print(GraphForge.__name__)"
2. Update Documentation Sites¶
If you have external docs: - Update version in docs deployment - Regenerate API documentation - Update "latest" links
3. Announce Release¶
GitHub Discussions:
# GraphForge v0.2.0 Released! 🎉
We're excited to announce the release of GraphForge v0.2.0!
## Highlights
- Variable-length path queries
- OPTIONAL MATCH support
- Improved error handling
Full details: https://github.com/DecisionNerd/graphforge/releases/tag/v0.2.0
Twitter/Social Media (if applicable):
🎉 GraphForge v0.2.0 is out!
✨ Variable-length paths: [*1..3]
🔍 OPTIONAL MATCH support
🐛 Bug fixes and improvements
📦 pip install graphforge==0.2.0
#Python #GraphDatabase #OpenCypher
Hotfix Process¶
For critical bugs in production release:
1. Create Hotfix Branch¶
# Branch from the release tag
git checkout -b hotfix/v0.2.1 v0.2.0
# Or from main if it's already ahead
git checkout -b hotfix/v0.2.1 main
2. Fix the Bug¶
3. Update Version and CHANGELOG¶
# Update pyproject.toml to 0.2.1
# Update CHANGELOG.md with hotfix details
git commit -m "chore(release): bump version to 0.2.1"
4. Fast-Track Review¶
# Create PR with "hotfix" label
gh pr create --title "Hotfix v0.2.1: Fix critical bug" \
--label hotfix
# Get expedited review and merge
gh pr merge --merge
5. Release Immediately¶
# Create tag and release
git tag -a v0.2.1 -m "Hotfix release v0.2.1"
git push origin v0.2.1
gh release create v0.2.1 \
--title "GraphForge v0.2.1 (Hotfix)" \
--notes "## Critical Bug Fix
Fixed critical bug in query execution that caused...
This is a hotfix release. All users on v0.2.0 should upgrade immediately."
Pre-release Versions¶
For testing before official release:
Alpha Releases¶
Purpose: Early testing, unstable
Version format: X.Y.Z-alpha.N (e.g., 0.3.0-alpha.1)
# Update version in pyproject.toml
version = "0.3.0-alpha.1"
# Create tag
git tag -a v0.3.0-alpha.1 -m "Alpha release for testing"
git push origin v0.3.0-alpha.1
# Create pre-release on GitHub (check "This is a pre-release")
gh release create v0.3.0-alpha.1 --prerelease \
--title "GraphForge v0.3.0-alpha.1" \
--notes "⚠️ Alpha release for testing only. Not for production use."
Beta Releases¶
Purpose: Feature complete, needs testing
Version format: X.Y.Z-beta.N (e.g., 0.3.0-beta.1)
Release Candidates¶
Purpose: Final testing before release
Version format: X.Y.Z-rc.N (e.g., 0.3.0-rc.1)
Automation Tools¶
Version Bumping Script¶
Create scripts/bump-version.py:
#!/usr/bin/env python3
"""Bump version in pyproject.toml"""
import sys
import re
from pathlib import Path
def bump_version(bump_type):
pyproject = Path("pyproject.toml")
content = pyproject.read_text()
# Find current version
match = re.search(r'version = "(\d+)\.(\d+)\.(\d+)"', content)
if not match:
print("Error: Could not find version in pyproject.toml")
sys.exit(1)
major, minor, patch = map(int, match.groups())
# Bump version
if bump_type == "major":
major += 1
minor = 0
patch = 0
elif bump_type == "minor":
minor += 1
patch = 0
elif bump_type == "patch":
patch += 1
else:
print(f"Error: Invalid bump type '{bump_type}'")
sys.exit(1)
new_version = f"{major}.{minor}.{patch}"
# Replace version
new_content = re.sub(
r'version = "\d+\.\d+\.\d+"',
f'version = "{new_version}"',
content
)
pyproject.write_text(new_content)
print(f"Version bumped to {new_version}")
return new_version
if __name__ == "__main__":
if len(sys.argv) != 2:
print("Usage: bump-version.py [major|minor|patch]")
sys.exit(1)
bump_version(sys.argv[1])
Usage:
python scripts/bump-version.py patch # 0.1.1 → 0.1.2
python scripts/bump-version.py minor # 0.1.1 → 0.2.0
python scripts/bump-version.py major # 0.1.1 → 1.0.0
Best Practices¶
DO ✅¶
- Follow semantic versioning strictly
- Update CHANGELOG.md for every release
- Test thoroughly before releasing
- Create annotated git tags with release notes
- Use GitHub Releases for visibility
- Announce breaking changes in advance
- Keep release notes user-focused
- Test package installation after publish
DON'T ❌¶
- Skip version numbers (0.1.1 → 0.1.3)
- Make breaking changes in patch releases
- Release without updating CHANGELOG
- Push to PyPI without testing
- Create release tags from non-main branches (except hotfixes)
- Reuse or delete version tags
- Release on Fridays (avoid weekend emergencies)
- Rush releases without proper testing
Troubleshooting¶
Release Failed to Publish to PyPI¶
# Check workflow logs
gh run view --log
# Manually publish if needed (requires PyPI token)
uv build
uv publish
Wrong Version Published¶
Cannot unpublish from PyPI! You can only: 1. Yank the release (marks it as unsuitable) 2. Publish a new patch version with fixes
Tag Already Exists¶
# Delete local tag
git tag -d v0.2.0
# Delete remote tag (careful!)
git push origin :refs/tags/v0.2.0
# Create correct tag
git tag -a v0.2.0 -m "Release version 0.2.0"
git push origin v0.2.0
Quick Reference¶
Check Current Version¶
List All Releases¶
View Release Details¶
Compare Versions¶
Related Documentation¶
Last Updated: 2026-02-01