webdrop-bridge/build/scripts/version_utils.py
claudi 1054266d0e
Some checks are pending
Tests & Quality Checks / Test on Python 3.11 (push) Waiting to run
Tests & Quality Checks / Test on Python 3.12 (push) Waiting to run
Tests & Quality Checks / Test on Python 3.11-1 (push) Waiting to run
Tests & Quality Checks / Test on Python 3.12-1 (push) Waiting to run
Tests & Quality Checks / Test on Python 3.10 (push) Waiting to run
Tests & Quality Checks / Test on Python 3.11-2 (push) Waiting to run
Tests & Quality Checks / Test on Python 3.12-2 (push) Waiting to run
Tests & Quality Checks / Build Artifacts (push) Blocked by required conditions
Tests & Quality Checks / Build Artifacts-1 (push) Blocked by required conditions
feat: Update version to 0.9.1, enhance release notes generation, and add changelog
2026-04-15 17:00:28 +02:00

100 lines
3.1 KiB
Python

"""Shared version management utilities for build scripts.
This module provides a single source of truth for version reading
to avoid duplication between different build scripts.
"""
import re
from pathlib import Path
def get_project_root() -> Path:
"""Get the project root directory.
Returns:
Path to project root (parent of build/scripts)
"""
return Path(__file__).parent.parent.parent
def get_current_version() -> str:
"""Read version from __init__.py.
This is the single source of truth for version information.
All build scripts and version management tools use this function.
Returns:
Current version string from __init__.py
Raises:
ValueError: If __version__ cannot be found in __init__.py
"""
project_root = get_project_root()
init_file = project_root / "src" / "webdrop_bridge" / "__init__.py"
if not init_file.exists():
raise FileNotFoundError(f"Cannot find __init__.py at {init_file}")
content = init_file.read_text(encoding="utf-8")
match = re.search(r'__version__\s*=\s*["\']([^"\']+)["\']', content)
if not match:
raise ValueError(
f"Could not find __version__ in {init_file}. " 'Expected: __version__ = "X.Y.Z"'
)
return match.group(1)
def extract_release_notes(changelog_content: str, version: str) -> str | None:
"""Extract the notes for a specific version from changelog content.
Args:
changelog_content: Full text of CHANGELOG.md
version: Version to extract, e.g. "0.9.1"
Returns:
The section content for that version, or None if not found.
"""
version_header = re.compile(
rf"^##\s*\[?{re.escape(version)}\]?(?:\s*-\s*.+)?\s*$",
re.MULTILINE,
)
match = version_header.search(changelog_content)
if not match:
return None
section_start = match.end()
next_header = re.search(r"^##\s+", changelog_content[section_start:], re.MULTILINE)
section_end = section_start + next_header.start() if next_header else len(changelog_content)
section = changelog_content[section_start:section_end].strip()
return section or None
def get_release_notes(version: str, project_root: Path | None = None) -> str:
"""Build a readable release body for publishing.
Prefers the matching version section from CHANGELOG.md. If no changelog
entry exists yet, falls back to a generic but user-facing description.
Args:
version: Release version string.
project_root: Optional project root override for testing.
Returns:
Release notes text suitable for Forgejo/GitHub release bodies.
"""
root = project_root or get_project_root()
changelog_file = root / "CHANGELOG.md"
if changelog_file.exists():
content = changelog_file.read_text(encoding="utf-8")
notes = extract_release_notes(content, version)
if notes:
return f"## WebDrop Bridge v{version}\n\n{notes}"
return (
f"## WebDrop Bridge v{version}\n\n"
"This release package contains the latest improvements, fixes, "
"and installer updates for this version."
)