forked from Urban-Meteorology-Reading/SUEWS
-
Notifications
You must be signed in to change notification settings - Fork 11
Expand file tree
/
Copy pathget_ver_git.py
More file actions
executable file
·224 lines (188 loc) · 7.52 KB
/
get_ver_git.py
File metadata and controls
executable file
·224 lines (188 loc) · 7.52 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
#!/usr/bin/env python3
"""
Version generation script for SUEWS/SuPy using git tags.
VERSIONING SYSTEM:
==================
This script generates version numbers based on git tags and commit distance.
For nightly builds (tags ending in .dev):
- Tag: 2025.8.17.dev
- At tag: generates 2025.8.17.dev0
- After 2 commits: generates 2025.8.17.dev2
For production releases:
- Tag: 2025.1.0
- At tag: generates 2025.1.0
- After commits: generates 2025.1.0.dev5 (5 commits after)
For UMEP/QGIS compatible builds (rc1 suffix):
- When BUILD_UMEP_VARIANT=true environment variable is set
- Tag: 2025.1.0 generates 2025.1.0rc1
- These are NumPy 1.x compatible builds for QGIS 3.40 LTR
- rc1 is a pre-release tag, ensuring pip install gets the stable version by default
- See .github/workflows/build-publish_to_pypi.yml for build configuration
CRITICAL FOR NIGHTLY BUILDS:
The GitHub Actions workflow MUST create the tag BEFORE building wheels.
This ensures the version number matches the intended date. Previously,
creating tags after builds caused version mismatches (e.g., Aug 16
builds using Aug 15 tag + commits = wrong version on TestPyPI).
USAGE:
Called by meson.build during the build process to set the package version.
"""
import subprocess
import re
import os
def get_version_from_git():
try:
# Get the most recent tag and the number of commits since that tag
# Only match version tags (starting with digits)
describe_output = (
subprocess.check_output(
[
"git",
"describe",
"--tags",
"--long",
"--match=[0-9]*",
],
stderr=subprocess.DEVNULL,
)
.strip()
.decode("utf-8")
)
# Match against the pattern including optional 'dev' part
match = re.match(
r"^(v?\d+\.\d+\.\d+(?:\.\w+)?)-(\d+)-g([0-9a-f]+)$", describe_output
)
# print(match.groups())
if match:
base_version = match.group(1)
distance = int(match.group(2))
commit_hash = match.group(3)
# Clean up version to be valid Python packaging version
if base_version.startswith("v"):
base_version = base_version[1:]
has_dev_suffix = ".dev" in base_version
# IMPORTANT: For nightly .dev tags, we ALWAYS append a number
# This ensures consistent versioning on TestPyPI:
# - Tag 2025.8.17.dev becomes version 2025.8.17.dev0
# - This prevents version mismatches between git tags and PyPI
if has_dev_suffix:
# Extract base version without .dev suffix
base_without_dev = base_version.rsplit(".dev", 1)[0]
# Always append .devN where N is the commit count (0 for the tag itself)
version = f"{base_without_dev}.dev{distance}"
elif distance == 0:
# For production tags without .dev, keep as-is
version = base_version
else:
# For production tags with commits after, add .dev with commit count
version = f"{base_version}.dev{distance}"
else:
raise ValueError(
f"Output '{describe_output}' does not match the expected pattern."
)
# Check for UMEP build variant (NumPy 1.x compatible build)
# This is set by CI workflow for QGIS/UMEP compatible releases
if os.environ.get("BUILD_UMEP_VARIANT") == "true":
if ".dev" in version:
# For dev/nightly builds: increment dev number to avoid version collision
# Standard: 2025.10.15.dev0 (NumPy 2.x)
# UMEP: 2025.10.15.dev1 (NumPy 1.x)
# This allows both to coexist on TestPyPI for testing
base, dev_num = version.rsplit(".dev", 1)
version = f"{base}.dev{int(dev_num) + 1}"
else:
# For production releases: use rc1 suffix
# rc1 (pre-release) ensures pip install gets stable version by default
version = version + "rc1"
return version
except subprocess.CalledProcessError:
# Git not available (e.g., inside cibuildwheel container)
# Try to read version from previously generated file
version_file = "src/supy/_version_scm.py"
if os.path.exists(version_file):
with open(version_file) as f:
for line in f:
if line.startswith("__version__"):
# Extract version from: __version__ = version = '2025.10.15'
version = line.split("=")[2].strip().strip("'\"")
return version
raise RuntimeError(
"Git command failed and no version file found. "
"Make sure you're running this script in a Git repository, "
"or that src/supy/_version_scm.py exists."
)
except Exception as e:
raise RuntimeError(
f"An error occurred while retrieving the version from Git: {e}"
)
def write_version_file(version_str):
version_file = "src/supy/_version_scm.py"
version_tuple = parse_version_tuple(version_str)
content = f"""# file generated by `get_ver_git.py`
# don't change, don't track in version control
__version__ = version = '{version_str}'
__version_tuple__ = version_tuple = {version_tuple}
"""
with open(version_file, "w") as file:
file.write(content)
# print(f"Generated {version_file} with version {version_str}")
def get_commit_info():
"""Get both short and full commit hashes."""
try:
commit_short = (
subprocess.check_output(
["git", "rev-parse", "--short=7", "HEAD"], stderr=subprocess.DEVNULL
)
.decode("utf-8")
.strip()
)
commit_full = (
subprocess.check_output(
["git", "rev-parse", "HEAD"], stderr=subprocess.DEVNULL
)
.decode("utf-8")
.strip()
)
return commit_short, commit_full
except:
return "unknown", "unknown"
def parse_version_tuple(version_str):
"""
Parse version string into tuple for _version_scm.py
Examples:
"2025.10.15" -> (2025, 10, 15)
"2025.10.15rc1" -> (2025, 10, 15, "rc1")
"2025.10.15.dev0" -> (2025, 10, 15, "dev0")
"""
# Split by dots
parts = version_str.split(".")
# Extract major, minor versions (always numeric)
major = int(parts[0])
minor = int(parts[1])
# Handle patch and any suffix (dev, rc, etc.)
patch_str = parts[2]
suffix = None
# Check for suffixes in the patch component
if "dev" in patch_str:
# e.g., "15dev0" -> patch=15, suffix="dev0"
patch, suffix = patch_str.split("dev", 1)
patch = int(patch) if patch else 0
suffix = "dev" + suffix
elif "rc" in patch_str:
# e.g., "15rc1" -> patch=15, suffix="rc1"
patch, suffix = patch_str.split("rc", 1)
patch = int(patch)
suffix = "rc" + suffix
else:
# Plain numeric patch
patch = int(patch_str)
# Handle 4-component versions like "2025.10.15.dev0"
if len(parts) > 3:
suffix = parts[3]
if suffix:
return (major, minor, patch, suffix)
else:
return (major, minor, patch)
if __name__ == "__main__":
version_str = get_version_from_git()
write_version_file(version_str)
print(get_version_from_git())