55
66Usage:
77 python check_release_notes.py --git-tag <tag> --component <component>
8- python check_release_notes.py --git-tag <tag> --component <component> --git-ref refs/tags/<tag>
98
10- By default, notes and release configuration come from the working tree.
11- --git-ref reads those files from the given ref without changing the checkout,
12- so the workflow's current checker can validate older release tags. Successful
13- checks append the parsed version to GITHUB_OUTPUT when it is set.
9+ Notes and release configuration come from the working tree. Successful checks
10+ append the parsed version to GITHUB_OUTPUT when it is set.
1411
1512Exit codes:
1613 0 — release-notes policy satisfied
2320import argparse
2421import os
2522import re
26- import subprocess
2723import sys
2824from pathlib import Path
2925
@@ -70,41 +66,16 @@ def is_post_release(version: str) -> bool:
7066 return ".post" in version
7167
7268
73- def read_repo_file (path : Path , repo_root : Path , git_ref : str | None = None ) -> bytes | None :
74- """Read a file from the working tree or an already-resolved Git commit."""
75- if git_ref is None :
76- full = repo_root / path
77- return full .read_bytes () if full .is_file () else None
78-
79- object_name = f"{ git_ref } :{ path .as_posix ()} "
80- result = subprocess .run (
81- ["git" , "cat-file" , "--batch-check=%(objecttype)" ], # noqa: S607
82- input = f"{ object_name } \n " ,
83- cwd = repo_root ,
84- check = True ,
85- capture_output = True ,
86- text = True ,
87- )
88- object_type = result .stdout .strip ()
89- if object_type == f"{ object_name } missing" or object_type == "tree" :
90- return None
91- if object_type != "blob" :
92- raise ValueError (f"Cannot read release-notes source { object_name !r} : unexpected object type { object_type !r} " )
93- return subprocess .run ( # noqa: S603 - the resolved commit and path are passed without a shell.
94- ["git" , "show" , object_name ], # noqa: S607
95- cwd = repo_root ,
96- check = True ,
97- capture_output = True ,
98- ).stdout
99-
100-
101- def load_backport_branch (repo_root : Path = Path ("." ), git_ref : str | None = None ) -> str | None :
102- contents = read_repo_file (Path ("ci" , "versions.yml" ), repo_root , git_ref )
103- if contents is not None :
104- for line in contents .decode ("utf-8" ).splitlines ():
105- m = BACKPORT_BRANCH_RE .match (line .strip ())
106- if m :
107- return m .group ("branch" )
69+ def load_backport_branch (repo_root : Path = Path ("." )) -> str | None :
70+ path = repo_root / "ci" / "versions.yml"
71+ try :
72+ with open (path , encoding = "utf-8" ) as f :
73+ for line in f :
74+ m = BACKPORT_BRANCH_RE .match (line .strip ())
75+ if m :
76+ return m .group ("branch" )
77+ except FileNotFoundError :
78+ pass
10879 github_ref_name = os .environ .get ("GITHUB_REF_NAME" , "" )
10980 if BACKPORT_BRANCH_NAME_RE .match (github_ref_name ):
11081 return github_ref_name
@@ -121,9 +92,7 @@ def notes_path(package: str, version: str) -> Path:
12192 return Path (package , "docs" , "source" , "release" , f"{ version } -notes.rst" )
12293
12394
124- def check_release_notes (
125- git_tag : str , component : str , repo_root : Path = Path ("." ), git_ref : str | None = None
126- ) -> list [tuple [str | Path , str ]]:
95+ def check_release_notes (git_tag : str , component : str , repo_root : Path = Path ("." )) -> list [tuple [str | Path , str ]]:
12796 """Return a list of (path, reason) for missing or empty release notes.
12897
12998 ``path`` is the repo-relative notes path, or a ``<placeholder>`` naming the
@@ -143,10 +112,10 @@ def check_release_notes(
143112 return []
144113
145114 path = notes_path (COMPONENT_TO_PACKAGE [component ], version )
146- contents = read_repo_file ( path , repo_root , git_ref )
147- if contents is None :
115+ full = repo_root / path
116+ if not full . is_file () :
148117 return [(path , "missing" )]
149- if not contents and component != "cuda-pathfinder" :
118+ if full . stat (). st_size == 0 and component != "cuda-pathfinder" :
150119 return [(path , "empty" )]
151120 return []
152121
@@ -193,7 +162,6 @@ def validate_backport_decision(
193162 backport_git_tag : str ,
194163 backport_branch : str | None ,
195164 repo_root : Path ,
196- git_ref : str | None = None ,
197165) -> tuple [int | None , list [tuple [str | Path , str ]]]:
198166 if component not in BACKPORT_PLANNING_COMPONENTS or is_post_release (version ):
199167 return None , []
@@ -203,7 +171,7 @@ def validate_backport_decision(
203171 return 2 , []
204172
205173 if is_backport_version (version , backport_branch ):
206- problems = check_release_notes (git_tag , component , repo_root , git_ref )
174+ problems = check_release_notes (git_tag , component , repo_root )
207175 if problems :
208176 warn_missing_backport_notes (git_tag , component , problems )
209177 else :
@@ -241,7 +209,7 @@ def validate_backport_decision(
241209 )
242210 return 2 , []
243211
244- problems = check_release_notes (decision , component , repo_root , git_ref )
212+ problems = check_release_notes (decision , component , repo_root )
245213 if problems :
246214 return 1 , problems
247215 return None , []
@@ -252,9 +220,6 @@ def main(argv: list[str] | None = None) -> int:
252220 parser .add_argument ("--git-tag" , required = True )
253221 parser .add_argument ("--component" , required = True , choices = list (COMPONENT_TO_PACKAGE ))
254222 parser .add_argument ("--repo-root" , default = Path ("." ), type = Path )
255- parser .add_argument (
256- "--git-ref" , help = "Read notes and release configuration from this Git ref without checking it out"
257- )
258223 parser .add_argument ("--backport-git-tag" , default = "" )
259224 parser .add_argument ("--backport-branch" , default = "" )
260225 args = parser .parse_args (argv )
@@ -267,41 +232,19 @@ def main(argv: list[str] | None = None) -> int:
267232 )
268233 return 2
269234
270- source_ref = args .git_ref
271- try :
272- if args .git_ref is not None :
273- args .git_ref = subprocess .run ( # noqa: S603 - --end-of-options prevents the ref from becoming an option.
274- ["git" , "rev-parse" , "--verify" , "--end-of-options" , f"{ args .git_ref } ^{{commit}}" ], # noqa: S607
275- cwd = args .repo_root ,
276- check = True ,
277- capture_output = True ,
278- text = True ,
279- ).stdout .strip ()
280- return check_policy (args , version )
281- except subprocess .CalledProcessError as exc :
282- detail = exc .stderr .decode ("utf-8" ) if isinstance (exc .stderr , bytes ) else exc .stderr
283- print (f"ERROR: cannot read Git release-notes source { source_ref !r} : { detail .strip ()} " , file = sys .stderr )
284- return 2
285- except ValueError as exc :
286- print (f"ERROR: { exc } " , file = sys .stderr )
287- return 2
288-
289-
290- def check_policy (args : argparse .Namespace , version : str ) -> int :
291235 if is_post_release (version ) and args .component != "cuda-pathfinder" :
292236 print (f"Post-release tag ({ args .git_tag } ), skipping release-notes check." )
293237 write_version_output (version )
294238 return 0
295239
296- backport_branch = args .backport_branch or load_backport_branch (args .repo_root , args . git_ref )
240+ backport_branch = args .backport_branch or load_backport_branch (args .repo_root )
297241 rc , problems = validate_backport_decision (
298242 git_tag = args .git_tag ,
299243 component = args .component ,
300244 version = version ,
301245 backport_git_tag = args .backport_git_tag ,
302246 backport_branch = backport_branch ,
303247 repo_root = args .repo_root ,
304- git_ref = args .git_ref ,
305248 )
306249 if rc is not None :
307250 if problems :
@@ -313,7 +256,7 @@ def check_policy(args: argparse.Namespace, version: str) -> int:
313256 return rc
314257
315258 if not problems :
316- problems = check_release_notes (args .git_tag , args .component , args .repo_root , args . git_ref )
259+ problems = check_release_notes (args .git_tag , args .component , args .repo_root )
317260
318261 if not problems :
319262 print (f"Release notes present for tag { args .git_tag } , component { args .component } ." )
0 commit comments