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
14 changes: 9 additions & 5 deletions src/generate_repo_overview/_policy_sync_html.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,14 @@ def render_policy_sync_section(
' <div class="policy-sync-stat-heading">Evaluation status</div>\n'
' <div class="policy-sync-stat-list">\n'
+ _policy_stat(summary.compliant, "Compliant", "compliant", "✓")
+ _policy_stat(summary.drifted, "Changes Needed", "changes-required", "!")
+ _policy_stat(summary.drifted, "Changes Needed", "changes-required", "X")
+ _policy_stat(
summary.not_applicable, "Not Applicable", "not-applicable", "N/A"
)
+ _optional_policy_stat(
summary.evaluation_failures, "Evaluation Errors", "error", "!"
summary.evaluation_failures, "Evaluation Errors", "error", "X"
)
+ _optional_policy_stat(summary.sync_failures, "Sync Failures", "error", "!")
+ _optional_policy_stat(summary.sync_failures, "Sync Failures", "error", "X")
+ _optional_policy_stat(summary.skipped, "Skipped", "not-evaluated", "—")
+ " </div>\n"
" </div>\n"
Expand Down Expand Up @@ -254,6 +254,10 @@ def _matrix_cell(outcome: PolicySyncOutcome | None) -> str:
if automated:
label = "Compliant (automated PR)"
marker = "✓✓"
if status_class == "changes-required" and outcome.policy_pr_status == "open":
link = _safe_external_url(outcome.pull_request_url or "")
if link:
return _policy_pr_badge(outcome.policy_pr_status, href=link)
content = (
f'<span class="policy-status {status_class}" title="{e(label)}" '
f'aria-label="{e(label)}">{marker}</span>'
Expand Down Expand Up @@ -306,13 +310,13 @@ def _status_display(outcome: PolicySyncOutcome) -> tuple[str, str, str]:
"pull-request-recreated",
"pull-request-recreated-no-changes",
}:
return "changes-required", "Changes required", "!"
return "changes-required", "Changes required", "X"
if outcome.status == "not-applicable":
return "not-applicable", "Not applicable", "N/A"
if outcome.status in {"skipped", "sync-error"}:
return "not-evaluated", "Not evaluated", "—"
if outcome.status == "error":
return "error", "Error", "!"
return "error", "Error", "X"
return "unknown", outcome.status or "Unknown", "?"


Expand Down
5 changes: 3 additions & 2 deletions src/generate_repo_overview/templates/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -504,7 +504,8 @@ th[data-tooltip]:focus::after, td[data-tooltip]:focus::after { opacity: 1; }
.policy-status.compliant { color: var(--green); background: #23863633; }
.policy-status.changes-required { color: var(--red); background: #da363333; }
.policy-status.not-applicable, .policy-status.not-evaluated { color: var(--muted); background: #8b949e22; }
.policy-status.error, .policy-status.unknown { color: var(--orange); background: #d2992233; }
.policy-status.error { color: var(--red); background: #da363333; }
.policy-status.unknown { color: var(--orange); background: #d2992233; }
.policy-pr-badge {
display: inline-flex;
align-items: center;
Expand All @@ -518,7 +519,7 @@ th[data-tooltip]:focus::after, td[data-tooltip]:focus::after { opacity: 1; }
}
.policy-pr-badge:hover { text-decoration: none; filter: brightness(1.15); }
.policy-pr-badge svg { width: 12px; height: 12px; }
.policy-pr-open { color: #3fb950; background: #2ea04333; border-color: #2ea04399; }
.policy-pr-open { color: var(--orange); background: #e3702d33; border-color: #e3702d99; }
.policy-pr-merged { color: #a371f7; background: #8250df33; border-color: #8250df99; }
.policy-pr-closed { color: #8b949e; background: #6e778133; border-color: #6e778199; }
.policy-pr-none, .policy-pr-unknown { color: var(--muted); background: #8b949e22; border-color: #8b949e66; }
Expand Down
36 changes: 35 additions & 1 deletion tests/test_policy_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ def test_policy_sync_tab_uses_repository_groups_and_pr_states() -> None:
'<span class="policy-status compliant" aria-label="Compliant">✓</span>' in page
)
assert (
'<span class="policy-status changes-required" aria-label="Changes Needed">!</span>'
'<span class="policy-status changes-required" aria-label="Changes Needed">X</span>'
in page
)
assert (
Expand All @@ -371,6 +371,11 @@ def test_policy_sync_tab_uses_repository_groups_and_pr_states() -> None:
assert "Actions in this run" not in page
assert 'class="policy-pr-badge policy-pr-open"' in page
assert ">Open</a>" in page
assert (
'<span class="policy-status changes-required" title="Changes required" aria-label="Changes required">X</span> <a href="https://github.com/org/tools/pull/1"'
not in page
)
assert ".policy-pr-open { color: var(--orange);" in page
assert (
'title="Compliant (automated PR)" aria-label="Compliant (automated PR)">✓✓</span>'
in page
Expand All @@ -382,6 +387,35 @@ def test_policy_sync_tab_uses_repository_groups_and_pr_states() -> None:
assert 'href="https://github.com/org/score/pull/3"' not in page


def test_policy_sync_error_outcomes_render_red_x() -> None:
"""Error outcomes use a red X in both the summary and matrix."""
report = PolicySyncReport(
schema_version=2,
summary=PolicySyncSummary(evaluation_failures=1),
outcomes=(
PolicySyncOutcome(
policy_id="policy-error",
repository="repo-error",
applicable="yes",
status="error",
error="evaluation failed",
),
),
)

page = render_index_page(_minimal_snapshot(), report)

assert (
'<span class="policy-status error" aria-label="Evaluation Errors">X</span>'
in page
)
assert (
'<span class="policy-status error" title="Error" aria-label="Error">X</span>'
in page
)
assert ".policy-status.error { color: var(--red);" in page


def test_render_details_discovers_configured_report_and_publishes_raw_json(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
Expand Down