Add channel-aware release tooling and updater recovery
This commit is contained in:
83
scripts/set_version.py
Executable file
83
scripts/set_version.py
Executable file
@@ -0,0 +1,83 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Update the authoritative launcher version and its project metadata mirror."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
import sys
|
||||
|
||||
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
||||
if str(PROJECT_ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(PROJECT_ROOT))
|
||||
|
||||
from scripts.release_lib import ReleaseToolError, load_config # noqa: E402
|
||||
from ptlinux.update_resolver import SemanticVersion
|
||||
|
||||
|
||||
SOURCE_PATTERN = re.compile(
|
||||
r'(?m)^__version__ = "[0-9]+\.[0-9]+\.[0-9]+(?:-(?:beta|rc)\.[0-9]+)?"$'
|
||||
)
|
||||
PROJECT_PATTERN = re.compile(
|
||||
r'(?m)^version = "[0-9]+\.[0-9]+\.[0-9]+(?:-(?:beta|rc)\.[0-9]+)?"$'
|
||||
)
|
||||
|
||||
|
||||
def replace_once(path: Path, pattern: re.Pattern[str], replacement: str) -> None:
|
||||
try:
|
||||
original = path.read_text(encoding="utf-8")
|
||||
except (OSError, UnicodeError) as exc:
|
||||
raise ReleaseToolError(f"cannot read version file {path}: {exc}") from exc
|
||||
updated, count = pattern.subn(replacement, original)
|
||||
if count != 1:
|
||||
raise ReleaseToolError(
|
||||
f"expected exactly one version declaration in {path}, found {count}"
|
||||
)
|
||||
if updated == original:
|
||||
return
|
||||
temporary = path.with_name(f".{path.name}.{os.getpid()}.tmp")
|
||||
try:
|
||||
with temporary.open("x", encoding="utf-8", newline="\n") as handle:
|
||||
handle.write(updated)
|
||||
handle.flush()
|
||||
os.fsync(handle.fileno())
|
||||
temporary.chmod(path.stat().st_mode & 0o777)
|
||||
os.replace(temporary, path)
|
||||
finally:
|
||||
temporary.unlink(missing_ok=True)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("version")
|
||||
arguments = parser.parse_args()
|
||||
try:
|
||||
SemanticVersion.parse(arguments.version)
|
||||
config = load_config()
|
||||
version_source = config.get("version_source")
|
||||
if not isinstance(version_source, str):
|
||||
raise ReleaseToolError("version_source must be configured")
|
||||
replace_once(
|
||||
PROJECT_ROOT / version_source,
|
||||
SOURCE_PATTERN,
|
||||
f'__version__ = "{arguments.version}"',
|
||||
)
|
||||
mirrors = config.get("version_mirrors")
|
||||
if not isinstance(mirrors, list) or not all(isinstance(item, str) for item in mirrors):
|
||||
raise ReleaseToolError("version_mirrors must be configured")
|
||||
for mirror in mirrors:
|
||||
replace_once(
|
||||
PROJECT_ROOT / mirror,
|
||||
PROJECT_PATTERN,
|
||||
f'version = "{arguments.version}"',
|
||||
)
|
||||
except (OSError, ValueError, ReleaseToolError) as exc:
|
||||
print(f"Version update failed: {exc}", file=sys.stderr)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user