@@ -28,6 +28,7 @@ def main() -> None:
2828 rerun_attempts = run ["run_attempt" ] - 1
2929 if rerun_attempts >= max_rerun_attempts :
3030 print (f"Skipped { label } : already rerun { rerun_attempts } times." )
31+ open_issue_if_missing (owner , repo , run )
3132 return
3233
3334 ignored_job_suffixes = tuple (
@@ -54,6 +55,7 @@ def main() -> None:
5455 f"Skipped { label } : { len (failed_real_jobs )} failed jobs"
5556 f" exceeded limit { max_failed_jobs } ."
5657 )
58+ open_issue_or_add_comment (owner , repo , run )
5759 return
5860
5961 subprocess .run (
@@ -92,6 +94,87 @@ def gh_get(path: str, query: dict[str, str] | None = None):
9294 return json .loads (result .stdout ) if result .stdout .strip () else {}
9395
9496
97+ def open_issue_if_missing (owner : str , repo : str , run : dict ) -> None :
98+ if find_workflow_issue_number (owner , repo , run ) is not None :
99+ return
100+ create_workflow_issue (owner , repo , run )
101+
102+
103+ def open_issue_or_add_comment (owner : str , repo : str , run : dict ) -> None :
104+ number = find_workflow_issue_number (owner , repo , run )
105+ if number is None :
106+ create_workflow_issue (owner , repo , run )
107+ else :
108+ subprocess .run (
109+ [
110+ "gh" ,
111+ "issue" ,
112+ "comment" ,
113+ str (number ),
114+ "--repo" ,
115+ f"{ owner } /{ repo } " ,
116+ "--body" ,
117+ workflow_issue_body (owner , repo , run ),
118+ ],
119+ check = True ,
120+ )
121+
122+
123+ def find_workflow_issue_number (owner : str , repo : str , run : dict ) -> int | None :
124+ title = workflow_issue_title (run )
125+ result = subprocess .run (
126+ [
127+ "gh" ,
128+ "issue" ,
129+ "list" ,
130+ "--repo" ,
131+ f"{ owner } /{ repo } " ,
132+ "--search" ,
133+ f"in:title { title } " ,
134+ "--limit" ,
135+ "20" ,
136+ "--json" ,
137+ "number,title" ,
138+ ],
139+ capture_output = True ,
140+ text = True ,
141+ check = True ,
142+ )
143+ for issue in json .loads (result .stdout ):
144+ issue_title = issue .get ("title" ) or ""
145+ if issue_title == title or issue_title .startswith (f"{ title } (#" ):
146+ return issue ["number" ]
147+ return None
148+
149+
150+ def create_workflow_issue (owner : str , repo : str , run : dict ) -> None :
151+ subprocess .run (
152+ [
153+ "gh" ,
154+ "issue" ,
155+ "create" ,
156+ "--repo" ,
157+ f"{ owner } /{ repo } " ,
158+ "--title" ,
159+ f"{ workflow_issue_title (run )} (#{ run ['run_number' ]} )" ,
160+ "--body" ,
161+ workflow_issue_body (owner , repo , run ),
162+ ],
163+ check = True ,
164+ )
165+
166+
167+ def workflow_issue_title (run : dict ) -> str :
168+ return f"Workflow failed: { run ['name' ]} "
169+
170+
171+ def workflow_issue_body (owner : str , repo : str , run : dict ) -> str :
172+ return (
173+ f"See [{ run ['name' ]} #{ run ['run_number' ]} ]"
174+ f"(https://github.com/{ owner } /{ repo } /actions/runs/{ run ['id' ]} )."
175+ )
176+
177+
95178def resolve_pr_number (owner : str , repo : str , run : dict ) -> int | None :
96179 pull_requests = run .get ("pull_requests" ) or []
97180 if pull_requests :
0 commit comments