forked from OSGeo/grass
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathupdate_version.py
executable file
·320 lines (267 loc) · 9.79 KB
/
update_version.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
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
#!/usr/bin/env python3
"""Update VERSION file to release and development versions"""
import sys
import datetime
from types import SimpleNamespace
import argparse
def read_version_file():
"""Return version file content as object instance with attributes"""
with open("include/VERSION", encoding="utf-8") as file:
lines = file.read().splitlines()
return SimpleNamespace(
major=lines[0], minor=lines[1], micro=lines[2], year=lines[3]
)
def write_version_file(major, minor, micro, year):
"""Write version file content from object instance with attributes"""
with open("include/VERSION", "w", encoding="utf-8") as file:
file.write(f"{major}\n")
file.write(f"{minor}\n")
file.write(f"{micro}\n")
file.write(f"{year}\n")
def is_int(value):
"""Return True if the *value* represents an integer, False otherwise"""
try:
int(value)
return True
except ValueError:
return False
def this_year():
"""Return current year"""
return datetime.date.today().year
def construct_version(version_info):
"""Construct version string from version info"""
return f"{version_info.major}.{version_info.minor}.{version_info.micro}"
def suggest_commit_from_version_file(action, tag):
"""Using information in the version file, suggest a commit message"""
version_file = read_version_file()
suggest_commit(action, construct_version(version_file), tag=tag)
def suggest_commit(action, version, tag):
"""Suggest a commit message for action and version"""
print("read:")
if tag:
print(" user_message: Use the provided messages for the commit and the tag.")
print(" note: Once all checks pass, you are expected to create a tag.")
else:
print(" user_message: Use the provided message for the commit")
print("use:")
print(f" commit_message: 'version: {action} {version}'")
if tag:
print(f" tag_message: 'GRASS GIS {version}'")
def release_candidate(args):
"""Switch to RC"""
version_file = read_version_file()
micro = version_file.micro
if micro.endswith("dev"):
micro = micro[:-3]
if not micro:
sys.exit("Creating RC from a dev micro without number is not possible")
micro = f"{micro}RC{args.number}"
else:
sys.exit(
"Creating RC from a non-dev VERSION file "
f"with micro '{micro}' is not possible"
)
write_version_file(
major=version_file.major,
minor=version_file.minor,
micro=micro,
year=this_year(),
)
suggest_commit_from_version_file("GRASS GIS", tag=True)
def release(_unused):
"""Switch to release version"""
version_file = read_version_file()
micro = version_file.micro
if micro.endswith("dev"):
micro = micro[:-3]
if not micro:
micro = 0
micro = f"{micro}"
else:
sys.exit("Creating a release from a non-dev VERSION file is not possible")
write_version_file(
major=version_file.major,
minor=version_file.minor,
micro=micro,
year=this_year(),
)
suggest_commit_from_version_file("GRASS GIS", tag=True)
def update_micro(_unused):
"""Update to next micro version"""
version_file = read_version_file()
micro = version_file.micro
if micro == "dev":
sys.exit("The micro version does not increase with development-only versions.")
# We could also add micro version when not present, but requested with:
# micro = "0dev"
elif micro.endswith("dev"):
sys.exit(f"Already dev with micro '{micro}'. Release first before update.")
elif is_int(micro):
micro = int(version_file.micro) + 1
micro = f"{micro}dev"
else:
if "RC" in micro:
sys.exit(
f"Updating micro for RC '{micro}' is not possible. "
"Release first before update."
)
sys.exit(f"Unknown micro version in VERSION file: '{micro}'")
write_version_file(
major=version_file.major,
minor=version_file.minor,
micro=micro,
year=this_year(),
)
suggest_commit_from_version_file("Start", tag=False)
def update_minor(args):
"""Update to next minor version"""
version_file = read_version_file()
micro = version_file.micro
minor = int(version_file.minor)
minor += 1
if micro.endswith("dev"):
micro = "0dev"
else:
sys.exit("Updating version from a non-dev VERSION file is not possible")
write_version_file(
major=version_file.major, minor=minor, micro=micro, year=this_year()
)
suggest_commit_from_version_file("Start", tag=False)
def update_major(_unused):
"""Update to next major version"""
version_file = read_version_file()
micro = version_file.micro
if micro.endswith("dev"):
micro = "0dev"
else:
sys.exit("Updating version from a non-dev VERSION file is not possible")
minor = 0
major = int(version_file.major) + 1
write_version_file(major=major, minor=minor, micro=micro, year=this_year())
suggest_commit_from_version_file("Start", tag=False)
def back_to_dev(_unused):
"""Switch version to development state"""
version_file = read_version_file()
micro = version_file.micro
if "RC" in micro:
micro = micro.split("RC")[0]
micro = f"{micro}dev"
action = "Back to"
elif is_int(micro):
micro = int(micro) + 1
micro = f"{micro}dev"
action = "Start"
else:
if micro.endswith("dev"):
sys.exit(f"Already dev with micro '{micro}'")
sys.exit(
"Can switch to dev only from release or RC VERSION file, "
f"not from micro '{micro}'"
)
write_version_file(
major=version_file.major,
minor=version_file.minor,
micro=micro,
year=this_year(),
)
suggest_commit_from_version_file(action, tag=False)
def status_as_yaml(version_info, today, version, tag):
"""Print VERSION file and today's date as YAML"""
print(f"today: {today}")
print(f"year: {version_info.year}")
print(f"major: {version_info.major}")
print(f"minor: {version_info.minor}")
print(f"micro: {version_info.micro}")
print(f"version: {version}")
if tag:
print(f"tag: {version}")
def status_as_bash(version_info, today, version, tag):
"""Print VERSION file and today's date as Bash eval variables"""
print(f"TODAY={today}")
print(f"YEAR={version_info.year}")
print(f"MAJOR={version_info.major}")
print(f"MINOR={version_info.minor}")
print(f"MICRO={version_info.micro}")
print(f"VERSION={version}")
if tag:
print(f"TAG={version}")
def status(args):
"""Print VERSION file and today's date"""
version_info = read_version_file()
today = datetime.date.today().isoformat()
version = construct_version(version_info)
if not version_info.micro.endswith("dev"):
tag = version
else:
tag = None
if args.bash:
status_as_bash(version_info=version_info, today=today, version=version, tag=tag)
else:
status_as_yaml(version_info=version_info, today=today, version=version, tag=tag)
def suggest_message(args):
"""Print suggestion for a commit message
Assumes that the version file was changed, but not commited yet,
but it does not check that assumption.
This shows a wrong commit message if going back from RCs,
but it is not likely this is needed because the suggestion
will be part of the message for the switch and there is
no other work to do afterwards except for the commit
(unlike updating the version number).
"""
version_info = read_version_file()
if not version_info.micro.endswith("dev"):
tag = construct_version(version_info)
action = "GRASS GIS"
else:
tag = None
action = "Start"
suggest_commit_from_version_file(action, tag=tag)
def main():
"""Translate sub-commands to function calls"""
parser = argparse.ArgumentParser(
description="Update VERSION file using the specified action.",
epilog="Run in the root directory to access the VERSION file.",
)
subparsers = parser.add_subparsers(dest="command", required=True)
subparser = subparsers.add_parser(
"rc", help="switch to release candidate (no dev suffix)"
)
subparser.add_argument(
"number", type=int, help="RC number (number sequence not checked)"
)
subparser.set_defaults(func=release_candidate)
subparser = subparsers.add_parser(
"dev", help="switch to development state (attaches dev suffix)"
)
subparser.set_defaults(func=back_to_dev)
subparser = subparsers.add_parser(
"release", help="switch to release version (no dev suffix)"
)
subparser.set_defaults(func=release)
subparser = subparsers.add_parser(
"major", help="increase major (X.y.z) version (attaches dev suffix)"
)
subparser.set_defaults(func=update_major)
subparser = subparsers.add_parser(
"minor", help="increase minor (x.Y.z) version (uses dev in micro)"
)
subparser.set_defaults(func=update_minor)
subparser = subparsers.add_parser(
"micro", help="increase micro (x.y.Z, aka patch) version (attaches dev suffix)"
)
subparser.set_defaults(func=update_micro)
subparser = subparsers.add_parser(
"status", help="show status of VERSION file (as YAML by default)"
)
subparser.add_argument(
"--bash", action="store_true", help="format as Bash variables for eval"
)
subparser.set_defaults(func=status)
subparser = subparsers.add_parser(
"suggest", help="suggest a commit message for new version"
)
subparser.set_defaults(func=suggest_message)
args = parser.parse_args()
args.func(args)
if __name__ == "__main__":
main()