Skip to content

Release Process

This document describes GraphForge's professional versioning and release process.

Table of Contents


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.md with 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:

[project]
name = "graphforge"
version = "0.2.0"  # ← Update this

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

# Make minimal changes to fix the issue
git commit -m "fix: critical bug in query execution"

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)

version = "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)

version = "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

# On PyPI web interface, click "Yank" for the bad version
# Then release a new version

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

grep 'version =' pyproject.toml

List All Releases

gh release list
git tag -l

View Release Details

gh release view v0.1.1
git show v0.1.1

Compare Versions

git diff v0.1.0..v0.1.1
gh compare v0.1.0...v0.1.1


Last Updated: 2026-02-01