-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathversion.py
More file actions
250 lines (219 loc) · 7.56 KB
/
Copy pathversion.py
File metadata and controls
250 lines (219 loc) · 7.56 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
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
#!/usr/bin/env python3
"""
liburlparser Version & Tag Manager
Supports:
- Showing current header version
- Reading latest git tag
- Setting version from tag
- Creating releases (commit + tag)
- Semantic version bumping
Repository:
https://github.com/mohammadraziei/liburlparser
Author:
Mohammad Raziei
License:
MIT
SPDX-License-Identifier: MIT
Run:
python version.py --help
"""
import re
import sys
import argparse
import subprocess
from pathlib import Path
HEADER_PATH = "include/urlparser.h"
PREFIX = "URLPARSER_"
HEADER_ABS_PATH = Path(__file__).parent / HEADER_PATH
# ============================================================
# Utilities
# ============================================================
VERSION_PATTERN = f"#define\\s+{PREFIX}VERSION_(MAJOR|MINOR|PATCH)\\s+(\\d+)"
def run(cmd):
subprocess.run(cmd, check=True)
def git_last_tag():
try:
result = subprocess.run(
["git", "describe", "--tags", "--abbrev=0"],
capture_output=True,
text=True,
check=True,
)
return result.stdout.strip()
except subprocess.CalledProcessError:
return None
def read_version():
if not HEADER_ABS_PATH.exists():
sys.exit(f"{HEADER_PATH} not found")
text = HEADER_ABS_PATH.read_text()
matches = re.findall(VERSION_PATTERN, text)
version = {name: int(value) for name, value in matches}
return version, text
def write_version(version, text):
def repl(match):
name = match.group(1)
return f"#define {PREFIX}VERSION_{name} {version[name]}"
new_text = re.sub(VERSION_PATTERN, repl, text)
HEADER_ABS_PATH.write_text(new_text)
def version_str(v):
return f"{v['MAJOR']}.{v['MINOR']}.{v['PATCH']}"
def bump(version, part):
if part == "major":
version["MAJOR"] += 1
version["MINOR"] = 0
version["PATCH"] = 0
elif part == "minor":
version["MINOR"] += 1
version["PATCH"] = 0
elif part == "patch":
version["PATCH"] += 1
# ============================================================
# Commands
# ============================================================
def cmd_show(args):
version, _ = read_version()
part = getattr(args, 'part', None)
if part:
print(version[part.upper()])
else:
print(version_str(version))
def cmd_bump_or_set(args):
version, text = read_version()
part = args.part.upper() # MAJOR, MINOR, or PATCH
if args.value:
val_str = args.value
# Handle other offsets (+1, -5, etc)
if val_str.startswith(('+', '-')):
try:
if len(val_str) == 1:
val_str += "1"
offset = int(val_str)
version[part] += offset
# Ensure version doesn't go below 0
if version[part] < 0:
version[part] = 0
except ValueError:
sys.exit(f"Invalid offset value: {val_str}")
else:
# It is a direct set value (e.g., 4)
try:
version[part] = int(val_str)
except ValueError:
sys.exit(f"Invalid value for {part}: {val_str}")
write_version(version, text)
print(f"Version updated to {version_str(version)}")
else:
# If no value provided, just show the current part
print(version[part])
def cmd_tag(args):
# --------------------------------------------------------
# python version.py tag
# --------------------------------------------------------
if args.subcommand is None:
tag = git_last_tag()
print(tag if tag else "No git tags found")
return
# --------------------------------------------------------
# python version.py tag set
# --------------------------------------------------------
if args.subcommand == "set":
tag = git_last_tag()
if not tag:
sys.exit("No git tags found")
value = tag.lstrip("v")
parts = value.split(".")
if len(parts) != 3 or not all(p.isdigit() for p in parts):
sys.exit("Invalid tag format")
version, text = read_version()
version["MAJOR"], version["MINOR"], version["PATCH"] = map(int, parts)
write_version(version, text)
print(f"Version set from tag → {value}")
return
# --------------------------------------------------------
# python version.py tag create [...]
# --------------------------------------------------------
if args.subcommand == "create":
version, text = read_version()
if args.value is None:
# use current header version
pass
elif args.value in ("major", "minor", "patch"):
bump(version, args.value)
else:
value = args.value.lstrip("v")
parts = value.split(".")
if len(parts) != 3 or not all(p.isdigit() for p in parts):
sys.exit("Invalid version format")
version["MAJOR"], version["MINOR"], version["PATCH"] = map(int, parts)
write_version(version, text)
new_version = version_str(version)
tag_name = f"v{new_version}"
run(["git", "add", str(HEADER_ABS_PATH)])
run(["git", "commit", "-m", f"Release {new_version}"])
run(["git", "tag", tag_name])
print(f"Committed and tagged {tag_name}")
return
# ============================================================
# CLI
# ============================================================
def main():
parser = argparse.ArgumentParser(
prog="version.py",
description="dynamic version and git tag manager",
formatter_class=argparse.RawTextHelpFormatter,
epilog="""
Examples:
python version.py
Show header version
python version.py minor
Show minor version
python version.py major +
Increase major version by 1
python version.py patch +2
Increase patch version by 2
python version.py minor -5
Decrease minor version by 5
python version.py patch 4
Set patch version to 4
python version.py tag
Show latest git tag
python version.py tag set
Set header version from latest tag
python version.py tag create
Commit and tag using current header version
python version.py tag create minor
Bump minor, commit and tag
python version.py tag create v1.2.3
Set version, commit and tag
"""
)
subparsers = parser.add_subparsers(dest="command")
# Default show (if no command provided)
parser.set_defaults(func=cmd_show)
# Version parts commands (major, minor, patch)
for part in ["major", "minor", "patch"]:
part_parser = subparsers.add_parser(part, help=f"Manage {part} version")
part_parser.add_argument(
"value",
nargs="?",
help="Value to set (e.g., 4), or offset (e.g., +, -, +1, -5)"
)
part_parser.set_defaults(func=cmd_bump_or_set, part=part)
# tag command
tag_parser = subparsers.add_parser("tag", help="Manage git tags")
tag_sub = tag_parser.add_subparsers(dest="subcommand")
tag_parser.set_defaults(func=cmd_tag)
tag_sub.add_parser("set", help="Set version from latest tag")
create_parser = tag_sub.add_parser("create", help="Create release tag")
create_parser.add_argument(
"value",
nargs="?",
help="major | minor | patch | X.Y.Z"
)
args = parser.parse_args()
if hasattr(args, "func"):
args.func(args)
else:
parser.print_help()
if __name__ == "__main__":
main()