Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/update-qpk-pin.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ on:
- "scripts/check_qpk_pin_consistency.py"
- "scripts/open_downstream_qpk_pin_prs.py"
- "scripts/merge_verified_strategy_qpk_pin_prs.py"
- "scripts/report_consumer_qpk_pin_prs.py"
- "tests/test_qpk_pin_consistency.py"
- "tests/test_merge_verified_strategy_qpk_pin_prs.py"
- "tests/test_report_consumer_qpk_pin_prs.py"
- "tests/test_update_qpk_pin_workflow.py"
- "docs/**"
- "**.md"
Expand Down
24 changes: 21 additions & 3 deletions scripts/report_consumer_qpk_pin_prs.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,29 @@ def generated_prs(repo: RepoSpec, *, env: dict[str, str]) -> list[dict[str, Any]
"--limit",
"100",
"--json",
"author,baseRefName,headRefName,isCrossRepository,isDraft,number,title,updatedAt,url",
"author,baseRefName,headRefName,isCrossRepository,isDraft,number,statusCheckRollup,title,updatedAt,url",
],
env=env,
)
return json.loads(result.stdout)


def ci_status(pr: dict[str, Any]) -> str:
"""Return a display-only CI state; never use it to mutate consumer PRs."""

checks = pr.get("statusCheckRollup")
if not isinstance(checks, list) or not checks:
return "MISSING"
if any(not isinstance(check, dict) or check.get("status") != "COMPLETED" for check in checks):
return "PENDING"
conclusions = {str(check.get("conclusion") or "").upper() for check in checks}
if conclusions == {"SUCCESS"}:
return "GREEN"
if conclusions & {"FAILURE", "TIMED_OUT", "CANCELLED", "ACTION_REQUIRED", "STARTUP_FAILURE"}:
return "FAILED"
return "NON_GREEN"


def classify_generated_prs(
prs: Iterable[dict[str, Any]],
*,
Expand All @@ -71,7 +87,9 @@ def classify_generated_prs(


def render_row(repo: RepoSpec, current: list[dict[str, Any]], stale: list[dict[str, Any]]) -> str:
current_refs = ", ".join(f"[#{item['number']}]({item['url']})" for item in current) or "—"
current_refs = ", ".join(
f"[#{item['number']}]({item['url']}) · {ci_status(item)}" for item in current
) or "—"
stale_refs = ", ".join(f"[#{item['number']}]({item['url']})" for item in stale) or "—"
return f"| {repo.name} | {current_refs} | {stale_refs} |"

Expand All @@ -93,7 +111,7 @@ def main() -> int:
print()
print("Consumer repositories are report-only: they are never auto-merged or auto-closed.")
print()
print("| Repository | Current generated PR | Recognizable stale generated PRs |")
print("| Repository | Current generated PR (CI) | Recognizable stale generated PRs |")
print("| --- | --- | --- |")
for repo in CONSUMER_REPOS:
current, stale = classify_generated_prs(
Expand Down
18 changes: 17 additions & 1 deletion tests/test_report_consumer_qpk_pin_prs.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from __future__ import annotations

from scripts.report_consumer_qpk_pin_prs import classify_generated_prs, render_row
from scripts.report_consumer_qpk_pin_prs import ci_status, classify_generated_prs, render_row
from scripts.open_downstream_qpk_pin_prs import RepoSpec


Expand All @@ -17,6 +17,7 @@ def _pr(*, branch: str, number: int = 1) -> dict[str, object]:
"number": number,
"title": "chore(deps): align QPK pin to 8378e939d932",
"url": f"https://example.test/pr/{number}",
"statusCheckRollup": [],
}


Expand Down Expand Up @@ -45,3 +46,18 @@ def test_render_row_includes_links_without_mutation_instruction() -> None:
assert "LongBridgePlatform" in row
assert "[#10](https://example.test/pr/10)" in row
assert "[#9](https://example.test/pr/9)" in row
assert "MISSING" in row


def test_ci_status_distinguishes_green_pending_failed_and_non_green() -> None:
pr = _pr(branch="auto/qpk-pin-sync-8378e939d932-longbridgeplatform")

assert ci_status(pr) == "MISSING"
pr["statusCheckRollup"] = [{"status": "IN_PROGRESS", "conclusion": ""}]
assert ci_status(pr) == "PENDING"
pr["statusCheckRollup"] = [{"status": "COMPLETED", "conclusion": "SUCCESS"}]
assert ci_status(pr) == "GREEN"
pr["statusCheckRollup"] = [{"status": "COMPLETED", "conclusion": "FAILURE"}]
assert ci_status(pr) == "FAILED"
pr["statusCheckRollup"] = [{"status": "COMPLETED", "conclusion": "SKIPPED"}]
assert ci_status(pr) == "NON_GREEN"
2 changes: 2 additions & 0 deletions tests/test_update_qpk_pin_workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,8 +238,10 @@ def test_dependency_success_reaches_only_guarded_pr_step(tmp_path: Path) -> None
assert ' - ".github/workflows/update-qpk-pin.yml"' in workflow
assert ' - "scripts/open_downstream_qpk_pin_prs.py"' in workflow
assert ' - "scripts/merge_verified_strategy_qpk_pin_prs.py"' in workflow
assert ' - "scripts/report_consumer_qpk_pin_prs.py"' in workflow
assert ' - "tests/test_qpk_pin_consistency.py"' in workflow
assert ' - "tests/test_merge_verified_strategy_qpk_pin_prs.py"' in workflow
assert ' - "tests/test_report_consumer_qpk_pin_prs.py"' in workflow
assert ' - "tests/test_update_qpk_pin_workflow.py"' in workflow
assert "workflow_dispatch:" not in workflow

Expand Down