ci: enforce pull request documentation coverage (#3586)

This commit is contained in:
Carlos Florêncio
2026-09-12 20:44:13 +01:00
committed by GitHub
parent 0483c054ae
commit 56bc954001
18 changed files with 3888 additions and 0 deletions
@@ -0,0 +1,100 @@
#!/usr/bin/env python3
"""Contract tests for the pull request documentation coverage workflow."""
from __future__ import annotations
from pathlib import Path
import re
import unittest
REPO_ROOT = Path(__file__).resolve().parents[2]
WORKFLOW = REPO_ROOT / ".github" / "workflows" / "pr-docs.yml"
LINT_WORKFLOW = REPO_ROOT / ".github" / "workflows" / "lint-action-pinning.yml"
class PullRequestDocumentationWorkflowContractTest(unittest.TestCase):
def setUp(self) -> None:
self.assertTrue(WORKFLOW.is_file(), "Documentation coverage workflow is missing")
self.workflow = WORKFLOW.read_text(encoding="utf-8")
# @covers AC-CI-PR-DOCS-001.1, AC-CI-PR-DOCS-002.2
def test_runs_for_all_pr_and_manual_retry_events(self) -> None:
trigger = self.workflow.partition("on:\n")[2].partition("\nconcurrency:")[0]
self.assertIn("pull_request_target:", trigger)
for event in (
"opened",
"reopened",
"synchronize",
"edited",
"labeled",
"unlabeled",
"ready_for_review",
):
self.assertIn(event, trigger)
self.assertIn("merge_group:", trigger)
self.assertIn("checks_requested", trigger)
self.assertIn("workflow_dispatch:", trigger)
self.assertIn("pr_number:", trigger)
self.assertNotIn("paths:", trigger)
# @covers AC-CI-PR-DOCS-003.1, AC-CI-PR-DOCS-003.4
def test_serializes_prs_without_cancelling_merge_group_runs(self) -> None:
self.assertIn("group: >-", self.workflow)
self.assertIn("github.event.merge_group.base_ref", self.workflow)
self.assertIn("github.event.pull_request.base.ref", self.workflow)
self.assertIn("queue: max", self.workflow)
self.assertIn("cancel-in-progress: false", self.workflow)
# @covers AC-CI-PR-DOCS-003.3
def test_uses_trusted_least_privilege_checkout(self) -> None:
permissions = self.workflow.partition("permissions:\n")[2].partition("\njobs:")[0]
self.assertEqual(
permissions.strip(),
"contents: read\n pull-requests: read\n statuses: write",
)
self.assertRegex(
self.workflow,
r"uses: actions/checkout@[0-9a-f]{40} # v[0-9]+",
)
self.assertIn("persist-credentials: false", self.workflow)
self.assertIn("github.event.merge_group.base_sha", self.workflow)
self.assertNotIn("github.event.pull_request.head.sha", self.workflow)
self.assertIn("ref: ${{ env.CHECKOUT_REF }}", self.workflow)
self.assertNotIn("pull_request:\n", self.workflow)
self.assertNotIn("pnpm install", self.workflow)
self.assertNotIn("npm install", self.workflow)
self.assertIn("node .github/scripts/pr-docs.cjs", self.workflow)
self.assertIn("name: Publish PR documentation coverage status", self.workflow)
self.assertNotIn("name: PR documentation coverage\n", self.workflow)
# @covers AC-CI-PR-DOCS-001.1, AC-CI-PR-DOCS-002.1, AC-CI-PR-DOCS-003.2
def test_dispatch_is_restricted_to_the_default_branch_and_script_owns_statuses(self) -> None:
self.assertRegex(
self.workflow,
re.compile(
r"github\.event_name != 'workflow_dispatch'.*github\.ref.*default_branch",
re.DOTALL,
),
)
self.assertIn("PR documentation coverage", (REPO_ROOT / ".github" / "scripts" / "pr-docs.cjs").read_text(encoding="utf-8"))
self.assertIn("statuses: write", self.workflow)
def test_contract_is_registered_in_the_required_lint_workflow(self) -> None:
lint_workflow = LINT_WORKFLOW.read_text(encoding="utf-8")
self.assertRegex(
lint_workflow,
r"(?m)^ - name: Test pull request documentation workflow contract$\n"
r"^ run: python3 .github/scripts/pr-docs-workflow-contract_test.py$",
)
self.assertRegex(
lint_workflow,
r"(?m)^ - name: Test pull request documentation validator$\n"
r"^ run: node --test .github/scripts/pr-docs.test.cjs$",
)
makefile = (REPO_ROOT / "Makefile").read_text(encoding="utf-8")
self.assertIn("@node --test .github/scripts/pr-docs.test.cjs", makefile)
if __name__ == "__main__":
unittest.main()
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff