@@ -154,27 +154,88 @@ def resolve_tag_and_commit(
154154 return "" , "" , "Either tag or commit_hash must be provided"
155155
156156
157+ # IGNORED_STATUS_CONTEXTS lists legacy commit-status contexts that should not
158+ # block a release build even when failing. Keep the set tiny and well-justified.
159+ #
160+ # verification/cla-signed: cla-bot fails on the upstream firecracker fork
161+ # whenever a backport branch carries commits authored by upstream maintainers
162+ # we don't have a CLA for (e.g. ilstam, ShadowCurse, JackThomson2). Those
163+ # contributors won't ever sign our CLA, so the status is permanently red on
164+ # every direct-mem / hint backport branch — we still want to ship those builds.
165+ IGNORED_STATUS_CONTEXTS = frozenset ({"verification/cla-signed" })
166+
167+ # IGNORED_CHECK_NAMES is the equivalent for the Checks API (apps that file a
168+ # check-run rather than a legacy status). Empty today; mirror IGNORED_STATUS_CONTEXTS
169+ # if a check-run-based bot ever ends up in the same situation.
170+ IGNORED_CHECK_NAMES = frozenset ()
171+
172+
173+ def _rollup_status (statuses : list [dict ]) -> tuple [str , int ]:
174+ """Compute (state, count) over the statuses list, mirroring how GitHub's
175+ combined-status endpoint rolls up: any failure → failure, else any pending
176+ → pending, else any success → success, else unknown.
177+ """
178+ if not statuses :
179+ return "unknown" , 0
180+ states = {s .get ("state" ) for s in statuses }
181+ if "failure" in states or "error" in states :
182+ return "failure" , len (statuses )
183+ if "pending" in states :
184+ return "pending" , len (statuses )
185+ if "success" in states :
186+ return "success" , len (statuses )
187+ return "unknown" , len (statuses )
188+
189+
157190def check_ci_status (commit_hash : str , repo : str = "e2b-dev/firecracker" ) -> tuple [bool , str ]:
158191 """
159192 Check CI status for a commit.
160193
161194 Returns (success, message).
162195 """
163- # Check commit status API
196+ # Check commit status API. Filter out IGNORED_STATUS_CONTEXTS and recompute
197+ # the rollup so a single permanently-red status (e.g. cla-bot on
198+ # external-contributor backport branches) doesn't block release builds.
164199 status_response = gh_api (f"/repos/{ repo } /commits/{ commit_hash } /status" )
165200 if not status_response :
166- status_response = {"state" : "unknown" , "total_count" : 0 }
167-
168- status = status_response .get ("state" , "unknown" )
169- status_count = status_response .get ("total_count" , 0 )
201+ status_response = {"state" : "unknown" , "total_count" : 0 , "statuses" : []}
202+
203+ raw_statuses = status_response .get ("statuses" , []) or []
204+ ignored_status_contexts = [
205+ s .get ("context" ) for s in raw_statuses
206+ if s .get ("context" ) in IGNORED_STATUS_CONTEXTS
207+ ]
208+ filtered_statuses = [
209+ s for s in raw_statuses
210+ if s .get ("context" ) not in IGNORED_STATUS_CONTEXTS
211+ ]
212+ if ignored_status_contexts :
213+ status , status_count = _rollup_status (filtered_statuses )
214+ print (
215+ f"Status API: ignoring contexts { sorted (set (ignored_status_contexts ))} "
216+ f"→ rollup state={ status } , count={ status_count } " ,
217+ file = sys .stderr ,
218+ )
219+ else :
220+ status = status_response .get ("state" , "unknown" )
221+ status_count = status_response .get ("total_count" , 0 )
222+ print (f"Status API: state={ status } , count={ status_count } " , file = sys .stderr )
170223
171- # Check check-runs API
224+ # Check check-runs API. Same filter for IGNORED_CHECK_NAMES.
172225 check_response = gh_api (f"/repos/{ repo } /commits/{ commit_hash } /check-runs" )
173226 if not check_response :
174227 check_response = {"total_count" : 0 , "check_runs" : []}
175228
176- check_count = check_response .get ("total_count" , 0 )
177- check_runs = check_response .get ("check_runs" , [])
229+ raw_check_runs = check_response .get ("check_runs" , []) or []
230+ ignored_check_names = [
231+ cr .get ("name" ) for cr in raw_check_runs
232+ if cr .get ("name" ) in IGNORED_CHECK_NAMES
233+ ]
234+ check_runs = [
235+ cr for cr in raw_check_runs
236+ if cr .get ("name" ) not in IGNORED_CHECK_NAMES
237+ ]
238+ check_count = len (check_runs )
178239
179240 # Determine check conclusion
180241 if check_count == 0 :
@@ -188,8 +249,14 @@ def check_ci_status(commit_hash: str, repo: str = "e2b-dev/firecracker") -> tupl
188249 else :
189250 check_conclusion = "unknown"
190251
191- print (f"Status API: state={ status } , count={ status_count } " , file = sys .stderr )
192- print (f"Check-runs API: conclusion={ check_conclusion } , count={ check_count } " , file = sys .stderr )
252+ if ignored_check_names :
253+ print (
254+ f"Check-runs API: ignoring { sorted (set (ignored_check_names ))} "
255+ f"→ conclusion={ check_conclusion } , count={ check_count } " ,
256+ file = sys .stderr ,
257+ )
258+ else :
259+ print (f"Check-runs API: conclusion={ check_conclusion } , count={ check_count } " , file = sys .stderr )
193260
194261 if status == "failure" or check_conclusion == "failure" :
195262 return False , f"CI failed for commit { commit_hash } - refusing to build"
0 commit comments