docs(readme): link to the Pages site + expand the Performance bullet … #975
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: CI | |
| on: | |
| pull_request: | |
| push: | |
| branches: [main] | |
| jobs: | |
| changes: | |
| name: Detect changes | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| pull-requests: read | |
| outputs: | |
| non-md: ${{ steps.filter.outputs.non-md }} | |
| docs-templates: ${{ steps.filter.outputs.docs-templates }} | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| fetch-depth: 0 | |
| - name: Detect non-Markdown changes | |
| id: filter | |
| env: | |
| BASE_SHA: ${{ github.event.pull_request.base.sha }} | |
| HEAD_SHA: ${{ github.event.pull_request.head.sha }} | |
| PUSH_BEFORE: ${{ github.event.before }} | |
| PUSH_SHA: ${{ github.sha }} | |
| run: | | |
| set +e | |
| ZERO_SHA="0000000000000000000000000000000000000000" | |
| if [ -n "$BASE_SHA" ] && [ -n "$HEAD_SHA" ]; then | |
| # Three-dot mirrors GitHub's PR diff (merge-base..head). | |
| files=$(git diff --name-only "$BASE_SHA...$HEAD_SHA") | |
| rc=$? | |
| elif [ -n "$PUSH_BEFORE" ] && [ -n "$PUSH_SHA" ] && [ "$PUSH_BEFORE" != "$ZERO_SHA" ]; then | |
| files=$(git diff --name-only "$PUSH_BEFORE" "$PUSH_SHA") | |
| rc=$? | |
| else | |
| echo "No valid diff range; defaulting to non-md=true and docs-templates=true" | |
| echo "non-md=true" >> "$GITHUB_OUTPUT" | |
| echo "docs-templates=true" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| if [ "$rc" -ne 0 ]; then | |
| echo "git diff failed; defaulting to non-md=true and docs-templates=true" | |
| echo "non-md=true" >> "$GITHUB_OUTPUT" | |
| echo "docs-templates=true" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| echo "Changed files:" | |
| echo "$files" | |
| if [ -z "$files" ] || echo "$files" | grep -qv '\.md$'; then | |
| echo "non-md=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "non-md=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| # Spec 041 §5.2: tier-drift triggers when a template or doc-app | |
| # changes. Templates live under docs/_pipeline/templates/, apps | |
| # under docs/_pipeline/apps/, and the doc-pipeline CLI itself | |
| # under src/Reactor.Cli/Docs/. | |
| if echo "$files" | grep -qE '^(docs/_pipeline/templates/|docs/_pipeline/apps/|src/Reactor\.Cli/Docs/)'; then | |
| echo "docs-templates=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "docs-templates=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| unit-tests: | |
| name: Unit Tests | |
| needs: changes | |
| if: needs.changes.outputs.non-md == 'true' | |
| runs-on: windows-latest | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5.2.0 | |
| with: | |
| dotnet-version: 10.0.x | |
| - name: Restore | |
| run: dotnet restore tests/Reactor.Tests/Reactor.Tests.csproj -p:Platform=x64 | |
| - name: Test | |
| run: dotnet test tests/Reactor.Tests/Reactor.Tests.csproj --no-restore -p:Platform=x64 --logger "console;verbosity=normal" | |
| integration-tests: | |
| name: Integration Tests | |
| needs: changes | |
| if: needs.changes.outputs.non-md == 'true' | |
| runs-on: windows-latest | |
| timeout-minutes: 30 | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5.2.0 | |
| with: | |
| dotnet-version: 10.0.x | |
| - name: Restore | |
| run: dotnet restore Reactor.slnx | |
| - name: Test | |
| run: dotnet test tests/Reactor.IntegrationTests/Reactor.IntegrationTests.csproj --no-restore --logger "console;verbosity=normal" | |
| selftests: | |
| name: Selftests | |
| needs: changes | |
| if: needs.changes.outputs.non-md == 'true' | |
| runs-on: windows-latest | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5.2.0 | |
| with: | |
| dotnet-version: 10.0.x | |
| - name: Restore | |
| run: dotnet restore tests/Reactor.SelfTests/Reactor.SelfTests.csproj -p:Platform=x64 | |
| - name: Test | |
| run: dotnet test tests/Reactor.SelfTests/Reactor.SelfTests.csproj --no-restore -p:Platform=x64 --logger "console;verbosity=normal" | |
| build-solution: | |
| name: Build solution | |
| needs: changes | |
| if: needs.changes.outputs.non-md == 'true' | |
| runs-on: windows-latest | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5.2.0 | |
| with: | |
| dotnet-version: 10.0.x | |
| - name: Restore | |
| run: dotnet restore Reactor.slnx | |
| - name: Build | |
| run: dotnet build Reactor.slnx --no-restore --configuration Release | |
| fuzz-smoke: | |
| name: Fuzz smoke | |
| needs: changes | |
| if: needs.changes.outputs.non-md == 'true' | |
| runs-on: windows-latest | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5.2.0 | |
| with: | |
| dotnet-version: 10.0.x | |
| # Fuzz-free pass: walk the seed corpus once through both harness bodies. | |
| # Guards against harness rot (parser API drift, broken seed inputs) without | |
| # needing the libfuzzer-dotnet driver or SharpFuzz instrumentation tool in | |
| # the CI image. Using `dotnet run` keeps the invocation independent of the | |
| # TFM / RID / Platform output-path layout, which has changed twice already. | |
| - name: Smoke run | |
| run: dotnet run --project tests/Reactor.Fuzz/Reactor.Fuzz.csproj -c Release -p:Platform=x64 -- smoke | |
| vulnerable-packages: | |
| name: Vulnerable packages | |
| needs: changes | |
| if: needs.changes.outputs.non-md == 'true' | |
| runs-on: windows-latest | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5.2.0 | |
| with: | |
| dotnet-version: 10.0.x | |
| - name: Restore | |
| run: dotnet restore Reactor.slnx | |
| # Fail the build on transitive vulnerable packages at High/Critical | |
| # severity. `dotnet list package --vulnerable` exits 0 regardless of | |
| # findings (the table is text output), so we parse the severity column | |
| # ourselves. Matches `> Package … High` / `Critical` rows; Moderate/Low | |
| # surface as warnings only. | |
| - name: Check for vulnerable packages | |
| shell: pwsh | |
| run: | | |
| $output = (dotnet list Reactor.slnx package --vulnerable --include-transitive 2>&1 | Out-String) | |
| $listExit = $LASTEXITCODE | |
| Write-Host $output | |
| # If the tool itself failed (restore error, NU* error, broken sdk), | |
| # we cannot trust the empty-findings result — fail loudly rather | |
| # than silently green-light the gate. | |
| if ($listExit -ne 0) { | |
| Write-Error "dotnet list package --vulnerable failed (exit $listExit). Cannot verify vulnerability status." | |
| exit $listExit | |
| } | |
| $bad = $output -split "`n" | Where-Object { | |
| $_ -match '^\s*>\s+\S+' -and $_ -match '\b(High|Critical)\b' | |
| } | |
| if ($bad) { | |
| Write-Error "High or Critical vulnerable package(s) detected:`n$($bad -join "`n")" | |
| exit 1 | |
| } | |
| Write-Host "No High/Critical vulnerable packages." | |
| aot-selftests: | |
| name: AOT Selftests | |
| needs: changes | |
| if: needs.changes.outputs.non-md == 'true' | |
| runs-on: windows-latest | |
| timeout-minutes: 30 | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5.2.0 | |
| with: | |
| dotnet-version: 10.0.x | |
| # Publish the selftest host with NativeAOT. PublishAotInternal=true, | |
| # Platform=x64, and rid=win-x64 are the canonical knobs documented in | |
| # docs/aot-support.md and used by tests/Reactor.AppTests.Host/probe-aot-skips.ps1. | |
| # `-o` pins the publish folder so the run step doesn't depend on the | |
| # default TFM/RID/Platform path layout. | |
| - name: Publish AOT host | |
| run: > | |
| dotnet publish tests/Reactor.AppTests.Host | |
| -p:PublishAotInternal=true | |
| -p:Platform=x64 | |
| -r win-x64 | |
| -c Release | |
| -o ${{ runner.temp }}/aot-publish | |
| --nologo | |
| # Run the published exe directly (not via `dotnet test`) so we exercise | |
| # the NativeAOT binary. SelfTestRunner.DefaultAotSkipPatterns gates the | |
| # known reflection-bound failures (40 fixtures across Devtools/MCP, | |
| # PropertyGrid auto-discovery, Issue142 XAML metadata, and two framework | |
| # cases); any *new* failure surfaces as exit code 1 and fails the job. | |
| # See docs/aot-support.md for the skip-list debugging workflow. | |
| - name: Run AOT selftests | |
| shell: pwsh | |
| run: | | |
| $exe = Join-Path '${{ runner.temp }}/aot-publish' 'Reactor.AppTests.Host.exe' | |
| if (-not (Test-Path $exe)) { | |
| Write-Error "AOT-published host not found at $exe" | |
| exit 1 | |
| } | |
| & $exe --self-test 2>&1 | Tee-Object -FilePath aot-selftest-output.tap | |
| exit $LASTEXITCODE | |
| - name: Upload TAP output | |
| if: always() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: aot-selftest-tap | |
| path: aot-selftest-output.tap | |
| if-no-files-found: ignore | |
| docs-check-tier: | |
| name: Docs tier-drift | |
| needs: changes | |
| if: needs.changes.outputs.docs-templates == 'true' | |
| runs-on: windows-latest | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5.2.0 | |
| with: | |
| dotnet-version: 10.0.x | |
| # Spec 041 §5.2 — fast tier-drift gate. Fails when a template's | |
| # declared tier no longer matches the §11 structural checklist. The | |
| # narrower `check-tier` surface runs in ~seconds vs. the full | |
| # `docs compile` (no build, capture, diagrams, or reference-gen). | |
| # `--ci` is not yet flipped on: the existing 24 W001 | |
| # winui-ref-not-declared warnings are intentional noise on | |
| # internals/meta pages and are being addressed separately as a | |
| # Phase 5 lint-quality item. Errors (REACTOR_DOC_TIER_001..012) | |
| # still fail the job. See docs/contributing/doc-pipeline.md §8. | |
| - name: Tier-lint | |
| run: dotnet run --project src/Reactor.Cli -- docs check-tier | |
| docs-build: | |
| name: Docs build | |
| needs: changes | |
| if: needs.changes.outputs.non-md == 'true' | |
| runs-on: windows-latest | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5.2.0 | |
| with: | |
| dotnet-version: 10.0.x | |
| - name: Compile docs | |
| run: dotnet run --project src/Reactor.Cli -- docs compile --no-screenshots --ci | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.12' | |
| cache: pip | |
| cache-dependency-path: docs/requirements.txt | |
| - name: Install MkDocs | |
| run: pip install -r docs/requirements.txt | |
| # Strict link/anchor check against the docs just compiled above, | |
| # mirroring the Pages publish workflow (.github/workflows/docs.yml). | |
| # `--strict` turns a broken cross-link or missing nav file into a | |
| # failure, so it fails the PR instead of the post-merge publish (the | |
| # only place --strict ran before). Folded into this job so there is a | |
| # single docs build. | |
| - name: mkdocs build --strict | |
| run: python -m mkdocs build --strict |