-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathsync_release_version.py
More file actions
121 lines (93 loc) · 3.38 KB
/
Copy pathsync_release_version.py
File metadata and controls
121 lines (93 loc) · 3.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#!/usr/bin/env python3
"""Sync release version metadata from pyproject.toml.
This script treats pyproject.toml as the canonical source of truth and keeps
the duplicated version fields in sync.
"""
from __future__ import annotations
import argparse
import re
import sys
import tomllib
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parent.parent
PYPROJECT_PATH = REPO_ROOT / "pyproject.toml"
VERSION_PY_PATH = REPO_ROOT / "socket_basics" / "version.py"
INIT_PY_PATH = REPO_ROOT / "socket_basics" / "__init__.py"
ACTION_YML_PATH = REPO_ROOT / "action.yml"
def read_canonical_version() -> str:
data = tomllib.loads(PYPROJECT_PATH.read_text())
return data["project"]["version"]
def replace_first(pattern: str, replacement: str, content: str, path: Path) -> str:
updated, count = re.subn(pattern, replacement, content, count=1, flags=re.MULTILINE)
if count != 1:
raise ValueError(f"Could not update expected version field in {path}")
return updated
def build_expected_files(version: str) -> dict[Path, str]:
expected: dict[Path, str] = {}
version_py = VERSION_PY_PATH.read_text()
expected[VERSION_PY_PATH] = replace_first(
r'^__version__ = "[^"]+"$',
f'__version__ = "{version}"',
version_py,
VERSION_PY_PATH,
)
init_py = INIT_PY_PATH.read_text()
expected[INIT_PY_PATH] = replace_first(
r'^__version__ = "[^"]+"$',
f'__version__ = "{version}"',
init_py,
INIT_PY_PATH,
)
action_yml = ACTION_YML_PATH.read_text()
expected[ACTION_YML_PATH] = replace_first(
r'^( image: "docker://ghcr\.io/socketdev/socket-basics:)[^"]+(")$',
rf'\g<1>{version}\2',
action_yml,
ACTION_YML_PATH,
)
return expected
def check_files(expected: dict[Path, str]) -> list[str]:
mismatches: list[str] = []
for path, rendered in expected.items():
current = path.read_text()
if current != rendered:
mismatches.append(str(path.relative_to(REPO_ROOT)))
return mismatches
def write_files(expected: dict[Path, str]) -> None:
for path, rendered in expected.items():
path.write_text(rendered)
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="Sync socket-basics version metadata from pyproject.toml"
)
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument(
"--check",
action="store_true",
help="Fail if any derived version files differ from pyproject.toml",
)
group.add_argument(
"--write",
action="store_true",
help="Rewrite derived version files to match pyproject.toml",
)
return parser.parse_args()
def main() -> int:
args = parse_args()
version = read_canonical_version()
expected = build_expected_files(version)
if args.write:
write_files(expected)
print(f"Synchronized release version metadata to {version}")
return 0
mismatches = check_files(expected)
if mismatches:
print(f"Release version metadata is out of sync with pyproject.toml ({version}):")
for mismatch in mismatches:
print(f" - {mismatch}")
print("Run: python3 scripts/sync_release_version.py --write")
return 1
print(f"Release version metadata is in sync: {version}")
return 0
if __name__ == "__main__":
sys.exit(main())