diff --git a/.github/wiki b/.github/wiki index 005922a42..9c578a579 160000 --- a/.github/wiki +++ b/.github/wiki @@ -1 +1 @@ -Subproject commit 005922a42075e4fb639cde3ec51c525f5ab55fd1 +Subproject commit 9c578a579fa0a6b6daa0165acef3b7f69957412e diff --git a/.github/workflows/reports.yml b/.github/workflows/reports.yml index 050d8ab0a..53c18a8f2 100644 --- a/.github/workflows/reports.yml +++ b/.github/workflows/reports.yml @@ -194,12 +194,56 @@ jobs: /coverage/|Preview coverage report /metrics/|Preview metrics report + validate_main_reports_html: + if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && !(github.event_name == 'workflow_dispatch' && inputs.cleanup-previews) + name: Validate Main Reports HTML + needs: verify_main_reports + runs-on: ubuntu-latest + + steps: + - name: Checkout published reports + uses: actions/checkout@v6 + with: + ref: gh-pages + path: gh-pages + + - name: Exclude pull request previews from main validation + run: rm -rf gh-pages/previews + + - name: Validate published main reports HTML + uses: anishathalye/proof-html@v2 + with: + directory: ./gh-pages + disable_external: true + check_external_hash: false + + validate_preview_reports_html: + if: github.event_name == 'pull_request' && github.event.action != 'closed' + name: Validate Pull Request Reports HTML + needs: verify_preview_reports + runs-on: ubuntu-latest + + steps: + - name: Checkout published reports + uses: actions/checkout@v6 + with: + ref: gh-pages + path: gh-pages + + - name: Validate published preview reports HTML + uses: anishathalye/proof-html@v2 + with: + directory: ./gh-pages/previews/pr-${{ github.event.pull_request.number }} + disable_external: true + check_external_hash: false + comment_preview: if: github.event_name == 'pull_request' && github.event.action != 'closed' name: Comment Pull Request Preview URLs needs: - reports - verify_preview_reports + - validate_preview_reports_html runs-on: ubuntu-latest permissions: pull-requests: write @@ -299,6 +343,8 @@ jobs: - reports - verify_main_reports - verify_preview_reports + - validate_main_reports_html + - validate_preview_reports_html - cleanup_preview - cleanup_orphaned_previews runs-on: ubuntu-latest @@ -325,7 +371,9 @@ jobs: echo "- PHP version source: \`${{ needs.resolve_php.outputs.php-version-source }}\`" echo "- Reports job result: \`${{ needs.reports.result }}\`" echo "- Main verification result: \`${{ needs.verify_main_reports.result }}\`" + echo "- Main HTML validation result: \`${{ needs.validate_main_reports_html.result }}\`" echo "- Preview verification result: \`${{ needs.verify_preview_reports.result }}\`" + echo "- Preview HTML validation result: \`${{ needs.validate_preview_reports_html.result }}\`" echo "- Closed-preview cleanup result: \`${{ needs.cleanup_preview.result }}\`" echo "- Orphan cleanup result: \`${{ needs.cleanup_orphaned_previews.result }}\`" echo "- Trigger: \`${TRIGGER_LABEL}\`" @@ -335,6 +383,7 @@ jobs: echo "- Coverage: https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}/coverage/" echo "- Metrics: https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}/metrics/" echo "- Deployment verification: \`${{ needs.verify_main_reports.result }}\`" + echo "- HTML validation: \`${{ needs.validate_main_reports_html.result }}\`" fi if [ "${{ github.event_name }}" = "pull_request" ] && [ "${{ github.event.action }}" != "closed" ]; then @@ -342,6 +391,7 @@ jobs: echo "- Preview coverage: https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}/previews/pr-${{ github.event.pull_request.number }}/coverage/" echo "- Preview metrics: https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}/previews/pr-${{ github.event.pull_request.number }}/metrics/" echo "- Preview verification: \`${{ needs.verify_preview_reports.result }}\`" + echo "- Preview HTML validation: \`${{ needs.validate_preview_reports_html.result }}\`" fi if [ "${{ github.event_name }}" = "pull_request" ] && [ "${{ github.event.action }}" = "closed" ]; then diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 76b2a4530..476366fcb 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,6 +13,11 @@ on: required: false type: number default: -1 + publish-required-statuses: + description: Mirror required test matrix checks as commit statuses for workflow-dispatched runs. + required: false + type: boolean + default: false workflow_dispatch: inputs: min-coverage: @@ -25,6 +30,11 @@ on: required: false type: number default: -1 + publish-required-statuses: + description: Mirror required test matrix checks as commit statuses for workflow-dispatched runs. + required: false + type: boolean + default: false pull_request: types: [opened, synchronize, reopened] push: @@ -32,6 +42,7 @@ on: permissions: contents: read + statuses: write concurrency: group: ${{ github.event_name == 'pull_request' && format('tests-pr-{0}', github.event.pull_request.number) || format('tests-{0}', github.ref) }} @@ -168,3 +179,35 @@ jobs: - Dependency health `max-outdated`: `${{ inputs.max-outdated || -1 }}` - Tests job result: `${{ needs.tests.result }}` - Dependency health result: `${{ needs.dependency-health.result }}` + + publish_required_statuses: + if: ${{ always() && inputs.publish-required-statuses }} + name: Publish Required Test Statuses + needs: + - tests + runs-on: ubuntu-latest + steps: + - name: Mirror required test matrix contexts + env: + GH_TOKEN: ${{ github.token }} + TARGET_SHA: ${{ github.sha }} + TARGET_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + TESTS_RESULT: ${{ needs.tests.result }} + run: | + if [ "${TESTS_RESULT}" = "success" ]; then + state="success" + description="Workflow-dispatched PHPUnit matrix passed." + else + state="failure" + description="Workflow-dispatched PHPUnit matrix result: ${TESTS_RESULT}." + fi + + for context in "Run Tests (8.3)" "Run Tests (8.4)" "Run Tests (8.5)"; do + gh api \ + --method POST \ + "repos/${GITHUB_REPOSITORY}/statuses/${TARGET_SHA}" \ + -f state="${state}" \ + -f context="${context}" \ + -f description="${description}" \ + -f target_url="${TARGET_URL}" + done diff --git a/.github/workflows/wiki-preview.yml b/.github/workflows/wiki-preview.yml index c3ef160c3..af29a1aed 100644 --- a/.github/workflows/wiki-preview.yml +++ b/.github/workflows/wiki-preview.yml @@ -116,7 +116,7 @@ jobs: env: GH_TOKEN: ${{ github.token }} HEAD_REF: ${{ github.event.pull_request.head.ref }} - run: gh workflow run tests.yml --ref "${HEAD_REF}" -f max-outdated=-1 + run: gh workflow run tests.yml --ref "${HEAD_REF}" -f max-outdated=-1 -f publish-required-statuses=true - uses: ./.dev-tools-actions/.github/actions/summary/write with: diff --git a/CHANGELOG.md b/CHANGELOG.md index e95201b5e..a9ffad9f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Keep required PHPUnit matrix checks reporting after workflow-managed `.github/wiki` pointer commits by running the pull-request test workflow without top-level path filters and aligning the packaged consumer test wrapper (#230) - Ignore intentional Composer Dependency Analyser shadow dependency findings by default while adding `dependencies --show-shadow-dependencies` for audits (#233) - Dispatch the required test workflow after wiki preview automation updates a pull-request `.github/wiki` pointer, avoiding permanently pending required checks on bot-authored pointer commits (#230) +- Mirror workflow-dispatched wiki pointer test results into required `Run Tests` commit statuses so branch protection recognizes bot-authored pointer commits (#230) +- Validate published main and pull-request report HTML with `proof-html` after GitHub Pages deployment health checks pass (#178) ## [1.21.0] - 2026-04-24 diff --git a/docs/advanced/branch-protection-and-bot-commits.rst b/docs/advanced/branch-protection-and-bot-commits.rst index 4e02a20e1..6cf434ff0 100644 --- a/docs/advanced/branch-protection-and-bot-commits.rst +++ b/docs/advanced/branch-protection-and-bot-commits.rst @@ -106,9 +106,12 @@ does not start another ``pull_request`` or ``push`` workflow run for commits pushed with the built-in workflow token. After the wiki preview workflow commits a parent-repository pointer update, it explicitly dispatches ``tests.yml`` for the pull request head branch so the newest bot-authored commit receives the -required ``Run Tests`` matrix checks. Test workflow concurrency cancels older -in-progress runs for the same pull request so the newest commit owns the -required check contexts. +required ``Run Tests`` matrix checks. Because manually dispatched workflow check +runs are not always treated as pull-request required checks, that dispatched +test run also mirrors the matrix result into commit statuses named +``Run Tests (8.3)``, ``Run Tests (8.4)``, and ``Run Tests (8.5)``. Test workflow +concurrency cancels older in-progress runs for the same pull request so the +newest commit owns the required check contexts. At a high level, the workflows need permission to read repository contents, write generated preview commits, update pull request comments, and publish Pages @@ -122,8 +125,10 @@ The reusable workflows default to read-only repository access and grant write permissions at the job level when generated content must be pushed or pull requests must be updated. -``tests.yml`` only needs ``contents: read`` because it checks out code, installs -dependencies, and runs PHPUnit. +``tests.yml`` needs ``contents: read`` because it checks out code, installs +dependencies, and runs PHPUnit. It also declares ``statuses: write`` so +workflow-dispatched test runs can mirror required matrix contexts onto +bot-authored wiki pointer commits. ``reports.yml`` keeps ``contents: write`` on jobs that publish or clean ``gh-pages`` content. The pull request preview comment runs as a separate job diff --git a/docs/advanced/consumer-automation.rst b/docs/advanced/consumer-automation.rst index 29190534b..ae1d17c1f 100644 --- a/docs/advanced/consumer-automation.rst +++ b/docs/advanced/consumer-automation.rst @@ -71,7 +71,8 @@ How GitHub Pages Publishing Works --------------------------------- - ``.github/workflows/reports.yml`` runs ``composer dev-tools reports``. -- The workflow delegates repeated GitHub Pages tasks to +- The workflow verifies published URLs, validates published HTML with + ``proof-html``, and delegates repeated GitHub Pages tasks to ``.github/actions/github-pages/*`` instead of keeping that shell logic inline. - Pull requests publish previews under ``previews/pr-/``. - On the ``main`` branch, GitHub Pages serves the generated site from the root diff --git a/docs/usage/github-actions.rst b/docs/usage/github-actions.rst index ad971ca71..7883974f9 100644 --- a/docs/usage/github-actions.rst +++ b/docs/usage/github-actions.rst @@ -71,6 +71,9 @@ The ``reports.yml`` workflow is responsible for generating technical documentati **Behavior:** * **Main Branch**: Runs all checks and deploys the final reports to the root of the ``gh-pages`` branch. * Runs a post-deploy health check against the published reports index and coverage URLs with retry/backoff to account for Pages propagation. + * Checks out the published ``gh-pages`` content after deployment + verification and runs ``proof-html`` against the main reports site, + excluding historical pull-request previews from that main-site scan. * Resolves the workflow PHP version from ``composer.lock`` or ``composer.json`` before installing dependencies. * Removes ``.dev-tools/cache`` from the publish directory before deployment so repository-local tool caches never leak into GitHub Pages output. * Appends a run summary with the published docs, coverage, and metrics URLs plus deployment verification status. @@ -78,6 +81,7 @@ The ``reports.yml`` workflow is responsible for generating technical documentati * Generates a **Preview** of the documentation, coverage, and metrics. * Deploys the preview to ``gh-pages`` under ``previews/pr-{number}/``. * Verifies the preview index and coverage URLs after deployment before posting preview links. + * Runs ``proof-html`` against the published preview directory before posting preview links. * Posts a **Sticky Comment** on the PR with links to the live preview, coverage report, and metrics site. * Appends a run summary with preview URLs and verification status. * Groups nested command output into collapsible GitHub Actions log sections so docs, tests, and metrics are easier to inspect independently. diff --git a/resources/github-actions/tests.yml b/resources/github-actions/tests.yml index a935fa736..bff4e9e64 100644 --- a/resources/github-actions/tests.yml +++ b/resources/github-actions/tests.yml @@ -14,13 +14,20 @@ on: required: false type: number default: -1 + publish-required-statuses: + description: Mirror required test matrix checks as commit statuses for workflow-dispatched runs. + required: false + type: boolean + default: false permissions: contents: read + statuses: write jobs: tests: uses: php-fast-forward/dev-tools/.github/workflows/tests.yml@main with: max-outdated: ${{ inputs.max-outdated || -1 }} + publish-required-statuses: ${{ inputs.publish-required-statuses || false }} secrets: inherit