The Gaudi Framework  master (6507eef1)
Loading...
Searching...
No Matches
update_version Namespace Reference

Classes

class  Fields
class  ReplacementRule
class  FileUpdater

Functions

str|None gitlab_token ()
tuple[str, str] normalize_version (str version)
tuple[str, list[str], list[str]] update_changelog (Fields fields)
str contributor_handle (str name)
 update_version (str version, datetime.datetime date, bool dry_run)

Function Documentation

◆ contributor_handle()

str contributor_handle ( str name)

Definition at line 210 of file update_version.py.

210def contributor_handle(name: str) -> str:
211 if token := gitlab_token():
212 from requests import get
213
214 users = get(
215 "https://gitlab.cern.ch/api/v4/users",
216 headers={"PRIVATE-TOKEN": token},
217 params={"search": name},
218 ).json()
219 if users:
220 return f'@{users[0]["username"]}'
221 return name
222
223
224@click.command()
225@click.argument("version", type=str)
226@click.argument(
227 "date",
228 type=click.DateTime(("%Y-%m-%d",)),
229 metavar="[DATE]",
230 default=datetime.datetime.now(),
231)
232@click.option(
233 "--dry-run",
234 "-n",
235 default=False,
236 is_flag=True,
237 help="only show what would change, but do not modify the files",
238)

◆ gitlab_token()

str | None gitlab_token ( )
Return the GitLab API token to use, if available.

The token is taken from the ``GITLAB_TOKEN`` environment variable or, as a
fallback, from the file indicated by ``GITLAB_TOKEN_FILE`` (default
``~/.gitlab_token``).  The file, if present, must contain only the token.

Definition at line 25 of file update_version.py.

25def gitlab_token() -> str | None:
26 """
27 Return the GitLab API token to use, if available.
28
29 The token is taken from the ``GITLAB_TOKEN`` environment variable or, as a
30 fallback, from the file indicated by ``GITLAB_TOKEN_FILE`` (default
31 ``~/.gitlab_token``). The file, if present, must contain only the token.
32 """
33 if token := os.environ.get("GITLAB_TOKEN"):
34 return token
35 token_file = os.environ.get("GITLAB_TOKEN_FILE", "~/.gitlab_token")
36 try:
37 with open(os.path.expanduser(token_file)) as f:
38 return f.read().strip() or None
39 except OSError:
40 return None
41
42

◆ normalize_version()

tuple[str, str] normalize_version ( str version)
Convert a version in format "vXrY" or "X.Y" in the pair ("X.Y", "vXrY").

>>> normalize_version("v37r0")
('37.0', 'v37r0')
>>> normalize_version("37.0.1")
('37.0.1', 'v37r0p1')

Definition at line 43 of file update_version.py.

43def normalize_version(version: str) -> tuple[str, str]:
44 """
45 Convert a version in format "vXrY" or "X.Y" in the pair ("X.Y", "vXrY").
46
47 >>> normalize_version("v37r0")
48 ('37.0', 'v37r0')
49 >>> normalize_version("37.0.1")
50 ('37.0.1', 'v37r0p1')
51 """
52 # extract the digits
53 numbers = re.findall(r"\d+", version)
54 return (
55 ".".join(numbers),
56 "".join("{}{}".format(*pair) for pair in zip("vrpt", numbers)),
57 )
58
59
GAUDI_API std::string format(const char *,...)
MsgStream format utility "a la sprintf(...)".
Definition MsgStream.cpp:93

◆ update_changelog()

tuple[str, list[str], list[str]] update_changelog ( Fields fields)
Special updater to fill draft changelog entry.

Definition at line 139 of file update_version.py.

