feat: Implement centralized version management and sync process

This commit is contained in:
claudi 2026-01-30 09:16:12 +01:00
parent c1133ae8e9
commit 0d9464854d
7 changed files with 523 additions and 45 deletions

152
scripts/sync_version.py Normal file
View file

@ -0,0 +1,152 @@
"""Sync version from __init__.py to changelog.
This script reads the version from src/webdrop_bridge/__init__.py and
updates the CHANGELOG.md. Config and pyproject.toml automatically read
from __init__.py, so no manual sync needed for those files.
This script uses shared version utilities (build/scripts/version_utils.py)
to ensure consistent version reading across all build scripts.
Usage:
python scripts/sync_version.py [--version VERSION]
Examples:
python scripts/sync_version.py # Use version from __init__.py
python scripts/sync_version.py --version 2.0.0 # Override with new version
"""
import argparse
import re
import sys
from datetime import datetime
from pathlib import Path
# Import shared version utilities
sys.path.insert(0, str(Path(__file__).parent.parent / "build" / "scripts"))
from version_utils import get_current_version, get_project_root
PROJECT_ROOT = get_project_root()
def get_current_version_from_init() -> str:
"""Get version from __init__.py using shared utility.
Returns:
Current version string from __init__.py
Raises:
ValueError: If __version__ cannot be found
"""
return get_current_version()
def update_init_version(version: str) -> None:
"""Update version in __init__.py.
Args:
version: New version string to set
"""
init_file = PROJECT_ROOT / "src/webdrop_bridge/__init__.py"
content = init_file.read_text()
new_content = re.sub(
r'__version__\s*=\s*["\'][^"\']+["\']',
f'__version__ = "{version}"',
content,
)
init_file.write_text(new_content)
print(f"✓ Updated src/webdrop_bridge/__init__.py to {version}")
def update_env_example(version: str) -> None:
"""Update APP_VERSION in .env.example (optional).
Note: config.py now reads from __init__.py by default.
Only update if .env.example explicitly sets APP_VERSION for testing.
Args:
version: New version string to set
"""
env_file = PROJECT_ROOT / ".env.example"
if env_file.exists():
content = env_file.read_text()
# Only update if APP_VERSION is explicitly set
if 'APP_VERSION=' in content:
new_content = re.sub(
r'APP_VERSION=[^\n]+',
f'APP_VERSION={version}',
content,
)
env_file.write_text(new_content)
print(f"✓ Updated .env.example to {version}")
else:
print(
f" .env.example does not override APP_VERSION "
f"(uses __init__.py)"
)
def update_changelog(version: str) -> None:
"""Add version header to CHANGELOG.md if not present.
Args:
version: New version string to add
"""
changelog = PROJECT_ROOT / "CHANGELOG.md"
if changelog.exists():
content = changelog.read_text()
if f"## [{version}]" not in content and f"## {version}" not in content:
date_str = datetime.now().strftime("%Y-%m-%d")
header = (
f"## [{version}] - {date_str}\n\n"
"### Added\n\n### Changed\n\n### Fixed\n\n"
)
new_content = header + content
changelog.write_text(new_content)
print(f"✓ Added version header to CHANGELOG.md for {version}")
def main() -> int:
"""Sync version across project.
Updates __init__.py (source of truth) and changelog.
Config and pyproject.toml automatically read from __init__.py.
Returns:
0 on success, 1 on error
"""
parser = argparse.ArgumentParser(
description="Sync version from __init__.py to dependent files"
)
parser.add_argument(
"--version",
type=str,
help="Version to set (if not provided, reads from __init__.py)",
)
args = parser.parse_args()
try:
if args.version:
if not re.match(r"^\d+\.\d+\.\d+", args.version):
print(
"❌ Invalid version format. Use semantic versioning"
" (e.g., 1.2.3)"
)
return 1
version = args.version
update_init_version(version)
else:
version = get_current_version_from_init()
print(f"📍 Current version from __init__.py: {version}")
update_env_example(version)
update_changelog(version)
print(f"\n✅ Version sync complete: {version}")
return 0
except Exception as e:
print(f"❌ Error: {e}")
return 1
if __name__ == "__main__":
sys.exit(main())