1111 python3 hack/create-release-tag.py <version>
1212 python3 hack/create-release-tag.py <version> --commit <SHA>
1313 python3 hack/create-release-tag.py <version> --date YYYY-MM-DD
14- python3 hack/create-release-tag.py <version> --commit <SHA> --date YYYY-MM-DD
14+ python3 hack/create-release-tag.py <version> --commit <SHA> \
15+ --date YYYY-MM-DD
1516
1617 --commit is required when the version has no entry in the bundle catalog
1718 (e.g. backport releases that were never shipped as a container image).
3334 hex SHA that Konflux publishes as an image tag alongside the version tag.
3435
3536 Operator image: openshift4-wincw/windows-machine-config-rhel9-operator
36- Provides: push_date — the UTC timestamp when the image was published to the
37- catalog. This is used as the tag date so the annotated tag timestamp
37+ Provides: push_date — the UTC timestamp when the image was published to
38+ the catalog. This is used as the tag date so the annotated tag timestamp
3839 reflects the actual release date rather than the date the tag was created.
3940
4041 Local git repository
41- Provides: existing tag detection (prevents accidental overwrites), full SHA
42- expansion from short SHAs via git rev-parse, commit existence verification
43- to catch catalog/repo mismatches before the tag is written, and upstream
44- remote detection for the post-creation push instruction.
42+ Provides: existing tag detection (prevents accidental overwrites), full
43+ SHA expansion from short SHAs via git rev-parse, commit existence
44+ verification to catch catalog/repo mismatches before the tag is written,
45+ and upstream remote detection for the post-creation push instruction.
4546
4647━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
4748LOGIC
5354 image tag (fallback for images that predate the OCI label).
5455 Logic:
5556 • Scans the bundle catalog for the image whose tags include vX.Y.Z.
56- • Prefers the full SHA from the OCI label; falls back to the short SHA tag.
57+ • Prefers the full SHA from the OCI label; falls back to the short SHA
58+ tag.
5759 • Any SHA (full or short) is expanded to a full 40-character commit via
5860 git rev-parse and verified to exist locally. This catches catalog/repo
5961 mismatches (e.g. if the local clone is stale) before writing anything.
6365
64662. TAG DATE RESOLUTION
6567 Source: Operator image catalog
66- Data: push_date field from the repository entry for the matching version.
68+ Data: push_date field from the repository entry for the matching
69+ version.
6770 Logic:
68- • The push_date is the UTC timestamp when the operator image was published.
69- • Only the date portion (YYYY-MM-DD) is used; the time is fixed to noon UTC
70- (12:00:00+0000) to produce an unambiguous, timezone-neutral timestamp.
71- • The fixed noon time is set via GIT_COMMITTER_DATE when git tag is invoked,
72- so it appears as the tag's creation date in git log and GitHub.
71+ • The push_date is the UTC timestamp when the operator image was
72+ published.
73+ • Only the date portion (YYYY-MM-DD) is used; the time is fixed to noon
74+ UTC (12:00:00+0000) to produce an unambiguous, timezone-neutral
75+ timestamp.
76+ • The fixed noon time is set via GIT_COMMITTER_DATE when git tag is
77+ invoked, so it appears as the tag's creation date in git log and GitHub.
7378 • --date skips the catalog lookup and uses the provided date instead.
7479
75803. TAG CREATION
85904. PUSH INSTRUCTION
8691 Logic:
8792 • After creation, scans git remote -v for a remote whose URL contains
88- "openshift/windows-machine-config-operator" and uses its configured name
89- (commonly "upstream") in the suggested push command.
93+ "openshift/windows-machine-config-operator" and uses its configured
94+ name (commonly "upstream") in the suggested push command.
9095 • Falls back to the canonical SSH URL if no matching remote is found, so
9196 the command is always complete and immediately usable.
9297"""
9398
99+ # pylint: disable=invalid-name # hyphenated script name is intentional
100+
94101import argparse
95102import os
96103import re
119126# ---------------------------------------------------------------------------
120127
121128def _fetch_pages (api_url : str ) -> list :
122- """Fetch all image records from a catalog API endpoint, handling pagination."""
129+ """Fetch all image records from a catalog API endpoint,
130+ handling pagination."""
123131 images , page = [], 0
124132 while True :
125- resp = requests .get (api_url , params = {"page_size" : 100 , "page" : page ,
126- "sort_by" : "creation_date[desc]" }, timeout = 30 )
133+ resp = requests .get (
134+ api_url ,
135+ params = {
136+ "page_size" : 100 ,
137+ "page" : page ,
138+ "sort_by" : "creation_date[desc]" ,
139+ },
140+ timeout = 30 ,
141+ )
127142 resp .raise_for_status ()
128143 batch = resp .json ().get ("data" , [])
129144 if not batch :
@@ -152,19 +167,25 @@ def _labels(img: dict) -> dict:
152167
153168def fetch_bundle_info (version : str ) -> tuple [str , str ]:
154169 """
155- Return (commit, source_description) for the given version from the bundle catalog.
156- Prefers the org.opencontainers.image.revision label (full SHA); falls back to
157- the short hex SHA image tag. Returns ('', '') if the version is not found.
170+ Return (commit, source_description) for the given version from the
171+ bundle catalog. Prefers the org.opencontainers.image.revision label
172+ (full SHA); falls back to the short hex SHA image tag.
173+ Returns ('', '') if the version is not found.
158174 """
159175 for img in _fetch_pages (BUNDLE_CATALOG_API ):
160176 repos = img .get ("repositories" , [])
161177 if _version_from_tags (repos ) != version :
162178 continue
163- all_tags = [t .get ("name" , "" ) for repo in repos for t in repo .get ("tags" , [])]
179+ all_tags = [
180+ t .get ("name" , "" ) for repo in repos for t in repo .get ("tags" , [])
181+ ]
164182 commit = _labels (img ).get ("org.opencontainers.image.revision" , "" )
165183 if commit :
166184 return commit , "bundle image OCI label"
167- sha_tags = [t for t in all_tags if _HEX_RE .match (t ) and not _VERSION_TAG_RE .match (t )]
185+ sha_tags = [
186+ t for t in all_tags
187+ if _HEX_RE .match (t ) and not _VERSION_TAG_RE .match (t )
188+ ]
168189 if sha_tags :
169190 return sha_tags [0 ], "bundle image tag (short SHA)"
170191 return "" , ""
@@ -173,8 +194,8 @@ def fetch_bundle_info(version: str) -> tuple[str, str]:
173194
174195def fetch_operator_push_date (version : str ) -> str :
175196 """
176- Return the push_date (YYYY-MM-DD) for the given version from the operator catalog,
177- or '' if not found.
197+ Return the push_date (YYYY-MM-DD) for the given version from the
198+ operator catalog, or '' if not found.
178199 """
179200 for img in _fetch_pages (OPERATOR_CATALOG_API ):
180201 for repo in img .get ("repositories" , []):
@@ -192,13 +213,16 @@ def fetch_operator_push_date(version: str) -> str:
192213
193214def git (* args , env = None ) -> str :
194215 """Run a git command and return stdout, raising on failure."""
195- result = subprocess .run (["git" , * args ], capture_output = True , text = True , env = env )
216+ result = subprocess .run (
217+ ["git" , * args ], capture_output = True , text = True , env = env , check = False
218+ )
196219 if result .returncode != 0 :
197220 raise RuntimeError (result .stderr .strip ())
198221 return result .stdout .strip ()
199222
200223
201224def tag_exists (tag : str ) -> bool :
225+ """Return True if the git tag already exists in this repository."""
202226 try :
203227 git ("rev-parse" , tag )
204228 return True
@@ -210,11 +234,11 @@ def resolve_commit(ref: str) -> str:
210234 """Return the full commit SHA for ref, or raise RuntimeError."""
211235 try :
212236 return git ("rev-parse" , f"{ ref } ^{{commit}}" )
213- except RuntimeError :
237+ except RuntimeError as exc :
214238 raise RuntimeError (
215239 f"Commit '{ ref } ' not found in this repository.\n "
216240 "Ensure your local repo is up to date: git fetch origin"
217- )
241+ ) from exc
218242
219243
220244# ---------------------------------------------------------------------------
@@ -223,8 +247,8 @@ def resolve_commit(ref: str) -> str:
223247
224248def _find_upstream_remote () -> str :
225249 """
226- Return the name of the remote pointing to openshift/windows-machine-config-operator,
227- or '' if none is configured.
250+ Return the name of the remote pointing to
251+ openshift/windows-machine-config-operator, or '' if none is configured.
228252 """
229253 try :
230254 lines = git ("remote" , "-v" ).splitlines ()
@@ -233,12 +257,15 @@ def _find_upstream_remote() -> str:
233257 for line in lines :
234258 # Each line: "<name>\t<url> (fetch|push)"
235259 parts = line .split ()
236- if len (parts ) >= 2 and "openshift/windows-machine-config-operator" in parts [1 ]:
260+ if (len (parts ) >= 2
261+ and "openshift/windows-machine-config-operator" in parts [1 ]):
237262 return parts [0 ]
238263 return ""
239264
240265
266+ # pylint: disable=too-many-locals,too-many-branches,too-many-statements
241267def main ():
268+ """Parse args, resolve tag details, confirm with user, create the tag."""
242269 parser = argparse .ArgumentParser (
243270 description = "Create an annotated WMCO release tag." ,
244271 formatter_class = argparse .RawDescriptionHelpFormatter ,
@@ -255,8 +282,10 @@ def main():
255282 )
256283 parser .add_argument ("version" , metavar = "X.Y.Z" ,
257284 help = "Release version without 'v' prefix" )
258- parser .add_argument ("--commit" , metavar = "SHA" ,
259- help = "Override commit SHA (required if version is not in catalog)" )
285+ parser .add_argument (
286+ "--commit" , metavar = "SHA" ,
287+ help = "Override commit SHA (required if version is not in catalog)" ,
288+ )
260289 parser .add_argument ("--date" , metavar = "YYYY-MM-DD" ,
261290 help = "Override published date" )
262291 args = parser .parse_args ()
@@ -273,24 +302,40 @@ def main():
273302 message = f"Windows Machine Config Operator { tag } "
274303
275304 if tag_exists (tag ):
276- print (f"ERROR: Tag '{ tag } ' already exists. Delete it first if you intend to recreate it." ,
277- file = sys .stderr )
305+ print (
306+ f"ERROR: Tag '{ tag } ' already exists. "
307+ "Delete it first if you intend to recreate it." ,
308+ file = sys .stderr ,
309+ )
278310 sys .exit (1 )
279311
280- # Resolve commit
281312 need_catalog = not args .commit or not args .date
282313 if need_catalog :
283- print (f"Fetching release details for { tag } from Red Hat Container Catalog..." , flush = True )
314+ print (
315+ f"Fetching release details for { tag } "
316+ "from Red Hat Container Catalog..." ,
317+ flush = True ,
318+ )
284319
285320 if args .commit :
286321 commit_sha = args .commit
287322 commit_source = "provided manually"
288323 else :
289324 commit_sha , commit_source = fetch_bundle_info (version )
290325 if not commit_sha :
291- print (f"\n ERROR: Could not resolve commit SHA for { tag } ." , file = sys .stderr )
292- print (" The bundle image for this version may not be in the catalog." , file = sys .stderr )
293- print (" Provide the commit manually: --commit <SHA>" , file = sys .stderr )
326+ print (
327+ f"\n ERROR: Could not resolve commit SHA for { tag } ." ,
328+ file = sys .stderr ,
329+ )
330+ print (
331+ " The bundle image for this version "
332+ "may not be in the catalog." ,
333+ file = sys .stderr ,
334+ )
335+ print (
336+ " Provide the commit manually: --commit <SHA>" ,
337+ file = sys .stderr ,
338+ )
294339 sys .exit (1 )
295340
296341 if args .date :
@@ -300,9 +345,19 @@ def main():
300345 published_date = fetch_operator_push_date (version )
301346 date_source = "operator image push date"
302347 if not published_date :
303- print (f"\n ERROR: Could not resolve published date for { tag } ." , file = sys .stderr )
304- print (" The operator image for this version may not be in the catalog." , file = sys .stderr )
305- print (" Provide the date manually: --date YYYY-MM-DD" , file = sys .stderr )
348+ print (
349+ f"\n ERROR: Could not resolve published date for { tag } ." ,
350+ file = sys .stderr ,
351+ )
352+ print (
353+ " The operator image for this version "
354+ "may not be in the catalog." ,
355+ file = sys .stderr ,
356+ )
357+ print (
358+ " Provide the date manually: --date YYYY-MM-DD" ,
359+ file = sys .stderr ,
360+ )
306361 sys .exit (1 )
307362
308363 # Expand to full commit SHA and verify it exists locally
@@ -343,7 +398,10 @@ def main():
343398 sys .exit (1 )
344399
345400 upstream_remote = _find_upstream_remote ()
346- push_target = upstream_remote if upstream_remote else "git@github.com:openshift/windows-machine-config-operator.git"
401+ upstream_url = (
402+ "git@github.com:openshift/windows-machine-config-operator.git"
403+ )
404+ push_target = upstream_remote if upstream_remote else upstream_url
347405
348406 print ()
349407 print (f"Tag '{ tag } ' created. To push to the upstream repository:" )
0 commit comments