139def update_changelog(fields: Fields) -> tuple[str, list[str], list[str]]:
140 """
141 Special updater to fill draft changelog entry.
142 """
143 latest_tag = run(
144 ["git", "describe", "--tags", "--abbrev=0"], capture_output=True, text=True
145 ).stdout.strip()
146 # This formats the git log as a rough markdown list
147 # - collect the log formatting it such that we can machine parse it
148 changes_txt = run(
149 ["git", "log", "--first-parent", "--format=%s<=>%b|", f"{latest_tag}.."],
150 capture_output=True,
151 text=True,
152 ).stdout
153 # - removing trailing separator and make it a single line
154 changes_txt = " ".join(changes_txt.strip().rstrip("|").splitlines())
155 # - normalize issues and merge requests links
156 changes = (
157 changes_txt.replace("Closes #", "gaudi/Gaudi#")
158 .replace("See merge request ", "")
159 .split("|")
160 )
161 # - split the messages and format the list
162 changes = [
163 f"- {msg.strip()} ({', '.join(refs.split())})\n"
164 if refs.strip()
165 else f"- {msg.strip()}\n"
166 for change in changes
167 for msg, refs in ([change.split("<=>", 1)] if "<=>" in change else [])
168 ]
169 # Now we get the list of contributors
170 contributors = sorted(
171 contributor_handle(name)
172 for name in set(
173 run(
174 ["git", "log", "--format=%an", f"{latest_tag}...HEAD"],
175 capture_output=True,
176 text=True,
177 ).stdout.splitlines()
178 )
179 )
180
181 filename = "CHANGELOG.md"
182 with open(filename) as f:
183 old = f.readlines()
184 for idx, line in enumerate(old):
185 if line.startswith("## ["):
186 break
187
188 data = old[:idx]
189 data.extend(
190 [
191 "## [{tag_version}](https://gitlab.cern.ch/gaudi/Gaudi/-/releases/{tag_version}) - {date}\n".format(
192 **fields.data
193 ),
194 "\nA special thanks to all the people that contributed to this release:\n",
195 ",\n".join(contributors),
196 ".\n\n",
197 "### Changed\n",
198 "### Added\n",
199 "### Fixed\n",
200 "\n",
201 ]
202 )
203 data.extend(changes)
204 data.extend(["\n", "\n"])
205 data.extend(old[idx:])
206
207 return filename, old, data
208
209

◆ update_version()

update_version ( str version,
datetime.datetime date,
bool dry_run )
Helper to easily update the project version number in all needed files.

Definition at line 239 of file update_version.py.

239def update_version(version: str, date: datetime.datetime, dry_run: bool):
240 """
241 Helper to easily update the project version number in all needed files.
242 """
243 fields = Fields(version, date.date())
244 click.echo(
245 "Bumping version to {cmake_version} (tag: {tag_version})".format(**fields.data)
246 )
247
248 for updater in [
249 FileUpdater(
250 "CMakeLists.txt",
251 [(r"^project\‍(Gaudi VERSION", "project(Gaudi VERSION {cmake_version}")],
252 ),
253 FileUpdater(
254 "CITATION.cff",
255 [
256 (r"^version: ", "version: {tag_version}"),
257 (r"^date-released: ", "date-released: '{date}'"),
258 ],
259 ),
260 FileUpdater(
261 "docs/source/conf.py",
262 [
263 (r"^version = ", 'version = "{cmake_version}"'),
264 (r"^release = ", 'release = "{tag_version}"'),
265 ],
266 ),
267 FileUpdater(
268 "pixi.toml",
269 [
270 # Only project version values, i.e. those that look like a
271 # version, so that we do not touch e.g. the `"*"` required by
272 # the pixi-build backend.
273 (r'^version = "\d', 'version = "{cmake_version}"'),
274 ],
275 ),
276 update_changelog,
277 ]:
278 filename, old, new = updater(fields)
279
280 if old != new:
281 if dry_run:
282 sys.stdout.writelines(
283 unified_diff(
284 old,
285 new,
286 fromfile=f"a/{filename}",
287 tofile=f"b/{filename}",
288 )
289 )
290 else:
291 click.echo(f"updated {filename}")
292 with open(filename, "w") as f:
293 f.writelines(new)
294
295