88import sys
99import os
1010
11+ def debug (verbose , message ):
12+ """Print debug message if verbose mode is enabled."""
13+ if verbose :
14+ print (f" [DEBUG] { message } " )
15+
1116def parse_repos_config (file_path ):
1217 with open (file_path , "r" ) as f :
1318 return yaml .safe_load (f )["repositories" ]
@@ -90,14 +95,15 @@ def is_version_gte(version1, version2):
9095 return False
9196 return parse_version (version1 ) >= parse_version (version2 )
9297
93- def is_merged_into_stable (repo_url , tag_name , stable_branch , github_token ):
98+ def is_merged_into_stable (repo_url , tag_name , stable_branch , github_token , verbose = False ):
9499 # First get the commit SHA for the tag
95100 api_base = repo_url .replace ("https://github.com/" , "https://api.github.com/repos/" )
96101 headers = {'Authorization' : f'token { github_token } ' } if github_token else {}
97102
98103 # Get tag's commit SHA
99104 tag_response = requests .get (f"{ api_base } /git/refs/tags/{ tag_name } " , headers = headers )
100105 if tag_response .status_code != 200 :
106+ debug (verbose , f"Could not fetch tag { tag_name } , status code: { tag_response .status_code } " )
101107 return False
102108
103109 # Handle both single object and array responses
@@ -106,22 +112,48 @@ def is_merged_into_stable(repo_url, tag_name, stable_branch, github_token):
106112 # Find the exact matching tag in the list
107113 matching_tags = [tag for tag in tag_data if tag ['ref' ] == f'refs/tags/{ tag_name } ' ]
108114 if not matching_tags :
115+ debug (verbose , f"No matching tag found for { tag_name } in response list" )
109116 return False
110117 tag_sha = matching_tags [0 ]['object' ]['sha' ]
111118 else :
112119 tag_sha = tag_data ['object' ]['sha' ]
113-
120+
121+ # Check if the tag is an annotated tag and get the actual commit SHA
122+ if tag_data .get ('object' , {}).get ('type' ) == 'tag' or (
123+ isinstance (tag_data , list ) and
124+ matching_tags and
125+ matching_tags [0 ].get ('object' , {}).get ('type' ) == 'tag' ):
126+
127+ # Get the commit that this tag points to
128+ tag_obj_response = requests .get (f"{ api_base } /git/tags/{ tag_sha } " , headers = headers )
129+ if tag_obj_response .status_code == 200 :
130+ tag_obj = tag_obj_response .json ()
131+ if 'object' in tag_obj and tag_obj ['object' ]['type' ] == 'commit' :
132+ commit_sha = tag_obj ['object' ]['sha' ]
133+ debug (verbose , f"Tag is annotated. Resolved commit SHA: { commit_sha } " )
134+ tag_sha = commit_sha # Use the actual commit SHA
135+
114136 # Get commits on stable branch containing this SHA
115137 commits_response = requests .get (
116138 f"{ api_base } /commits?sha={ stable_branch } &per_page=100" ,
117139 headers = headers
118140 )
119141 if commits_response .status_code != 200 :
142+ debug (verbose , f"Could not fetch commits for branch { stable_branch } , status code: { commits_response .status_code } " )
120143 return False
121144
122145 # Check if any commit in stable's history matches our tag's SHA
123146 stable_commits = [commit ['sha' ] for commit in commits_response .json ()]
124- return tag_sha in stable_commits
147+
148+ is_merged = tag_sha in stable_commits
149+
150+ debug (verbose , f"Tag SHA: { tag_sha } " )
151+ debug (verbose , f"First 5 stable commits: { stable_commits [:5 ]} " )
152+ debug (verbose , f"Total stable commits fetched: { len (stable_commits )} " )
153+ if not is_merged :
154+ debug (verbose , f"Tag SHA not found in first { len (stable_commits )} commits of stable branch" )
155+
156+ return is_merged
125157
126158def is_release_candidate (version ):
127159 return "-rc" in version
@@ -195,13 +227,15 @@ def pr_exists_with_title(repo_url, title, github_token):
195227 return None
196228
197229def main ():
198- github_token = get_github_token ()
230+ parser = argparse .ArgumentParser (description = "Check release status of Lean4 repositories" )
231+ parser .add_argument ("toolchain" , help = "The toolchain version to check (e.g., v4.6.0)" )
232+ parser .add_argument ("--verbose" , "-v" , action = "store_true" , help = "Enable verbose debugging output" )
233+ args = parser .parse_args ()
199234
200- if len (sys .argv ) != 2 :
201- print ("Usage: python3 release_checklist.py <toolchain>" )
202- sys .exit (1 )
203-
204- toolchain = sys .argv [1 ]
235+ github_token = get_github_token ()
236+ toolchain = args .toolchain
237+ verbose = args .verbose
238+
205239 stripped_toolchain = strip_rc_suffix (toolchain )
206240 lean_repo_url = "https://github.com/leanprover/lean4"
207241
@@ -291,6 +325,7 @@ def main():
291325 print (f" ✅ PR with title '{ pr_title } ' exists: #{ pr_number } ({ pr_url } )" )
292326 else :
293327 print (f" ❌ PR with title '{ pr_title } ' does not exist" )
328+ print (f" Run `script/release_steps.py { toolchain } { name } ` to create it" )
294329 repo_status [name ] = False
295330 continue
296331 print (f" ✅ On compatible toolchain (>= { toolchain } )" )
@@ -303,8 +338,10 @@ def main():
303338 print (f" ✅ Tag { toolchain } exists" )
304339
305340 if check_stable and not is_release_candidate (toolchain ):
306- if not is_merged_into_stable (url , toolchain , "stable" , github_token ):
341+ if not is_merged_into_stable (url , toolchain , "stable" , github_token , verbose ):
342+ org_repo = extract_org_repo_from_url (url )
307343 print (f" ❌ Tag { toolchain } is not merged into stable" )
344+ print (f" Run `script/merge_remote.py { org_repo } stable { toolchain } ` to merge it" )
308345 repo_status [name ] = False
309346 continue
310347 print (f" ✅ Tag { toolchain } is merged into stable" )
0 commit comments