@@ -110,55 +110,51 @@ def validate_readme_versions(root: Path, version: str) -> list[str]:
110110 return errors
111111
112112
113- def tag_commit (root : Path , tag : str ) -> str | None :
114- result = run_git (root , "rev-list " , "-n " , "1" , tag )
113+ def origin_release_tags (root : Path ) -> list [ tuple [ tuple [ int , int , int ], str , str ]] :
114+ result = run_git (root , "ls-remote " , "--tags " , "origin" )
115115 if result .returncode != 0 :
116- return None
117- return result . stdout . strip () or None
116+ detail = result . stderr . strip () or "unknown git ls-remote failure"
117+ raise ValidationError ( f"could not query origin release tags: { detail } " )
118118
119-
120- def is_ancestor (root : Path , ancestor : str , descendant : str ) -> bool :
121- result = run_git (root , "merge-base" , "--is-ancestor" , ancestor , descendant )
122- return result .returncode == 0
123-
124-
125- def codex_release_tags (root : Path ) -> list [tuple [tuple [int , int , int ], str ]]:
126- result = run_git (root , "tag" , "--list" , f"{ CODEX_TAG_PREFIX } [0-9]*.[0-9]*.[0-9]*" )
127- tags : list [tuple [tuple [int , int , int ], str ]] = []
128- baseline_commit = tag_commit (root , LEGACY_BASELINE_TAG )
129- if baseline_commit :
130- tags .append (((0 , 1 , 0 ), LEGACY_BASELINE_TAG ))
131- if result .returncode != 0 :
132- return tags
133- for tag in result .stdout .splitlines ():
134- raw = tag .removeprefix (CODEX_TAG_PREFIX )
135- try :
136- parsed = parse_semver (raw )
137- except ValidationError :
119+ discovered : dict [str , tuple [tuple [int , int , int ], str ]] = {}
120+ for line in result .stdout .splitlines ():
121+ parts = line .split ()
122+ if len (parts ) != 2 or not parts [1 ].startswith ("refs/tags/" ):
138123 continue
139- if baseline_commit and parsed == (0 , 1 , 0 ):
140- commit = tag_commit (root , tag )
141- if commit and commit != baseline_commit :
142- continue
143- elif baseline_commit :
144- commit = tag_commit (root , tag )
145- if commit and not is_ancestor (root , baseline_commit , commit ):
124+ commit , ref = parts
125+ tag = ref .removeprefix ("refs/tags/" )
126+ peeled = tag .endswith ("^{}" )
127+ if peeled :
128+ tag = tag [:- 3 ]
129+ if tag == LEGACY_BASELINE_TAG :
130+ parsed = (0 , 1 , 0 )
131+ elif tag .startswith (CODEX_TAG_PREFIX ):
132+ try :
133+ parsed = parse_semver (tag .removeprefix (CODEX_TAG_PREFIX ))
134+ except ValidationError :
146135 continue
147- if any ( existing == parsed for existing , _ in tags ) :
136+ else :
148137 continue
149- tags .append ((parsed , tag ))
150- return sorted (tags )
138+ existing = discovered .get (tag )
139+ if existing is None or peeled :
140+ discovered [tag ] = (parsed , commit )
141+ return sorted ((parsed , tag , commit ) for tag , (parsed , commit ) in discovered .items ())
151142
152143
153- def changed_paths_since (root : Path , tag : str , watched_paths : list [str ]) -> list [str ]:
144+ def changed_paths_since (root : Path , base_ref : str , watched_paths : list [str ]) -> list [str ]:
154145 existing = [path for path in watched_paths if (root / path ).exists ()]
155146 if not existing :
156147 return []
157- result = run_git (root , "diff" , "--name-only" , tag , "--" , * existing )
158- changed = set (result .stdout .splitlines ()) if result .returncode == 0 else set ()
148+ result = run_git (root , "diff" , "--name-only" , base_ref , "--" , * existing )
149+ if result .returncode != 0 :
150+ detail = result .stderr .strip () or "unknown git diff failure"
151+ raise ValidationError (f"could not compare release files against { base_ref } : { detail } " )
152+ changed = set (result .stdout .splitlines ())
159153 untracked = run_git (root , "ls-files" , "--others" , "--exclude-standard" , "--" , * existing )
160- if untracked .returncode == 0 :
161- changed .update (untracked .stdout .splitlines ())
154+ if untracked .returncode != 0 :
155+ detail = untracked .stderr .strip () or "unknown git ls-files failure"
156+ raise ValidationError (f"could not inspect untracked release files: { detail } " )
157+ changed .update (untracked .stdout .splitlines ())
162158 return sorted (path for path in changed if path in set (watched_paths ))
163159
164160
@@ -177,17 +173,25 @@ def validate_release(root: Path) -> tuple[list[str], list[str]]:
177173 errors .append (f"CHANGELOG.md is missing a section for v{ version } " )
178174 errors .extend (validate_readme_versions (root , version ))
179175
180- tags = codex_release_tags (root )
176+ try :
177+ tags = origin_release_tags (root )
178+ except ValidationError as exc :
179+ errors .append (str (exc ))
180+ return errors , warnings
181181 if not tags :
182- warnings .append ("no codex-vX.Y.Z git tags or legacy v0.1.0 baseline found; skipping tag-diff version-bump check " )
182+ errors .append ("origin has no codex-vX.Y.Z release tags or legacy v0.1.0 baseline" )
183183 return errors , warnings
184184
185- latest_tuple , latest_tag = tags [- 1 ]
185+ latest_tuple , latest_tag , latest_commit = tags [- 1 ]
186186 if version_tuple < latest_tuple :
187187 errors .append (f".codex/VERSION { version } is older than latest tag { latest_tag } " )
188188 return errors , warnings
189189
190- changed = changed_paths_since (root , latest_tag , watched_paths )
190+ try :
191+ changed = changed_paths_since (root , latest_commit , watched_paths )
192+ except ValidationError as exc :
193+ errors .append (str (exc ))
194+ return errors , warnings
191195 if version_tuple == latest_tuple and changed :
192196 errors .append (
193197 "installable or release files changed since "
0 commit comments