-
Notifications
You must be signed in to change notification settings - Fork 21
/
Copy pathrelease.py
183 lines (121 loc) · 6.14 KB
/
release.py
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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
import os
import subprocess
import sys
from typing import List
from pyboolnet.version import read_version_txt, write_version_txt, read_version_git
GIT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), ".git"))
COMMIT = subprocess.check_output(["git", f"--git-dir={GIT_DIR}", "rev-parse", "HEAD"]).strip().decode("utf-8")
PORCELAIN = subprocess.check_output(["git", f"--git-dir={GIT_DIR}", "status", "--porcelain"]).decode("utf-8").split("\n")
MODIFIED = [x for x in PORCELAIN if " M " in x]
TAGS = subprocess.check_output(["git", f"--git-dir={GIT_DIR}", "tag"]).strip().decode("utf-8").split("\n")
INDENT = 8
def subprocess_run(command: List[str]) -> subprocess.CompletedProcess:
return subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
def print_process_output(command: List[str], process: subprocess.CompletedProcess, indent: int = INDENT):
lines = [indent * " " + "command = " + " ".join(command)]
lines += [indent * " " + line for line in [x for x in process.stdout.decode("utf-8").strip().split("\n") if x]]
lines += [indent * " " + line for line in [x for x in process.stderr.decode("utf-8").strip().split("\n") if x]]
print("\n".join(lines))
def repo_is_ready_for_release() -> bool:
is_ok = len(MODIFIED) == 0
print(f"repo is ready: {'OK' if is_ok else 'FAILED'}")
if not is_ok:
lines = [INDENT * " " + line for line in MODIFIED]
print("\n".join(lines))
return is_ok
def pytest_is_happy() -> bool:
print("running pytest ..")
command = ["python3", "-m", "pytest", "tests/"]
result = subprocess_run(command=command)
print(f"pytest is happy: {'OK' if result.returncode == 0 else 'FAIL'}")
print_process_output(command=command, process=result)
return result.returncode == 0
def read_release_version():
if len(sys.argv) > 1:
release_version = sys.argv[1]
print(f"release_version={release_version}")
return release_version
else:
print("release_version=", end="")
return input()
def read_release_message():
print("release_message=", end="")
return input()
def create_release_commit_or_exit(version: str, message: str) -> bool:
command = ["git", "commit", "-a", "-m", f"release {version}" + (f": {message}" if message else "")]
result = subprocess_run(command=command)
print(f"creating release commit: {'OK' if result.returncode == 0 else 'FAIL'}")
print_process_output(command=command, process=result)
return result.returncode == 0
def create_release_tag(tag: str) -> bool:
command = ["git", "tag", tag, "-m", f"release {tag}"]
result = subprocess_run(command=command)
print(f"creating release tag: {'OK' if result.returncode == 0 else 'FAIL'}")
print_process_output(command=command, process=result)
return result.returncode == 0
def push_release() -> bool:
command = ["git", "push"]
result = subprocess_run(command=command)
print(f"pushing release commit: {'OK' if result.returncode == 0 else 'FAIL'}")
print_process_output(command=command, process=result)
return result.returncode == 0
def push_tag() -> bool:
command = ["git", "push", "--tags"]
result = subprocess_run(command=command)
print(f"pushing tag: {'OK' if result.returncode == 0 else 'FAIL'}")
print_process_output(command=command, process=result)
return result.returncode == 0
def reset_version_and_exit(reset_version: str):
write_version_txt(version=reset_version)
print(f"reset version: {reset_version}")
sys.exit(1)
def bake_release_version_into_code(release_version: str) -> bool:
write_version_txt(version=release_version)
print("baking release version: OK")
return True
def release_version_is_acceptable(current_version: str, release_version: str) -> bool:
if release_version in TAGS:
print("release tag is acceptable: FAIL (tag in use)")
return False
if release_version <= current_version:
print(f"release tag is acceptable: FAIL (release version too small)")
print("continue? (y/n)")
return input() == "y"
return True
def print_announcement(current_version: str, release_version: str, release_message: str):
commit_messages = subprocess.check_output(["git", "log", '--pretty=format:"%s"', f"{current_version}..{release_version}"]).decode("utf-8").split("\n")
print(f"## release {release_version}: {release_message}")
for message in commit_messages[1:]:
print(f" - {message[1:-1]}")
print(f" - compare: [compare/{current_version}...{release_version}](https://github.com/hklarner/pyboolnet/compare/{current_version}...{release_version})")
def update_readme_md(current_version: str, release_version: str):
with open("README.md", "r") as fp:
text = fp.read()
text = text.replace(current_version, release_version)
with open("README.md", "w") as fp:
fp.write(text)
print("replaced version string in README.md: OK")
if __name__ == "__main__":
print(f"version according to git: {read_version_git()}")
current_version = read_version_txt()
print(f"version according to txt: {current_version}")
release_version = read_release_version()
release_message = read_release_message()
if not release_version_is_acceptable(current_version=current_version, release_version=release_version):
sys.exit(1)
if not repo_is_ready_for_release():
sys.exit(1)
if not pytest_is_happy():
sys.exit(1)
if not bake_release_version_into_code(release_version=release_version):
sys.exit(1)
update_readme_md(current_version=current_version, release_version=release_version)
if not create_release_commit_or_exit(version=release_version, message=release_message):
reset_version_and_exit(reset_version=current_version)
if not create_release_tag(tag=release_version):
reset_version_and_exit(reset_version=current_version)
if not push_release():
reset_version_and_exit(reset_version=current_version)
if not push_tag():
reset_version_and_exit(reset_version=current_version)
print_announcement(current_version=current_version, release_version=release_version, release_message=release_message)