test: blocked-check 用 _req_safe 替代 _req 避免 API 错误误判 - Closes #58
- 新增 _req_safe():API 错误返回 None 而非 sys.exit(1) - blocked_check / _unblock_issues_blocked_by / _get_blocking_refs 改用 _req_safe - API 失败时保守处理:保持 blocked 状态(不误解除) - 验证:#18 正确识别被 #57 阻塞 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+41
-23
@@ -56,6 +56,27 @@ def _req(method, path, data=None):
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
def _req_safe(method, path, data=None):
|
||||||
|
"""Like _req but returns None on HTTPError instead of crashing.
|
||||||
|
Used for probing issue/PR existence where the caller can handle absence.
|
||||||
|
"""
|
||||||
|
url = f"{BASE}{path}"
|
||||||
|
payload = json.dumps(data).encode("utf-8") if data else None
|
||||||
|
req = urllib.request.Request(url, data=payload, method=method)
|
||||||
|
req.add_header("Authorization", f"token {GITEA_TOKEN}")
|
||||||
|
req.add_header("Content-Type", "application/json")
|
||||||
|
try:
|
||||||
|
with urllib.request.urlopen(req) as resp:
|
||||||
|
raw = resp.read()
|
||||||
|
if not raw:
|
||||||
|
return {}
|
||||||
|
return json.loads(raw)
|
||||||
|
except urllib.error.HTTPError as e:
|
||||||
|
body = e.read().decode()
|
||||||
|
print(f"API Error {e.code}: {body}", file=sys.stderr)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
# ── Issue operations ─────────────────────────────────────────────────────────
|
# ── Issue operations ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
def list_issues(labels: list[str] | None = None):
|
def list_issues(labels: list[str] | None = None):
|
||||||
@@ -82,17 +103,17 @@ def _get_blocking_refs(issue_num: int) -> set[int]:
|
|||||||
"""
|
"""
|
||||||
refs: set[int] = set()
|
refs: set[int] = set()
|
||||||
# Body
|
# Body
|
||||||
issue = _req("GET", f"/issues/{issue_num}")
|
issue = _req_safe("GET", f"/issues/{issue_num}")
|
||||||
|
if issue is None:
|
||||||
|
return refs # API error → return empty set, keep blocked
|
||||||
body = issue.get("body", "") or ""
|
body = issue.get("body", "") or ""
|
||||||
refs.update(int(m.group(1)) for m in re.finditer(r'#(\d+)', body))
|
refs.update(int(m.group(1)) for m in re.finditer(r'#(\d+)', body))
|
||||||
# Comments
|
# Comments
|
||||||
try:
|
comments = _req_safe("GET", f"/issues/{issue_num}/comments")
|
||||||
comments = _req("GET", f"/issues/{issue_num}/comments")
|
if comments:
|
||||||
for c in comments:
|
for c in comments:
|
||||||
cbody = c.get("body", "") or ""
|
cbody = c.get("body", "") or ""
|
||||||
refs.update(int(m.group(1)) for m in re.finditer(r'#(\d+)', cbody))
|
refs.update(int(m.group(1)) for m in re.finditer(r'#(\d+)', cbody))
|
||||||
except SystemExit:
|
|
||||||
pass
|
|
||||||
return refs
|
return refs
|
||||||
|
|
||||||
|
|
||||||
@@ -119,13 +140,13 @@ def blocked_check():
|
|||||||
|
|
||||||
all_resolved = True
|
all_resolved = True
|
||||||
for blk in blocking_nums:
|
for blk in blocking_nums:
|
||||||
try:
|
blk_issue = _req_safe("GET", f"/issues/{blk}")
|
||||||
blk_issue = _req("GET", f"/issues/{blk}")
|
if blk_issue is None:
|
||||||
if blk_issue.get("state") != "closed":
|
all_resolved = False # API error → keep blocked
|
||||||
all_resolved = False
|
break
|
||||||
break
|
if blk_issue.get("state") != "closed":
|
||||||
except SystemExit:
|
all_resolved = False
|
||||||
pass
|
break
|
||||||
|
|
||||||
if all_resolved:
|
if all_resolved:
|
||||||
current_label_names = [l["name"] for l in issue.get("labels", [])]
|
current_label_names = [l["name"] for l in issue.get("labels", [])]
|
||||||
@@ -179,10 +200,7 @@ def _unblock_issues_blocked_by(closed_num):
|
|||||||
in any blocked issue and all referenced issues are now closed,
|
in any blocked issue and all referenced issues are now closed,
|
||||||
removes the 'blocked' label and comments on the unblocked issue.
|
removes the 'blocked' label and comments on the unblocked issue.
|
||||||
"""
|
"""
|
||||||
try:
|
all_blocked = _req_safe("GET", "/issues?state=open&labels=blocked")
|
||||||
all_blocked = _req("GET", "/issues?state=open&labels=blocked")
|
|
||||||
except SystemExit:
|
|
||||||
return
|
|
||||||
if not all_blocked:
|
if not all_blocked:
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -196,13 +214,13 @@ def _unblock_issues_blocked_by(closed_num):
|
|||||||
for blk in blocking_nums:
|
for blk in blocking_nums:
|
||||||
if blk == closed_num:
|
if blk == closed_num:
|
||||||
continue
|
continue
|
||||||
try:
|
blk_issue = _req_safe("GET", f"/issues/{blk}")
|
||||||
blk_issue = _req("GET", f"/issues/{blk}")
|
if blk_issue is None:
|
||||||
if blk_issue.get("state") != "closed":
|
all_resolved = False # API error → keep blocked
|
||||||
all_resolved = False
|
break
|
||||||
break
|
if blk_issue.get("state") != "closed":
|
||||||
except SystemExit:
|
all_resolved = False
|
||||||
pass # Inaccessible → treat as resolved
|
break
|
||||||
|
|
||||||
if all_resolved:
|
if all_resolved:
|
||||||
current_label_names = [l["name"] for l in issue.get("labels", [])]
|
current_label_names = [l["name"] for l in issue.get("labels", [])]
|
||||||
|
|||||||
Reference in New Issue
Block a user