diff --git a/.github/scripts/check-csharp-pipeline-status.mjs b/.github/scripts/check-csharp-pipeline-status.mjs index ba59b8b..4cf6e3e 100755 --- a/.github/scripts/check-csharp-pipeline-status.mjs +++ b/.github/scripts/check-csharp-pipeline-status.mjs @@ -18,6 +18,7 @@ export const evaluatePipeline = ({ eventName, documentationChanged, needs }) => if (documentationChanged) { required.add("generatePdfWithCode"); required.add("buildDocumentation"); + required.add("publishDocumentation"); } if (eventName === "push") { @@ -25,9 +26,6 @@ export const evaluatePipeline = ({ eventName, documentationChanged, needs }) => required.add("pushNuGetPackageToGitHubPackageRegistry"); required.add("pushToNuget"); required.add("publishRelease"); - if (documentationChanged) { - required.add("publishDocumentation"); - } } const failures = []; diff --git a/.github/scripts/check-csharp-pipeline-status.test.mjs b/.github/scripts/check-csharp-pipeline-status.test.mjs index a7008e3..0a96d27 100755 --- a/.github/scripts/check-csharp-pipeline-status.test.mjs +++ b/.github/scripts/check-csharp-pipeline-status.test.mjs @@ -25,6 +25,7 @@ test("accepts a pull request after validation and documentation builds pass", () findChangedCsFiles: "success", generatePdfWithCode: "success", buildDocumentation: "success", + publishDocumentation: "success", }); assert.deepEqual( evaluatePipeline({ eventName: "pull_request", documentationChanged: true, needs }), @@ -32,6 +33,22 @@ test("accepts a pull request after validation and documentation builds pass", () ); }); +test("rejects a skipped documentation assembly on pull requests", () => { + const needs = results({ + test: "success", + findChangedCsFiles: "success", + generatePdfWithCode: "success", + buildDocumentation: "success", + }); + assert.deepEqual( + evaluatePipeline({ eventName: "pull_request", documentationChanged: true, needs }), + { + passed: false, + failures: ["publishDocumentation: required job finished with skipped"], + }, + ); +}); + test("accepts expected documentation skips when no maintained input changed", () => { const needs = results({ test: "success", diff --git a/.github/scripts/csharp-workflow-policy.test.mjs b/.github/scripts/csharp-workflow-policy.test.mjs index ff0d508..1a9837f 100755 --- a/.github/scripts/csharp-workflow-policy.test.mjs +++ b/.github/scripts/csharp-workflow-policy.test.mjs @@ -103,16 +103,36 @@ test("gates both package publishers on a release preflight", () => { } }); -test("builds documentation on pull requests and transfers the PDF", () => { - assert.ok(jobs.get("buildDocumentation"), "buildDocumentation job should exist"); - assert.doesNotMatch(jobs.get("generatePdfWithCode"), /github\.event_name == 'push'/); - assert.doesNotMatch(jobs.get("buildDocumentation"), /github\.event_name == 'push'/); - assert.match(jobs.get("generatePdfWithCode"), /actions\/upload-artifact@/); +test("builds PDF and API documentation in parallel before publishing both", () => { + const pdf = jobs.get("generatePdfWithCode"); + const documentation = jobs.get("buildDocumentation"); + const publisher = jobs.get("publishDocumentation"); + + assert.ok(pdf, "generatePdfWithCode job should exist"); + assert.ok(documentation, "buildDocumentation job should exist"); + assert.ok(publisher, "publishDocumentation job should exist"); + assert.match(pdf, /needs: \[findChangedCsFiles\]/); + assert.match(documentation, /needs: \[findChangedCsFiles\]/); + assert.doesNotMatch(documentation, /needs:.*generatePdfWithCode/); + assert.doesNotMatch(pdf, /github\.event_name == 'push'/); + assert.doesNotMatch(documentation, /github\.event_name == 'push'/); + assert.match(pdf, /name: csharp-pdf/); + assert.match(documentation, /name: csharp-documentation/); assert.doesNotMatch(workflow, /actions\/download-artifact@/); - assert.match(jobs.get("buildDocumentation"), /gh run download/); - assert.match(jobs.get("buildDocumentation"), /actions\/upload-artifact@/); - assert.match(jobs.get("publishDocumentation"), /gh run download/); - assert.match(jobs.get("publishDocumentation"), /github\.event_name == 'push'/); + assert.match( + publisher, + /needs: \[findChangedCsFiles, generatePdfWithCode, buildDocumentation\]/, + ); + assert.match(publisher, /--name csharp-documentation/); + assert.match(publisher, /--name csharp-pdf/); + assert.match( + publisher, + /if: \$\{\{ needs\.findChangedCsFiles\.outputs\.documentationChanged == 'true' \}\}/, + ); + assert.match( + publisher, + /- name: Publish documentation to gh-pages\n if: \$\{\{ github\.event_name == 'push' \}\}/, + ); }); test("aggregates every job result so skipped dependents cannot hide failures", () => { diff --git a/.github/workflows/csharp.yml b/.github/workflows/csharp.yml index 1eb11ce..887c273 100644 --- a/.github/workflows/csharp.yml +++ b/.github/workflows/csharp.yml @@ -208,7 +208,7 @@ jobs: retention-days: 1 buildDocumentation: - needs: [findChangedCsFiles, generatePdfWithCode] + needs: [findChangedCsFiles] if: ${{ needs.findChangedCsFiles.outputs.documentationChanged == 'true' }} runs-on: ubuntu-24.04 timeout-minutes: 20 @@ -229,15 +229,8 @@ jobs: "$RUNNER_TEMP/docfx/docfx" docfx.json --warningsAsErrors cp _site/README.html _site/index.html - - name: Download generated PDF - env: - GH_TOKEN: ${{ github.token }} - run: gh run download "$GITHUB_RUN_ID" --repo "$GITHUB_REPOSITORY" --name csharp-pdf --dir _site - - name: Validate documentation output - run: | - test -s _site/index.html - test -s _site/Platform.Interfaces.pdf + run: test -s _site/index.html - name: Upload documentation site timeout-minutes: 5 @@ -249,8 +242,8 @@ jobs: retention-days: 1 publishDocumentation: - needs: [findChangedCsFiles, buildDocumentation] - if: ${{ github.event_name == 'push' && needs.findChangedCsFiles.outputs.documentationChanged == 'true' }} + needs: [findChangedCsFiles, generatePdfWithCode, buildDocumentation] + if: ${{ needs.findChangedCsFiles.outputs.documentationChanged == 'true' }} runs-on: ubuntu-24.04 timeout-minutes: 10 permissions: @@ -261,12 +254,20 @@ jobs: with: persist-credentials: false - - name: Download documentation site + - name: Download documentation artifacts env: GH_TOKEN: ${{ github.token }} - run: gh run download "$GITHUB_RUN_ID" --repo "$GITHUB_REPOSITORY" --name csharp-documentation --dir _site + run: | + gh run download "$GITHUB_RUN_ID" --repo "$GITHUB_REPOSITORY" --name csharp-documentation --dir _site + gh run download "$GITHUB_RUN_ID" --repo "$GITHUB_REPOSITORY" --name csharp-pdf --dir _site + + - name: Validate documentation site + run: | + test -s _site/index.html + test -s _site/Platform.Interfaces.pdf - name: Publish documentation to gh-pages + if: ${{ github.event_name == 'push' }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: ../.github/scripts/publish-csharp-docs.sh _site