Skip to content

Instantly share code, notes, and snippets.

@stormslowly
Created June 23, 2025 10:45
Show Gist options
  • Save stormslowly/d4691ed42c84c169f8099f402b0a3251 to your computer and use it in GitHub Desktop.
Save stormslowly/d4691ed42c84c169f8099f402b0a3251 to your computer and use it in GitHub Desktop.
extended git command to show PR checks status
#!/usr/bin/env python3
import subprocess
import json
import sys
import os
from collections import defaultdict
def run_cmd(cmd, capture=True):
result = subprocess.run(cmd, shell=True, capture_output=capture, text=True)
if result.returncode != 0:
return None
return result.stdout.strip() if capture else None
def get_current_branch():
return run_cmd("git rev-parse --abbrev-ref HEAD")
def get_pr_info(branch):
out = run_cmd(f'gh pr view {branch} --json url,statusCheckRollup')
return json.loads(out) if out else None
def get_jobs_summary(run_url):
run_id = run_url.rstrip('/').split('/')[-1]
out = run_cmd(f'gh run view {run_id} --json jobs')
if not out:
return None, None
jobs = json.loads(out).get("jobs", [])
total = len(jobs)
succeeded = sum(1 for j in jobs if j.get("conclusion") == "success")
return succeeded, total
def get_status_emoji(status, conclusion, run_url):
if status == "COMPLETED":
return {
"SUCCESS": "✅",
"FAILURE": "❌",
"CANCELLED": "🚫",
"TIMED_OUT": "⏰",
"SKIPPED": "⏭️",
"NEUTRAL": "🟡",
}.get(conclusion, "❓")
else:
succeeded, total = get_jobs_summary(run_url)
if succeeded is not None:
return f"🕐 [{succeeded}/{total}]"
return "⌛️"
def check_sort_key(check):
status_priority = {
"FAILURE": 0,
"SUCCESS": 1,
"TIMED_OUT": 2,
"SKIPPED": 3,
}
# 取结论,转大写,优先级默认 99
conclusion = (check.get("conclusion") or "").upper()
priority = status_priority.get(conclusion, 99)
return (priority, check["name"])
def main():
branch = get_current_branch()
if not branch:
print("❌ 无法获取当前分支名")
sys.exit(1)
pr = get_pr_info(branch)
if not pr or not pr.get("statusCheckRollup"):
print(f"❌ 当前分支 '{branch}' 没有关联的 PR 或没有 status check")
sys.exit(1)
print(f"🔗 PR: {pr['url']}")
print(f"🔍 Workflow 状态(分支:{branch}):\n")
checks = pr["statusCheckRollup"]
# 分成两类:没有 workflowName(即 github-actions) 和 有 workflowName(其他平台 or 多 run)
no_workflow = []
workflows = defaultdict(list)
for check in checks:
name = check.get("name", "Unknown")
status = check.get("status", "UNKNOWN")
conclusion = check.get("conclusion", "-")
url = check.get("targetUrl", "")
workflow = check.get("workflowName")
started_at = check.get("startedAt") or ""
check_item = {
"name": name,
"status": status,
"conclusion": conclusion,
"url": url,
"startedAt": started_at,
}
if not workflow: # GitHub Actions 单独列出
no_workflow.append(check_item)
else:
workflows[workflow].append(check_item)
# # 输出 github-actions 类型
# if no_workflow:
# print("📦 GitHub Actions checks:\n")
# for check in no_workflow:
# emoji = get_status_emoji(check["status"], check["conclusion"], check["url"])
# print(f"{check['name']:<30} {emoji} {check['url']}")
# print()
# 输出其他 workflow 分组
for workflow_name in sorted(workflows):
print(f"🔧 Workflow: {workflow_name}")
# 按 startedAt 倒序
group = sorted(workflows[workflow_name], key=check_sort_key)
for check in group:
emoji = get_status_emoji(check["status"], check["conclusion"], check["url"])
print(f" {emoji} {check['name']:<28} {check['url']}")
print()
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment