"""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 # Enable UTF-8 output on Windows if sys.platform == "win32": import io sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8") sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8") # 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())