@@ -33,12 +33,18 @@ def env(name: str, default: str = "") -> str:
3333
3434
3535def env_int (name : str , default : int ) -> int :
36- try : return int (env (name , str (default )))
37- except ValueError : return default
36+ try :
37+ return int (env (name , str (default )))
38+ except ValueError :
39+ return default
3840
3941
40- def github_request (token : str , method : str , path : str ,
41- payload : dict [str , Any ] | None = None ) -> Any :
42+ def github_request (
43+ token : str ,
44+ method : str ,
45+ path : str ,
46+ payload : dict [str , Any ] | None = None ,
47+ ) -> Any :
4248 url = f"{ API_BASE } { path } " if not path .startswith ("https://" ) else path
4349 data = json .dumps (payload ).encode () if payload else None
4450 headers = {
@@ -47,7 +53,8 @@ def github_request(token: str, method: str, path: str,
4753 "X-GitHub-Api-Version" : "2022-11-28" ,
4854 "User-Agent" : "codex-review-gate" ,
4955 }
50- if payload : headers ["Content-Type" ] = "application/json"
56+ if payload :
57+ headers ["Content-Type" ] = "application/json"
5158 req = urllib .request .Request (url , data = data , method = method , headers = headers )
5259 try :
5360 with urllib .request .urlopen (req , timeout = 30 ) as resp :
@@ -69,8 +76,10 @@ def step_summary(text: str) -> None:
6976
7077def load_policy () -> dict [str , Any ]:
7178 if POLICY_PATH .exists ():
72- try : return json .loads (POLICY_PATH .read_text (encoding = "utf-8" ))
73- except (OSError , json .JSONDecodeError ): pass
79+ try :
80+ return json .loads (POLICY_PATH .read_text (encoding = "utf-8" ))
81+ except (OSError , json .JSONDecodeError ):
82+ pass
7483 return {
7584 "version" : 1 ,
7685 "blocked_path_patterns" : [
@@ -85,8 +94,10 @@ def compile_patterns(policy: dict[str, Any]) -> list[re.Pattern[str]]:
8594 pp : list [re .Pattern [str ]] = []
8695 for p in policy .get ("blocked_path_patterns" , []):
8796 if isinstance (p , str ) and p .strip ():
88- try : pp .append (re .compile (p , re .IGNORECASE ))
89- except re .error : pass
97+ try :
98+ pp .append (re .compile (p , re .IGNORECASE ))
99+ except re .error :
100+ pass
90101 return pp
91102
92103
@@ -111,8 +122,11 @@ def scan_diff(diff_text: str, path_patterns: list[re.Pattern[str]]) -> list[str]
111122 violations .append (f"**Blocked file**: `{ current } ` matches `{ pat .pattern } `" )
112123 break
113124 continue
114- if line .startswith ("+++ b/" ): current = line [6 :]; continue
115- if not line .startswith ("+" ) or line .startswith ("+++" ): continue
125+ if line .startswith ("+++ b/" ):
126+ current = line [6 :]
127+ continue
128+ if not line .startswith ("+" ) or line .startswith ("+++" ):
129+ continue
116130 m = _SENSITIVE .search (line [1 :])
117131 if m :
118132 violations .append (f"**Hardcoded secret** in `{ current } `: `{ m .group (0 )[:100 ]} `" )
@@ -128,8 +142,10 @@ def check_metadata(files: list[dict[str, Any]], policy: dict[str, Any]) -> list[
128142 for f in files :
129143 fn = f .get ("filename" , "?" )
130144 st = (f .get ("status" ) or "" ).lower ().strip ()
131- if st == "removed" : issues .append (f"**File deleted**: `{ fn } ` — verify intentional" )
132- elif st == "renamed" : issues .append (f"**File renamed**: `{ f .get ('previous_filename' , '?' )} ` → `{ fn } `" )
145+ if st == "removed" :
146+ issues .append (f"**File deleted**: `{ fn } ` — verify intentional" )
147+ elif st == "renamed" :
148+ issues .append (f"**File renamed**: `{ f .get ('previous_filename' , '?' )} ` → `{ fn } `" )
133149 if len (files ) > mx_f :
134150 issues .append (f"**Too many files**: { len (files )} changed (limit { mx_f } )" )
135151 if ta + td > mx_l :
@@ -144,12 +160,18 @@ def run_static_guard(token: str, repo: str, pr_number: int) -> int:
144160 page = 1
145161 while True :
146162 try :
147- batch = github_request (token , "GET" ,
148- f"/repos/{ repo } /pulls/{ pr_number } /files?per_page=100&page={ page } " )
149- except RuntimeError : break
150- if not isinstance (batch , list ) or not batch : break
163+ batch = github_request (
164+ token ,
165+ "GET" ,
166+ f"/repos/{ repo } /pulls/{ pr_number } /files?per_page=100&page={ page } " ,
167+ )
168+ except RuntimeError :
169+ break
170+ if not isinstance (batch , list ) or not batch :
171+ break
151172 files .extend (batch )
152- if len (batch ) < 100 : break
173+ if len (batch ) < 100 :
174+ break
153175 page += 1
154176
155177 diff_text = ""
@@ -165,23 +187,29 @@ def run_static_guard(token: str, repo: str, pr_number: int) -> int:
165187 )
166188 with urllib .request .urlopen (req , timeout = 30 ) as resp :
167189 diff_text = resp .read ().decode ("utf-8" , errors = "replace" )
168- except Exception : pass
190+ except Exception :
191+ pass
169192
170193 issues = check_metadata (files , policy ) + scan_diff (diff_text , compile_patterns (policy ))
171- if not issues : return 0
194+ if not issues :
195+ return 0
172196
173197 print (f"STATIC → BLOCKED: { len (issues )} issue(s)" )
174- for i in issues : print (f" • { i } " )
175- step_summary (f"## Merge blocked: { len (issues )} static issue(s)\n \n " +
176- "\n " .join (f"- { i } " for i in issues ))
198+ for i in issues :
199+ print (f" • { i } " )
200+ step_summary (
201+ f"## Merge blocked: { len (issues )} static issue(s)\n \n "
202+ + "\n " .join (f"- { i } " for i in issues )
203+ )
177204 return 1
178205
179206
180207# ─── app review ──────────────────────────────────────────────────────────────
181208
182209def get_codex_review (token : str , repo : str , pr_number : int ) -> dict [str , Any ] | None :
183210 reviews = github_request (token , "GET" , f"/repos/{ repo } /pulls/{ pr_number } /reviews?per_page=100" )
184- if not isinstance (reviews , list ): return None
211+ if not isinstance (reviews , list ):
212+ return None
185213 for r in reversed (reviews ):
186214 if isinstance (r , dict ) and (r .get ("user" ) or {}).get ("login" ) == BOT_LOGIN :
187215 return r
@@ -228,16 +256,20 @@ def main() -> int:
228256 pr_number = pr .get ("number" )
229257 head_sha = (pr .get ("head" ) or {}).get ("sha" )
230258 if not pr_number or not head_sha :
231- print (f"::warning::Cannot resolve PR context" ); return 0
259+ print ("::warning::Cannot resolve PR context" )
260+ return 0
232261
233262 print (f"PR #{ pr_number } sha={ head_sha [:12 ]} event={ event_name } " )
234263
235264 # ── Phase 1: Static guard (skip on review-only events) ────────────
236265 if event_name != "pull_request_review" :
237- try : rc = run_static_guard (token , repo , pr_number )
266+ try :
267+ rc = run_static_guard (token , repo , pr_number )
238268 except RuntimeError as exc :
239- print (f"::warning::Static guard error: { exc } " ); rc = 0
240- if rc != 0 : return 1
269+ print (f"::warning::Static guard error: { exc } " )
270+ rc = 0
271+ if rc != 0 :
272+ return 1
241273 print ("STATIC → clean" )
242274
243275 # ── Phase 2: App review ───────────────────────────────────────────
@@ -250,8 +282,10 @@ def main() -> int:
250282 return rc
251283
252284 # WAIT: poll for existing or upcoming review
253- try : existing = get_codex_review (token , repo , pr_number )
254- except RuntimeError : existing = None
285+ try :
286+ existing = get_codex_review (token , repo , pr_number )
287+ except RuntimeError :
288+ existing = None
255289
256290 if existing is not None :
257291 rc , title , summary = app_decision (existing )
@@ -266,8 +300,10 @@ def main() -> int:
266300
267301 while time .time () < deadline :
268302 time .sleep (poll_s )
269- try : review = get_codex_review (token , repo , pr_number )
270- except RuntimeError : continue
303+ try :
304+ review = get_codex_review (token , repo , pr_number )
305+ except RuntimeError :
306+ continue
271307 if review is not None :
272308 rc , title , summary = app_decision (review )
273309 print (f"WAIT → found review → exit={ rc } : { title } " )
0 commit comments