diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 1df4abb..6f6c95f 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -24,6 +24,25 @@ jobs: extension_config_path: config/extension.json test_contract_path: config/test-contract.json + # Stable branch-protection context independent of PG14/PG19 matrix churn. + # This result requires the ENTIRE maintained-major reusable Windows build. + # No Release/dispatch/publish operations occur in this job. + windows_required: + name: Windows required summary + if: ${{ always() && !startsWith(github.ref, 'refs/heads/release/') }} + needs: windows + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: Test required Windows aggregate allow/deny contract + run: python3 tests/test_required_windows_summary.py + - name: Require full maintained-PG Windows matrix success + env: + WINDOWS_MATRIX_RESULT: ${{ needs.windows.result }} + run: python3 scripts/required_windows_summary.py + candidate_report: name: Verify candidate Windows matrix and immutable artifacts if: ${{ always() && github.event_name == 'workflow_dispatch' && startsWith(github.ref, 'refs/heads/auto-candidate/') }} diff --git a/scripts/required_windows_summary.py b/scripts/required_windows_summary.py new file mode 100644 index 0000000..f4b2c36 --- /dev/null +++ b/scripts/required_windows_summary.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +"""Fail-closed, fixed-name Windows required CI result for maintained-PG matrix. + +Never publishes or dispatches. Relies only on GitHub Actions 'needs.windows.result' +for the COMPLETE reusable Windows matrix (not on volatile per-PG job names). +""" +from __future__ import annotations +import os +import sys + + +def verified_matrix_result(value): + return type(value) is str and value == "success" + + +def main(): + if not verified_matrix_result(os.environ.get("WINDOWS_MATRIX_RESULT")): + print("WINDOWS REQUIRED SUMMARY DENIED: full maintained-PG Windows matrix " + "was not successful", file=sys.stderr) + return 2 + print("WINDOWS REQUIRED SUMMARY VERIFIED: full maintained-PG Windows matrix " + "completed successfully") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_required_windows_summary.py b/tests/test_required_windows_summary.py new file mode 100644 index 0000000..088ed92 --- /dev/null +++ b/tests/test_required_windows_summary.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +"""Test the exact stable aggregate check used by Windows CI, without Release.""" +import importlib.util +from pathlib import Path +import os +import subprocess +import sys +import unittest + +ROOT = Path(__file__).resolve().parents[1] +spec = importlib.util.spec_from_file_location( + "summary", ROOT / "scripts" / "required_windows_summary.py") +module = importlib.util.module_from_spec(spec) +spec.loader.exec_module(module) + + +class WindowsSummaryTests(unittest.TestCase): + def test_success_only(self): + self.assertTrue(module.verified_matrix_result("success")) + for value in ("failure", "cancelled", "skipped", "", "Success", + "success ", True, 1, None): + with self.subTest(value=value): + self.assertFalse(module.verified_matrix_result(value)) + + def test_real_exit_status_allow_and_deny(self): + path = ROOT / "scripts" / "required_windows_summary.py" + for value, expected in (("success", 0), ("failure", 2), + ("cancelled", 2), ("skipped", 2), ("", 2)): + env = dict(os.environ, WINDOWS_MATRIX_RESULT=value) + res = subprocess.run([sys.executable, str(path)], env=env, + capture_output=True, text=True) + with self.subTest(result=value): + self.assertEqual(res.returncode, expected) + + def test_workflow_depends_on_complete_reusable_matrix_not_pg14_name(self): + contents = (ROOT / ".github" / "workflows" / "windows.yml").read_text() + self.assertIn(" windows_required:", contents) + summary = contents.split(" windows_required:", 1)[1].split( + "\n candidate_report:", 1)[0] + self.assertIn("name: Windows required summary", summary) + self.assertIn("needs: windows", summary) + self.assertIn("always()", summary) + self.assertIn("needs.windows.result", summary) + self.assertIn("scripts/required_windows_summary.py", summary) + self.assertNotIn("PostgreSQL 14", summary) + self.assertNotIn("workflow_dispatch:", summary) + + +if __name__ == "__main__": + unittest.main()