forked from actris-cloudnet/cloudnetpy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
release-version
executable file
·155 lines (131 loc) · 4.83 KB
/
release-version
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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
#!/usr/bin/env python
import argparse
import re
import subprocess
import sys
from dataclasses import dataclass
from datetime import date
from pathlib import Path
VERSION_FILE = "cloudnetpy/version.py"
CHANGELOG_FILE = "CHANGELOG.md"
def main(component: str):
current_branch = (
subprocess.check_output(["git", "rev-parse", "--abbrev-ref", "HEAD"])
.decode("utf-8")
.strip()
)
assert current_branch == "master", "FATAL: Not in master branch"
old_version = _read_version()
if component == "major":
new_version = Version(old_version.major + 1, 0, 0)
elif component == "minor":
new_version = Version(old_version.major, old_version.minor + 1, 0)
elif component == "patch":
new_version = Version(old_version.major, old_version.minor, old_version.patch + 1)
entry = _read_changelog(old_version)
if not _confirm(f"Updating cloudnetPy version {old_version} -> {new_version}. Continue?"):
return
print(f"\nCurrent changelog entry in {CHANGELOG_FILE}:\n\n{entry}\n")
if not _confirm("Use this for the release?"):
return
_update_changelog(new_version)
_write_version(new_version)
_commit_and_push(new_version)
@dataclass
class Version:
major: int
minor: int
patch: int
def __str__(self):
return f"{self.major}.{self.minor}.{self.patch}"
def _confirm(msg: str) -> bool:
return input(f"{msg} y/n [y] ").lower() in ("y", "")
def _commit_and_push(version: Version):
git_commands = [
["add", VERSION_FILE, CHANGELOG_FILE],
["commit", "-m", f"Release version {version}"],
["push"],
["tag", f"v{version}"],
["push", "--tags"],
]
for command in git_commands:
subprocess.run(["git"] + command, check=True)
def _read_int(text: str, key: str) -> int:
m = re.search(f"^{key} = (\d+)$", text, flags=re.M) # pylint: disable=W1401
if m is None:
raise ValueError
return int(m[1])
def _write_int(text: str, key: str, value: int) -> str:
return re.sub(f"^{key} = \d+$", f"{key} = {value}", text, flags=re.M) # pylint: disable=W1401
def _read_version() -> Version:
text = Path(VERSION_FILE).read_text(encoding="utf-8")
return Version(
major=_read_int(text, "MAJOR"),
minor=_read_int(text, "MINOR"),
patch=_read_int(text, "PATCH"),
)
def _write_version(version: Version):
p = Path(VERSION_FILE)
text = p.read_text(encoding="utf-8")
text = _write_int(text, "MAJOR", version.major)
text = _write_int(text, "MINOR", version.minor)
text = _write_int(text, "PATCH", version.patch)
p.write_text(text, encoding="utf-8")
def _read_changelog(old_version: Version) -> str:
path = Path(CHANGELOG_FILE)
old_content = path.read_text(encoding="utf-8")
entry_start = re.search(r"^## Unpublished$", old_content, re.M)
if entry_start is None:
old_tag = f"v{old_version}"
print(
f"ERROR: No changelog entry found! Updating {CHANGELOG_FILE} with commits "
f"since {old_tag}:\n",
file=sys.stderr,
)
git_log = subprocess.run(
["git", "log", "--pretty=format:- %s", f"{old_tag}.."],
check=True,
capture_output=True,
text=True,
).stdout
print(
f"{git_log}\n\nPlease edit {CHANGELOG_FILE} to your liking and try again.",
file=sys.stderr,
)
new_content = re.sub(
"^##[^#]", f"## Unpublished\n\n{git_log}\n\n## ", old_content, count=1, flags=re.M
)
path.write_text(new_content, encoding="utf-8")
sys.exit(1)
entry_end = re.search(r"^##[^#]", old_content[entry_start.end() :], re.M)
if entry_end is None:
raise ValueError
entry = old_content[entry_start.end() : entry_start.end() + entry_end.start()].strip()
if not entry:
print(
f'ERROR: Changelog entry is empty! Please specify your changes in "Unpublished" '
f"section of {CHANGELOG_FILE}",
file=sys.stderr,
)
sys.exit(1)
return entry
def _update_changelog(version: Version):
today = date.today().isoformat()
p = Path(CHANGELOG_FILE)
old_content = p.read_text(encoding="utf-8")
new_content = re.sub("^## Unpublished$", f"## {version} – {today}", old_content, flags=re.M)
assert new_content != old_content, f"Failed to update {CHANGELOG_FILE}"
p.write_text(new_content, encoding="utf-8")
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Bump CloudnetPy version number.")
parser.add_argument(
"component",
choices=["major", "minor", "patch"],
type=str,
help="Version number component to be updated.",
)
args = parser.parse_args()
try:
main(args.component)
except AssertionError as err:
print(err)