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
100 lines
3.1 KiB
Python
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."
|
|
)
|