mirror of
https://github.com/headroomlabs-ai/headroom.git
synced 2026-08-27 14:17:10 -04:00
## Description Fix the PR health label job so `status: ci failing` reflects the latest check attempt for each check, not historical failed or cancelled attempts that still appear in `statusCheckRollup`. This showed up on #984: the current checks were green, but the label job kept `status: ci failing` because older failed template runs were still present in the rollup payload. ## Type of Change - [x] Bug fix (non-breaking change that fixes an issue) - [ ] New feature (non-breaking change that adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Documentation update - [ ] Performance improvement - [ ] Code refactoring (no functional changes) ## Changes Made - Added a small `.github/scripts/pr-health-labels.py` helper that groups check-rollup entries by logical check name and evaluates only the newest entry for each check. - Updated the PR health workflow label job to call the helper instead of treating any historical failing rollup entry as current failure. - Added regression tests for historical failures followed by latest passing attempts, plus current latest failure behavior. ## Testing - [x] Unit tests pass (`pytest`) - [x] New tests added for new functionality - [x] Manual testing performed ### Test Output ```text PYTEST_ADDOPTS='-p no:cacheprovider' pytest scripts/tests -q 47 passed, 1 warning in 0.39s python .github/scripts/pr-health-labels.py --state-json '<payload with old FAILURE and latest SUCCESS>' passing data=$(gh pr view 984 --repo chopratejas/headroom --json statusCheckRollup) python .github/scripts/pr-health-labels.py --state-json "$data" passing ``` ## Real Behavior Proof - Environment: macOS local checkout, Python 3.11.7, live GitHub PR #984 check-rollup payload fetched with `gh pr view`. - Exact command / steps: Added regression coverage for historical failed/cancelled check runs followed by latest successful runs, ran the scripts test suite, and evaluated live PR #984's `statusCheckRollup` with the new helper. - Observed result: The helper returns `passing` for #984's live payload even though older failed/cancelled check runs are still present, while still returning `failing` when the latest attempt for a check failed. - Not tested: A full GitHub Actions run of the updated workflow on upstream before merge; this PR should exercise the workflow on itself. ## Review Readiness - [x] I have performed a self-review - [x] This PR is ready for human review
79 lines
2.4 KiB
Python
79 lines
2.4 KiB
Python
#!/usr/bin/env python3
|
|
"""Helpers for PR health maintenance labels."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
import sys
|
|
from datetime import datetime, timezone
|
|
from typing import Any
|
|
|
|
FAILING_STATES = {"FAILURE", "TIMED_OUT", "ACTION_REQUIRED", "CANCELLED", "ERROR"}
|
|
|
|
|
|
def _parse_timestamp(value: Any) -> datetime:
|
|
if not isinstance(value, str) or not value:
|
|
return datetime.min.replace(tzinfo=timezone.utc)
|
|
normalized = value.removesuffix("Z") + "+00:00" if value.endswith("Z") else value
|
|
try:
|
|
parsed = datetime.fromisoformat(normalized)
|
|
except ValueError:
|
|
return datetime.min.replace(tzinfo=timezone.utc)
|
|
if parsed.tzinfo is None:
|
|
return parsed.replace(tzinfo=timezone.utc)
|
|
return parsed
|
|
|
|
|
|
def _check_key(check: dict[str, Any]) -> tuple[str, str]:
|
|
workflow = str(check.get("workflowName") or check.get("workflow") or "")
|
|
name = str(check.get("name") or check.get("context") or "")
|
|
return workflow, name
|
|
|
|
|
|
def _check_time(check: dict[str, Any]) -> datetime:
|
|
return max(
|
|
_parse_timestamp(check.get("startedAt")),
|
|
_parse_timestamp(check.get("completedAt")),
|
|
)
|
|
|
|
|
|
def _state(check: dict[str, Any]) -> str:
|
|
return str(check.get("conclusion") or check.get("state") or "").upper()
|
|
|
|
|
|
def current_checks(payload: dict[str, Any]) -> list[dict[str, Any]]:
|
|
latest_by_key: dict[tuple[str, str], dict[str, Any]] = {}
|
|
for check in payload.get("statusCheckRollup") or []:
|
|
if not isinstance(check, dict):
|
|
continue
|
|
key = _check_key(check)
|
|
if not any(key):
|
|
continue
|
|
previous = latest_by_key.get(key)
|
|
if previous is None or _check_time(check) >= _check_time(previous):
|
|
latest_by_key[key] = check
|
|
return list(latest_by_key.values())
|
|
|
|
|
|
def check_state(payload: dict[str, Any]) -> str:
|
|
for check in current_checks(payload):
|
|
if _state(check) in FAILING_STATES:
|
|
return "failing"
|
|
return "passing"
|
|
|
|
|
|
def parse_args(argv: list[str]) -> argparse.Namespace:
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("--state-json", required=True, help="JSON from gh pr view")
|
|
return parser.parse_args(argv)
|
|
|
|
|
|
def main(argv: list[str] | None = None) -> int:
|
|
args = parse_args(argv or sys.argv[1:])
|
|
print(check_state(json.loads(args.state_json)))
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|