webdrop-bridge/VERSIONING_SIMPLIFIED.md

3.8 KiB

Simplified Versioning System

Problem Solved

Previously, the application version had to be manually updated in multiple places:

  1. src/webdrop_bridge/__init__.py - source of truth
  2. pyproject.toml - package version
  3. .env.example - environment example
  4. Run scripts/sync_version.py - manual sync step

This was error-prone and tedious.

Solution: Single Source of Truth

The version is now defined only in one place:

# src/webdrop_bridge/__init__.py
__version__ = "1.0.0"

All other components automatically read from this single source.

How It Works

1. pyproject.toml (Automatic)

[tool.setuptools.dynamic]
version = {attr = "webdrop_bridge.__version__"}

[project]
name = "webdrop-bridge"
dynamic = ["version"]  # Reads from __init__.py

When you build the package, setuptools automatically extracts the version from __init__.py.

2. config.py (Automatic - with ENV override)

# Lazy import to avoid circular imports
if not os.getenv("APP_VERSION"):
    from webdrop_bridge import __version__
    app_version = __version__
else:
    app_version = os.getenv("APP_VERSION")

The config automatically reads from __init__.py, but can be overridden with the APP_VERSION environment variable if needed.

3. sync_version.py (Simplified)

The script now only handles:

  • Updating __init__.py with a new version
  • Updating CHANGELOG.md with a new version header
  • Optional: updating .env.example if it explicitly sets APP_VERSION

It no longer needs to manually sync pyproject.toml or config defaults.

Workflow

To Release a New Version

Option 1: Simple (Recommended)

# Edit only one file
# src/webdrop_bridge/__init__.py:
__version__ = "1.1.0"  # Change this

# Then run sync script to update changelog
python scripts/sync_version.py

Option 2: Using the Sync Script

python scripts/sync_version.py --version 1.1.0

The script will:

  • Update __init__.py
  • Update CHANGELOG.md
  • (Optional) Update .env.example if it has APP_VERSION=

What Happens Automatically

When you run your application:

  1. Config loads and checks environment for APP_VERSION
  2. If not set, it imports __version__ from __init__.py
  3. The version is displayed in the UI
  4. Update checks use the correct version

When you build with pip install:

  1. setuptools reads __version__ from __init__.py
  2. Package metadata is set automatically
  3. No manual sync needed

Verification

To verify the version is correctly propagated:

# Check __init__.py
python -c "from webdrop_bridge import __version__; print(__version__)"

# Check config loading
python -c "from webdrop_bridge.config import Config; c = Config.from_env(); print(c.app_version)"

# Check package metadata (after building)
pip show webdrop-bridge

All should show the same version.

Best Practices

  1. Always edit __init__.py first - it's the single source of truth
  2. Run sync_version.py to update changelog - keeps release notes organized
  3. Use environment variables only for testing - don't hardcode overrides
  4. Run tests after version changes - config tests verify version loading

Migration Notes

If you had other places where version was defined:

  • Remove version from pyproject.toml [project] section
  • Add dynamic = ["version"] instead
  • Don't manually edit .env.example for version
  • Let sync_version.py handle it
  • Don't hardcode version in config.py defaults
  • Use lazy import from __init__.py

Testing the System

Run the config tests to verify everything works:

pytest tests/unit/test_config.py -v

All tests should pass, confirming version loading works correctly.


Result: One place to change, multiple places automatically updated. Simple, clean, professional.