Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| eccaa28b1d | |||
| d73da7cda9 |
+42
-29
@@ -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
|
||||||
|
|
||||||
|
|
||||||
@@ -103,12 +124,7 @@ def blocked_check():
|
|||||||
If no references found or all referenced issues are closed,
|
If no references found or all referenced issues are closed,
|
||||||
removes the 'blocked' label.
|
removes the 'blocked' label.
|
||||||
"""
|
"""
|
||||||
try:
|
all_blocked = _req_safe("GET", "/issues?state=open&labels=blocked")
|
||||||
all_blocked = _req("GET", "/issues?state=open&labels=blocked")
|
|
||||||
except SystemExit:
|
|
||||||
print("No blocked issues found.")
|
|
||||||
return
|
|
||||||
|
|
||||||
if not all_blocked:
|
if not all_blocked:
|
||||||
print("No blocked issues found.")
|
print("No blocked issues found.")
|
||||||
return
|
return
|
||||||
@@ -119,13 +135,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 +195,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 +209,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