AgentEvalTool/scripts/sync_version.py

96 lines
2.9 KiB
Python
Executable File

#!/usr/bin/env python3
"""Synchronize frontend package versions with pyproject.toml.
pyproject.toml is the single source of truth for the project version.
Run this script before building the frontend or deploying so the two
files never drift apart.
Usage:
scripts/sync_version.py # update package.json and package-lock.json
scripts/sync_version.py --check # exit 1 if they already differ
"""
from __future__ import annotations
import argparse
import json
import re
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
PYPROJECT = ROOT / "pyproject.toml"
PACKAGE_JSON = ROOT / "frontend" / "web" / "package.json"
PACKAGE_LOCK = ROOT / "frontend" / "web" / "package-lock.json"
_VERSION_RE = re.compile(r'^version\s*=\s*"([^"]+)"', re.MULTILINE)
def read_pyproject_version() -> str:
text = PYPROJECT.read_text(encoding="utf-8")
match = _VERSION_RE.search(text)
if not match:
raise RuntimeError(f"could not find version= in {PYPROJECT}")
return match.group(1)
def package_versions_match(version: str) -> bool:
package_data = json.loads(PACKAGE_JSON.read_text(encoding="utf-8"))
lock_data = json.loads(PACKAGE_LOCK.read_text(encoding="utf-8"))
return (
package_data.get("version") == version
and lock_data.get("version") == version
and lock_data.get("packages", {}).get("", {}).get("version") == version
)
def write_package_versions(version: str) -> bool:
"""Update package.json and package-lock.json. Returns True if changed."""
if package_versions_match(version):
return False
data = json.loads(PACKAGE_JSON.read_text(encoding="utf-8"))
data["version"] = version
PACKAGE_JSON.write_text(
json.dumps(data, indent=2, ensure_ascii=False) + "\n",
encoding="utf-8",
)
lock_data = json.loads(PACKAGE_LOCK.read_text(encoding="utf-8"))
lock_data["version"] = version
lock_data.setdefault("packages", {}).setdefault("", {})["version"] = version
PACKAGE_LOCK.write_text(
json.dumps(lock_data, indent=2, ensure_ascii=False) + "\n",
encoding="utf-8",
)
return True
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"--check", action="store_true",
help="Exit with status 1 if versions already match (CI-friendly).",
)
args = parser.parse_args()
version = read_pyproject_version()
if args.check:
if not package_versions_match(version):
print(f"OUT_OF_SYNC pyproject={version} frontend package versions differ")
return 1
print(f"OK version={version}")
return 0
changed = write_package_versions(version)
if changed:
print(f"synced frontend package versions -> {version}")
else:
print(f"already in sync: {version}")
return 0
if __name__ == "__main__":
sys.exit(main())