Website #14
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: KeyLoad website | |
| on: | |
| push: | |
| branches: [main] | |
| paths: ['site/**'] | |
| workflow_run: | |
| workflows: [KeyLoad CI] | |
| branches: [main] | |
| types: [completed] | |
| workflow_dispatch: | |
| inputs: | |
| mode: | |
| description: Qualify candidate website, or publish the current main website | |
| required: true | |
| default: validate | |
| type: choice | |
| options: [validate, publish] | |
| evidence_run: | |
| description: Historical comparison run for validation only; blank selects latest comparison | |
| required: false | |
| type: string | |
| permissions: | |
| contents: read | |
| actions: read | |
| concurrency: | |
| group: keyload-pages | |
| cancel-in-progress: false | |
| jobs: | |
| qualify: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| outputs: | |
| mode: ${{ steps.source.outputs.mode }} | |
| site_revision: ${{ steps.source.outputs.revision }} | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| GH_REPO: ${{ github.repository }} | |
| EVIDENCE_DIR: ${{ github.workspace }}/artifacts/site-evidence | |
| KEYLOAD_SITE_REPOSITORY: ${{ github.workspace }}/website | |
| KEYLOAD_SITE_GITHUB_CAPTURE: ${{ github.workspace }}/artifacts/site-evidence/github-capture | |
| KEYLOAD_SITE_ARCHIVE: ${{ github.workspace }}/artifacts/site-evidence/comparison-suite.zip | |
| KEYLOAD_SITE_ARCHIVE_RECEIPT: ${{ github.workspace }}/artifacts/site-evidence/archive-receipt.json | |
| KEYLOAD_SITE_REPORTS: ${{ github.workspace }}/artifacts/comparisons | |
| KEYLOAD_SITE_COVERAGE: ${{ github.workspace }}/artifacts/site-evidence/js-coverage | |
| steps: | |
| - name: Checkout trusted workflow control source | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| ref: ${{ github.workflow_sha }} | |
| path: control | |
| persist-credentials: false | |
| - name: Select website source and authenticated comparison evidence | |
| id: source | |
| env: | |
| EVENT_NAME: ${{ github.event_name }} | |
| REQUESTED_MODE: ${{ inputs.mode }} | |
| REQUESTED_RUN: ${{ inputs.evidence_run }} | |
| CONTROL_REVISION: ${{ github.workflow_sha }} | |
| shell: bash | |
| run: | | |
| mode=publish | |
| if [[ "$EVENT_NAME" == workflow_dispatch ]]; then mode="$REQUESTED_MODE"; fi | |
| revision="$GITHUB_SHA" | |
| if [[ "$mode" == publish ]]; then | |
| [[ "$GITHUB_REF" == refs/heads/main && -z "$REQUESTED_RUN" ]] | |
| revision=$(timeout 120s gh api "repos/$GH_REPO/git/ref/heads/main" --jq .object.sha) | |
| fi | |
| [[ "$revision" =~ ^[a-f0-9]{40}$ && "$CONTROL_REVISION" =~ ^[a-f0-9]{40}$ ]] | |
| printf 'mode=%s\nrevision=%s\n' "$mode" "$revision" >> "$GITHUB_OUTPUT" | |
| printf 'KEYLOAD_SITE_SOURCE_REVISION=%s\n' "$revision" >> "$GITHUB_ENV" | |
| printf 'KEYLOAD_SITE_CONTROL_REVISION=%s\n' "$CONTROL_REVISION" >> "$GITHUB_ENV" | |
| args=("--input=$KEYLOAD_SITE_GITHUB_CAPTURE" "--mode=$mode" "--site-revision=$revision" "--workflow-revision=$CONTROL_REVISION") | |
| if [[ -n "$REQUESTED_RUN" ]]; then args+=("--requested-run=$REQUESTED_RUN"); fi | |
| bash control/scripts/Features/BenchmarkComparisons/collect-github-evidence.sh "${args[@]}" | |
| proof="$KEYLOAD_SITE_GITHUB_CAPTURE/metadata-proof.json" | |
| artifact_id=$(jq -er '.artifact.id' "$proof") | |
| [[ "$artifact_id" =~ ^[1-9][0-9]*$ ]] | |
| timeout 120s gh api "repos/$GH_REPO/actions/artifacts/$artifact_id/zip" > "$KEYLOAD_SITE_ARCHIVE" | |
| node control/scripts/Features/BenchmarkComparisons/github-evidence.mjs verify-archive --receipt="$proof" --archive="$KEYLOAD_SITE_ARCHIVE" > "$EVIDENCE_DIR/archive-envelope.json" | |
| jq -e '.ok == true' "$EVIDENCE_DIR/archive-envelope.json" | |
| jq '.result' "$EVIDENCE_DIR/archive-envelope.json" > "$KEYLOAD_SITE_ARCHIVE_RECEIPT" | |
| printf 'KEYLOAD_SITE_EVIDENCE_RUN=%s\nKEYLOAD_SITE_MEASURED_REVISION=%s\n' "$(jq -r '.run.id' "$proof")" "$(jq -r '.measuredSourceRevision' "$proof")" >> "$GITHUB_ENV" | |
| printf 'measured_revision=%s\n' "$(jq -r '.measuredSourceRevision' "$proof")" >> "$GITHUB_OUTPUT" | |
| - name: Checkout website source | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| ref: ${{ steps.source.outputs.revision }} | |
| path: website | |
| persist-credentials: false | |
| - name: Inspect measured producer source | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| ref: ${{ steps.source.outputs.measured_revision }} | |
| path: measured | |
| persist-credentials: false | |
| - name: Verify checkouts and retain runtime/source receipts | |
| shell: bash | |
| run: | | |
| [[ "$(git -C website rev-parse HEAD)" == "$KEYLOAD_SITE_SOURCE_REVISION" ]] | |
| [[ "$(git -C measured rev-parse HEAD)" == "$KEYLOAD_SITE_MEASURED_REVISION" ]] | |
| test -f measured/.github/workflows/ci.yml | |
| git -C website rev-parse HEAD > "$EVIDENCE_DIR/website-revision.txt" | |
| git -C measured rev-parse HEAD > "$EVIDENCE_DIR/measured-revision.txt" | |
| git -C control rev-parse HEAD > "$EVIDENCE_DIR/control-revision.txt" | |
| tools=(collect-github-evidence.sh github-evidence-contracts.mjs github-evidence-runs.mjs github-evidence-proof.mjs github-evidence.mjs) | |
| for file in "${tools[@]}"; do | |
| relative="scripts/Features/BenchmarkComparisons/$file" | |
| cmp "control/$relative" "website/$relative" | |
| sha256sum "website/$relative" >> "$EVIDENCE_DIR/executed-evidence-tools.sha256" | |
| done | |
| cp measured/.github/workflows/ci.yml "$EVIDENCE_DIR/measured-producer.yml" | |
| node --version > "$EVIDENCE_DIR/node-version.txt" | |
| chrome=$(command -v google-chrome || command -v google-chrome-stable || command -v chromium || command -v chromium-browser) | |
| test -n "$chrome" && test -x "$chrome" | |
| "$chrome" --version > "$EVIDENCE_DIR/browser-version.txt" | |
| printf 'KEYLOAD_SITE_BROWSER=%s\n' "$chrome" >> "$GITHUB_ENV" | |
| mkdir -p "$KEYLOAD_SITE_COVERAGE/node" | |
| cd website | |
| git ls-files -z | xargs -0 sha256sum > "$EVIDENCE_DIR/source.sha256" | |
| - uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 | |
| with: | |
| global-json-file: website/global.json | |
| - name: Qualify required analyzer dependency | |
| working-directory: website | |
| shell: bash | |
| run: | | |
| dotnet --info > "$EVIDENCE_DIR/dotnet-info.txt" | |
| pwsh --version > "$EVIDENCE_DIR/powershell-version.txt" | |
| dotnet restore tests/KeyLoad.Analyzers.Tests/KeyLoad.Analyzers.Tests.csproj | |
| dotnet build tests/KeyLoad.Analyzers.Tests/KeyLoad.Analyzers.Tests.csproj --no-restore --configuration Release | |
| mkdir -p "$EVIDENCE_DIR/analyzer-coverage" | |
| cp scripts/Features/CodeQuality/site-analyzer-coverage.settings.xml "$EVIDENCE_DIR/analyzer-coverage/coverage.config.xml" | |
| pwsh -NoProfile -File scripts/Features/CodeQuality/site-analyzer-coverage.ps1 -Mode Prepare -Repository "$KEYLOAD_SITE_REPOSITORY" -Contract "$KEYLOAD_SITE_REPOSITORY/scripts/Features/CodeQuality/site-analyzer-coverage.contract.json" -EvidenceRoot "$EVIDENCE_DIR/analyzer-coverage" | |
| dotnet test --project tests/KeyLoad.Analyzers.Tests --no-build --no-restore --configuration Release --report-trx --results-directory "$EVIDENCE_DIR/analyzer-tests" --coverage --coverage-settings "$EVIDENCE_DIR/analyzer-coverage/coverage.config.xml" --coverage-output-format cobertura --coverage-output "$EVIDENCE_DIR/analyzer-coverage/coverage.cobertura.xml" | |
| - name: Enforce native analyzer coverage counts | |
| if: success() || failure() | |
| working-directory: website | |
| shell: pwsh | |
| run: ./scripts/Features/CodeQuality/site-analyzer-coverage.ps1 -Mode Verify -Repository "$env:KEYLOAD_SITE_REPOSITORY" -Contract "$env:KEYLOAD_SITE_REPOSITORY/scripts/Features/CodeQuality/site-analyzer-coverage.contract.json" -EvidenceRoot "$env:EVIDENCE_DIR/analyzer-coverage" -CoverageReport "$env:EVIDENCE_DIR/analyzer-coverage/coverage.cobertura.xml" | |
| - name: Restore and build independent site qualification | |
| working-directory: website | |
| run: | | |
| dotnet restore tests/KeyLoad.SiteTests/KeyLoad.SiteTests.csproj | |
| dotnet build tests/KeyLoad.SiteTests/KeyLoad.SiteTests.csproj --no-restore --configuration Release | |
| - name: Verify focused format and governance | |
| working-directory: website | |
| shell: bash | |
| run: | | |
| dotnet format src/KeyLoad.Analyzers/KeyLoad.Analyzers.csproj --verify-no-changes --no-restore --severity warn --verbosity minimal > "$EVIDENCE_DIR/format-analyzers.txt" 2>&1 | |
| dotnet format tests/KeyLoad.Analyzers.Tests/KeyLoad.Analyzers.Tests.csproj --verify-no-changes --no-restore --severity warn --verbosity minimal > "$EVIDENCE_DIR/format-analyzer-tests.txt" 2>&1 | |
| dotnet format tests/KeyLoad.SiteTests/KeyLoad.SiteTests.csproj --verify-no-changes --no-restore --severity warn --verbosity minimal > "$EVIDENCE_DIR/format-site-tests.txt" 2>&1 | |
| node scripts/Features/RepositoryGovernance/verify.mjs | |
| - name: Run full site TUnit suite with mandatory same-archive preparation | |
| working-directory: website | |
| env: | |
| NODE_V8_COVERAGE: ${{ github.workspace }}/artifacts/site-evidence/js-coverage/node | |
| run: dotnet test --project tests/KeyLoad.SiteTests --no-build --no-restore --configuration Release --report-trx --results-directory "$EVIDENCE_DIR/site-tests" | |
| - name: Require complete passing test receipts without skips | |
| shell: pwsh | |
| run: | | |
| $receipts = foreach ($suite in @('analyzer-tests', 'site-tests')) { | |
| $files = @(Get-ChildItem -LiteralPath "$env:EVIDENCE_DIR/$suite" -Filter '*.trx' -File) | |
| if ($files.Count -ne 1) { throw "Expected exactly one $suite TRX receipt" } | |
| [xml]$document = Get-Content -LiteralPath $files[0].FullName -Raw | |
| $counts = $document.TestRun.ResultSummary.Counters | |
| if ($null -eq $counts -or [int]$counts.total -le 0 -or | |
| [int]$counts.executed -ne [int]$counts.total -or | |
| [int]$counts.passed -ne [int]$counts.total) { | |
| throw "Incomplete or failing $suite qualification; skipped tests cannot pass" | |
| } | |
| [ordered]@{ suite = $suite; total = [int]$counts.total; executed = [int]$counts.executed; passed = [int]$counts.passed; sha256 = (Get-FileHash -LiteralPath $files[0].FullName -Algorithm SHA256).Hash.ToLowerInvariant() } | |
| } | |
| ConvertTo-Json -InputObject @($receipts) -Depth 4 | Set-Content -LiteralPath "$env:EVIDENCE_DIR/test-receipts.json" -Encoding utf8NoBOM | |
| - name: Build exact qualified website and publication receipt | |
| working-directory: website | |
| shell: bash | |
| run: | | |
| jq -e '.passed == true' "$EVIDENCE_DIR/analyzer-coverage/report.json" | |
| jq -e '.passed == true' "$KEYLOAD_SITE_COVERAGE/report.json" | |
| receipt="$EVIDENCE_DIR/archive-inputs/extraction.json" | |
| verify_inputs() { | |
| jq -e --arg root "$KEYLOAD_SITE_REPORTS" --slurpfile verified "$KEYLOAD_SITE_ARCHIVE_RECEIPT" ' | |
| .schemaVersion == 1 and .reportsRoot == $root and .archive == $verified[0].archive and | |
| (.files | type == "array" and length == 12) and | |
| ([.files[].path] | sort) == (["smoke", "json-1k-c8", "json-16k-c4"] as $profiles | | |
| ["results.json", "samples.csv", "results.md", "runner.log"] as $names | | |
| [$profiles[] as $profile | $names[] | $profile + "/" + .] | sort) and | |
| all(.files[]; (.sha256 | type == "string" and test("^[a-f0-9]{64}$")) and | |
| (.bytes | type == "number" and . >= 0 and . <= 8388608 and floor == .)) and | |
| ([.files[].bytes] | add) <= 67108864 | |
| ' "$receipt" > /dev/null | |
| jq -er '.files[] | [.path,.sha256,.bytes] | @tsv' "$receipt" > "$EVIDENCE_DIR/input-recheck.tsv" | |
| [[ "$(sha256sum "$KEYLOAD_SITE_ARCHIVE" | cut -d' ' -f1)" == "$(jq -r '.archive.sha256' "$KEYLOAD_SITE_ARCHIVE_RECEIPT")" ]] | |
| [[ "$(stat -c %s "$KEYLOAD_SITE_ARCHIVE")" == "$(jq -r '.archive.bytes' "$KEYLOAD_SITE_ARCHIVE_RECEIPT")" ]] | |
| local checked=0 | |
| while IFS=$'\t' read -r path digest bytes; do | |
| [[ "$(sha256sum "$KEYLOAD_SITE_REPORTS/$path" | cut -d' ' -f1)" == "$digest" ]] | |
| [[ "$(stat -c %s "$KEYLOAD_SITE_REPORTS/$path")" == "$bytes" ]] | |
| checked=$((checked + 1)) | |
| done < "$EVIDENCE_DIR/input-recheck.tsv" | |
| [[ "$checked" == 12 ]] | |
| } | |
| verify_inputs | |
| node site/scripts/build.mjs --reports="$KEYLOAD_SITE_REPORTS" --output="$GITHUB_WORKSPACE/_site" --revision="$KEYLOAD_SITE_MEASURED_REVISION" --evidence-url="https://github.com/$GH_REPO/actions/runs/$KEYLOAD_SITE_EVIDENCE_RUN" --site-revision="$KEYLOAD_SITE_SOURCE_REVISION" > "$EVIDENCE_DIR/final-builder.json" | |
| for profile in smoke json-1k-c8 json-16k-c4; do | |
| for file in results.json samples.csv results.md; do | |
| cmp "$KEYLOAD_SITE_REPORTS/$profile/$file" "$GITHUB_WORKSPACE/_site/data/runs/$profile/$file" | |
| done | |
| done | |
| verify_inputs | |
| timeout 120s gh api --paginate --slurp "repos/$GH_REPO/actions/runs/$GITHUB_RUN_ID/attempts/$GITHUB_RUN_ATTEMPT/jobs?per_page=100" > "$EVIDENCE_DIR/qualification-jobs-pages.json" | |
| job_url=$(jq -er '[.[].jobs[] | select(.name == "qualify")] | select(length == 1) | .[0].html_url' "$EVIDENCE_DIR/qualification-jobs-pages.json") | |
| native_hash=$(sha256sum "$EVIDENCE_DIR/analyzer-coverage/report.json" | cut -d' ' -f1) | |
| site_hash=$(sha256sum "$KEYLOAD_SITE_COVERAGE/report.json" | cut -d' ' -f1) | |
| jq -n --slurpfile proof "$KEYLOAD_SITE_ARCHIVE_RECEIPT" --slurpfile inputs "$receipt" --slurpfile tests "$EVIDENCE_DIR/test-receipts.json" --arg run_url "https://github.com/$GH_REPO/actions/runs/$GITHUB_RUN_ID" --arg job_url "$job_url" --arg native_hash "$native_hash" --arg site_hash "$site_hash" '{schemaVersion:1,source:{website:$proof[0].siteSourceRevision,measured:$proof[0].measuredSourceRevision,control:$proof[0].controlWorkflowRevision},evidence:{run:$proof[0].run,comparisonJob:$proof[0].comparisonJob,artifact:$proof[0].artifact,archive:$proof[0].archive},reports:[$inputs[0].files[]|select(.path|endswith("/runner.log")|not)],qualification:{runUrl:$run_url,jobUrl:$job_url,nativeReportSha256:$native_hash,siteReportSha256:$site_hash,tests:$tests[0]}}' > "$GITHUB_WORKSPACE/_site/data/publication.json" | |
| - name: Upload exact qualified Pages output | |
| if: success() && steps.source.outputs.mode == 'publish' | |
| uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4 | |
| with: | |
| path: _site | |
| - name: Retain all qualification and provenance evidence | |
| if: always() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: site-qualification-${{ steps.source.outputs.revision }}-${{ github.run_attempt }} | |
| path: | | |
| _site | |
| artifacts/site-evidence | |
| website/artifacts/code-quality/KeyLoad.SiteTests | |
| website/artifacts/code-quality/KeyLoad.Analyzers | |
| website/artifacts/code-quality/KeyLoad.Analyzers.Tests | |
| website/tests/KeyLoad.Analyzers.Tests/bin/Release/net10.0/KeyLoad.Analyzers.dll | |
| website/tests/KeyLoad.Analyzers.Tests/bin/Release/net10.0/KeyLoad.Analyzers.pdb | |
| deploy: | |
| needs: qualify | |
| if: needs.qualify.result == 'success' && needs.qualify.outputs.mode == 'publish' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| permissions: | |
| contents: read | |
| actions: read | |
| pages: write | |
| id-token: write | |
| environment: | |
| name: github-pages | |
| url: ${{ steps.deployment.outputs.page_url }} | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| GH_REPO: ${{ github.repository }} | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| ref: ${{ github.workflow_sha }} | |
| path: control | |
| persist-credentials: false | |
| - uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 | |
| with: | |
| name: site-qualification-${{ needs.qualify.outputs.site_revision }}-${{ github.run_attempt }} | |
| path: qualification | |
| - name: Recheck current website and immutable comparison evidence | |
| env: | |
| QUALIFIED_REVISION: ${{ needs.qualify.outputs.site_revision }} | |
| CONTROL_REVISION: ${{ github.workflow_sha }} | |
| shell: bash | |
| run: | | |
| revision=$(timeout 120s gh api "repos/$GH_REPO/git/ref/heads/main" --jq .object.sha) | |
| [[ "$revision" == "$QUALIFIED_REVISION" ]] | |
| capture="$GITHUB_WORKSPACE/predeploy-capture" | |
| bash control/scripts/Features/BenchmarkComparisons/collect-github-evidence.sh --input="$capture" --mode=publish --site-revision="$revision" --workflow-revision="$CONTROL_REVISION" | |
| node control/scripts/Features/BenchmarkComparisons/github-evidence.mjs fresh --before="$GITHUB_WORKSPACE/qualification/artifacts/site-evidence/archive-receipt.json" --after="$capture/metadata-proof.json" > predeploy-proof.json | |
| date --utc --iso-8601=seconds > predeploy-checked-at.txt | |
| - uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5 | |
| - name: Publish qualified website | |
| id: deployment | |
| uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4 | |
| - name: Record actual Pages deployment result | |
| if: always() | |
| env: | |
| DEPLOYMENT_OUTCOME: ${{ steps.deployment.outcome }} | |
| DEPLOYMENT_URL: ${{ steps.deployment.outputs.page_url }} | |
| SITE_REVISION: ${{ needs.qualify.outputs.site_revision }} | |
| shell: bash | |
| run: | | |
| jq -n --arg outcome "$DEPLOYMENT_OUTCOME" --arg url "$DEPLOYMENT_URL" --arg revision "$SITE_REVISION" --arg run_url "https://github.com/$GH_REPO/actions/runs/$GITHUB_RUN_ID" --argjson attempt "$GITHUB_RUN_ATTEMPT" '{schemaVersion:1,siteSourceRevision:$revision,qualification:{runUrl:$run_url,attempt:$attempt},provider:{outcome:$outcome,pageUrl:$url}}' > deployment-receipt.json | |
| - name: Retain freshness and deployment receipt | |
| if: always() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: site-publication-${{ github.run_id }}-${{ github.run_attempt }} | |
| path: | | |
| predeploy-capture | |
| predeploy-proof.json | |
| predeploy-checked-at.txt | |
| deployment-receipt.json |