-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathupdate_version.py
More file actions
executable file
·114 lines (92 loc) · 4.19 KB
/
Copy pathupdate_version.py
File metadata and controls
executable file
·114 lines (92 loc) · 4.19 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
#!/usr/bin/env python3
"""Sync app_version_conf.py's APP_VERSION with appdata.xml's version of
record, and optionally cut the release for it.
appdata.xml (export/flatpak/org.dupot.filebrowser.appdata.xml) is what
Flatpak/appstream actually show the user, and each release() bump belongs
there first — see org.dupot.filebrowser.appdata.xml's <releases>, newest
first. APP_VERSION in domain/conf/app_version_conf.py (shown in the
"About" dialog, via app_window.py) is a separate hardcoded copy that has
to be kept in sync by hand, and it's already drifted from appdata.xml
once. This script closes that loop: read the first (= latest) <release
version="..."> entry and write it into APP_VERSION, instead of editing
both by hand.
Usage:
./update_version.py # sync APP_VERSION only
./update_version.py --release # sync, then tag the current commit
# with that version and push the tag
# to origin (the release itself — no
# separate GitHub Release step, this
# env has no `gh`)
"""
import argparse
import pathlib
import re
import subprocess
import sys
import xml.etree.ElementTree as ET
ROOT_DIR = pathlib.Path(__file__).parent
APPDATA_PATH = ROOT_DIR / "export" / "flatpak" / "org.dupot.filebrowser.appdata.xml"
APP_VERSION_CONF_PATH = ROOT_DIR / "src" / "domain" / "conf" / "app_version_conf.py"
_APP_VERSION_RE = re.compile(r'^(APP_VERSION\s*=\s*)"([^"]*)"', re.MULTILINE)
def get_latest_version(appdata_path: pathlib.Path) -> str:
root = ET.parse(appdata_path).getroot()
# <releases> lists newest first (AppStream convention) — the first
# <release> child is "the" current version.
release = root.find("releases/release")
if release is None:
raise SystemExit(f"No <release> entry found in {appdata_path}")
version = release.get("version")
if not version:
raise SystemExit(f"<release> in {appdata_path} has no version attribute")
return version
def set_app_version(app_version_conf_path: pathlib.Path, version: str) -> str | None:
"""Rewrites APP_VERSION in app_version_conf_path to `version`. Returns
the previous value, or None if it was already up to date."""
text = app_version_conf_path.read_text(encoding="utf-8")
match = _APP_VERSION_RE.search(text)
if match is None:
raise SystemExit(f"No APP_VERSION assignment found in {app_version_conf_path}")
previous = match.group(2)
if previous == version:
return None
new_text = _APP_VERSION_RE.sub(rf'\g<1>"{version}"', text, count=1)
app_version_conf_path.write_text(new_text, encoding="utf-8")
return previous
def _git(*args: str) -> str:
result = subprocess.run(
["git", *args], cwd=ROOT_DIR, capture_output=True, text=True, check=True
)
return result.stdout.strip()
def create_release(version: str) -> None:
"""Tags the current commit with `version` (bare, e.g. "1.0.12" — same
convention as the existing "1.0.0" tag, no "v" prefix) and pushes the
tag to origin."""
if _git("status", "--porcelain"):
raise SystemExit(
"Working tree has uncommitted changes — commit them (including "
"any APP_VERSION sync above) before tagging a release."
)
if _git("tag", "-l", version):
raise SystemExit(f"Tag {version} already exists — nothing to do.")
_git("tag", "-a", version, "-m", f"Release {version}")
_git("push", "origin", version)
print(f"Tagged and pushed release {version}")
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"--release",
action="store_true",
help="Also tag the current commit with the version and push the tag to origin.",
)
args = parser.parse_args()
version = get_latest_version(APPDATA_PATH)
previous = set_app_version(APP_VERSION_CONF_PATH, version)
if previous is None:
print(f"APP_VERSION already up to date ({version})")
else:
print(f"APP_VERSION: {previous} -> {version}")
if args.release:
create_release(version)
return 0
if __name__ == "__main__":
sys.exit(main())