deps: Bump the test-and-analyzers group with 4 updates #304
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 / Tests | |
| on: | |
| push: | |
| branches: [ "main" ] | |
| pull_request: | |
| branches: [ "main" ] | |
| jobs: | |
| build-and-test: | |
| # Use a matrix to avoid duplicating the job for Linux and Windows | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, windows-latest] | |
| runs-on: ${{ matrix.os }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup .NET 10 | |
| uses: actions/setup-dotnet@v4 | |
| with: | |
| dotnet-version: 10.x | |
| # NuGet package cache (speeds up restore) | |
| - name: Cache NuGet packages | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.nuget/packages | |
| key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }} | |
| restore-keys: | | |
| ${{ runner.os }}-nuget- | |
| - name: Install linux components (OpenMP) | |
| if: matrix.os == 'ubuntu-latest' | |
| run: sudo apt-get update -y && sudo apt-get install -y libomp-dev | |
| - name: Security Audit (Vulnerable Packages) | |
| run: dotnet list package --vulnerable --include-transitive | |
| - name: Restore dependencies | |
| run: dotnet restore | |
| - name: Build | |
| run: dotnet build -c Release --no-restore | |
| - name: Test (Windows) | |
| if: matrix.os == 'windows-latest' | |
| run: dotnet test -c Release --no-build --verbosity normal | |
| # XPlat Code Coverage instruments every sequence point; on the numerical hot loops (matmul, autograd, | |
| # optimizers) that is a 10-900× slowdown for the training/integration tests — which is why the Linux | |
| # coverage job took minutes while the uninstrumented Windows job runs the same tests in ~25s. The | |
| # coverlet.runsettings excludes those hot namespaces from instrumentation, so coverage stays fast while | |
| # the report still covers the orchestration code (loaders, runtime, agents, data, anomalies, …). | |
| - name: Test with Coverage (Linux) | |
| if: matrix.os == 'ubuntu-latest' | |
| run: dotnet test ./Tests/Tests.csproj -c Release --no-build --verbosity normal --collect:"XPlat Code Coverage" --settings coverlet.runsettings --results-directory ./coverage | |
| # Generate and upload reports (Linux only, to avoid doing it twice) | |
| - name: Install ReportGenerator | |
| if: matrix.os == 'ubuntu-latest' | |
| run: dotnet tool install -g dotnet-reportgenerator-globaltool | |
| - name: Generate Coverage Report | |
| if: matrix.os == 'ubuntu-latest' | |
| run: reportgenerator -reports:./coverage/*/coverage.cobertura.xml -targetdir:./coverage/report -reporttypes:Html | |
| - name: Upload coverage artifact | |
| if: matrix.os == 'ubuntu-latest' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: code-coverage-report | |
| path: ./coverage/report | |
| - name: Upload coverage to Codecov | |
| if: matrix.os == 'ubuntu-latest' | |
| uses: codecov/codecov-action@v4 | |
| with: | |
| files: ./coverage/*/coverage.cobertura.xml | |
| fail_ci_if_error: true | |
| token: ${{ secrets.CODECOV_TOKEN }} # Required in V4! | |
| # NATIVE AOT GUARD: publishes the AotSmokeTest executable under Native AOT. | |
| # | |
| # The previous version of this job published Sources/Main (a class library) | |
| # under PublishAot=true. That did NOT invoke ILCompiler — Native AOT requires | |
| # an executable entry point, so publishing a library is symbolic at best. | |
| # | |
| # AotSmokeTest is a thin console exe that references DevOnBike.Overfit and | |
| # touches a slice of the public surface. With PublishAot=true on the exe, | |
| # ILCompiler actually runs end-to-end: static trim/AOT analyzers, IL→native | |
| # code generation, and native linking. With TreatWarningsAsErrors=true on | |
| # the smoketest, any IL2026 / IL3050 / IL31xx warning surfacing in the | |
| # reachable graph fails the build. | |
| # | |
| # If someone adds LINQ, Reflection, Activator, Expression, Array.Copy, raw | |
| # ArrayPool<T>.Shared (banned by RS0030), or any trim/AOT-incompatible API | |
| # to Sources/Main and it's reachable from the smoketest, this step fails. | |
| # | |
| # The job also runs the produced binary as a smoke test — a non-zero exit | |
| # from the AOT-compiled binary fails the job. | |
| aot-guard: | |
| runs-on: ubuntu-latest | |
| needs: build-and-test | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-dotnet@v4 | |
| with: | |
| dotnet-version: 10.x | |
| - name: Install Native AOT prerequisites (clang + zlib) | |
| run: sudo apt-get update -y && sudo apt-get install -y clang zlib1g-dev libomp-dev | |
| - name: Publish AotSmokeTest under Native AOT | |
| # TreatWarningsAsErrors promotes the trim/AOT analyzer warnings (IL2026 / IL3050 / IL2xxx / | |
| # IL3xxx) to errors — that is the point of this guard, and a genuine AOT regression still fails it. | |
| # * GenerateDocumentationFile=false — the smoke test does not need XML docs; this drops the | |
| # CS15xx doc-comment warning family (e.g. CS1574 unresolved cref). | |
| # The project's own advisory analyzers (OVERFIT* perf rules, IDISP* disposable style) are exempted | |
| # centrally via <WarningsNotAsErrors> in Directory.Build.props — do NOT re-add a -p:WarningsNotAsErrors | |
| # here: a global -p OVERRIDES (does not merge with) the props value and would drop the OVERFIT carve-out, | |
| # re-failing the guard on ~1000 perf suggestions unrelated to AOT. | |
| run: | | |
| dotnet publish ./Tests/AotSmokeTest/AotSmokeTest.csproj \ | |
| -c Release -r linux-x64 \ | |
| -p:PublishAot=true \ | |
| -p:TreatWarningsAsErrors=true \ | |
| -p:GenerateDocumentationFile=false \ | |
| -o ./aot-publish | |
| - name: Run AOT-compiled smoketest binary | |
| run: ./aot-publish/AotSmokeTest | |
| # The `overfit` CLI is the real Native-AOT consumer of the serve path (Sources/Server's HttpListener | |
| # OpenAI server + System.Text.Json source-gen) AND of Sources/Main via OverfitClient. Publishing it | |
| # under PublishAot=true with TreatWarningsAsErrors=true makes ILCompiler run end-to-end over that whole | |
| # reachable graph, so any IL2026 / IL3050 / IL31xx trim/AOT regression on the serve/chat path fails CI | |
| # (OVERFIT*/IDISP* advisory analyzers exempted centrally in Directory.Build.props — unrelated to AOT/trim | |
| # safety; see the smoketest step above for why a -p:WarningsNotAsErrors must NOT be re-added here). | |
| - name: Publish overfit CLI under Native AOT | |
| run: | | |
| dotnet publish ./Sources/Cli/Cli.csproj \ | |
| -c Release -r linux-x64 \ | |
| -p:PublishAot=true \ | |
| -p:TreatWarningsAsErrors=true \ | |
| -p:GenerateDocumentationFile=false \ | |
| -o ./aot-publish-cli | |
| - name: Run AOT-compiled overfit CLI (smoke) | |
| # `list` exercises the CLI end-to-end (System.CommandLine parse → command action → model-store scan) | |
| # and exits 0 with no models present — a non-blocking smoke check (unlike `serve`, which would block). | |
| run: ./aot-publish-cli/overfit list | |
| # ── Analyzer guard-of-the-guard ────────────────────────────────────────────── | |
| # The custom perf analyzer (Sources/Analyzers, OVERFIT001-015) can die SILENTLY: if its | |
| # Microsoft.CodeAnalysis.CSharp reference is NEWER than the SDK compiler's Roslyn, the compiler | |
| # rejects the whole assembly (warning CS9057) and every rule stops running — while the build | |
| # still "succeeds". This happened for real on 2026-06-12 (a 4.14 → 5.3 bump vs the SDK's 5.0). | |
| # A normal green build therefore proves nothing about the analyzer. This job proves the rules | |
| # actually FIRE: it drops a deliberate OVERFIT008 violation (an error-severity rule) into | |
| # Sources/Main and REQUIRES the build to fail with exactly that diagnostic. | |
| analyzer-guard: | |
| runs-on: ubuntu-latest | |
| needs: build-and-test | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-dotnet@v4 | |
| with: | |
| dotnet-version: 10.x | |
| - name: Tripwire — a deliberate violation must fail the build with OVERFIT008 | |
| run: | | |
| cat > Sources/Main/Runtime/__AnalyzerTripwire.cs <<'EOF' | |
| // CI tripwire (analyzer-guard job): deliberate OVERFIT008 violation — never committed. | |
| using System.Threading.Tasks; | |
| namespace DevOnBike.Overfit.Runtime | |
| { | |
| internal static class __AnalyzerTripwire | |
| { | |
| public static void Run() | |
| { | |
| Parallel.For(0, 4, i => _ = i); | |
| } | |
| } | |
| } | |
| EOF | |
| set +e | |
| OUTPUT=$(dotnet build ./Sources/Main/Main.csproj -c Release 2>&1) | |
| STATUS=$? | |
| set -e | |
| rm Sources/Main/Runtime/__AnalyzerTripwire.cs | |
| if [ "$STATUS" -eq 0 ]; then | |
| echo "$OUTPUT" | tail -30 | |
| if echo "$OUTPUT" | grep -q "CS9057"; then | |
| echo "::error::Analyzer assembly REJECTED by the compiler (CS9057) — the Microsoft.CodeAnalysis.CSharp pin in Directory.Packages.props is newer than the SDK's Roslyn. Every OVERFIT rule is silently OFF." | |
| else | |
| echo "::error::Analyzer is DEAD: the tripwire build succeeded, but error OVERFIT008 was expected." | |
| fi | |
| exit 1 | |
| fi | |
| if ! echo "$OUTPUT" | grep -q "OVERFIT008"; then | |
| echo "$OUTPUT" | tail -30 | |
| echo "::error::The tripwire build failed, but NOT with OVERFIT008 — investigate." | |
| exit 1 | |
| fi | |
| echo "Analyzer alive: the tripwire failed the build with OVERFIT008, as required." | |
| # Second tripwire: the [OverfitHotPath] escalation (OVERFIT900) can rot independently of the | |
| # rules themselves (a bad SupportedDiagnostics/Report wiring stops the error firing while every | |
| # rule still reports its warning). Drop a per-call allocation inside a [OverfitHotPath] method and | |
| # REQUIRE the build to fail with OVERFIT900. | |
| - name: Tripwire — [OverfitHotPath] must escalate a per-call rule to OVERFIT900 | |
| run: | | |
| cat > Sources/Main/Runtime/__HotPathTripwire.cs <<'EOF' | |
| // CI tripwire (analyzer-guard job): [OverfitHotPath] escalation — never committed. | |
| using DevOnBike.Overfit.Diagnostics; | |
| namespace DevOnBike.Overfit.Runtime | |
| { | |
| internal static class __HotPathTripwire | |
| { | |
| [OverfitHotPath] | |
| public static int[] Run() | |
| { | |
| return new int[4]; | |
| } | |
| } | |
| } | |
| EOF | |
| set +e | |
| OUTPUT=$(dotnet build ./Sources/Main/Main.csproj -c Release 2>&1) | |
| STATUS=$? | |
| set -e | |
| rm Sources/Main/Runtime/__HotPathTripwire.cs | |
| if [ "$STATUS" -eq 0 ]; then | |
| echo "$OUTPUT" | tail -30 | |
| echo "::error::[OverfitHotPath] escalation is DEAD: a per-call allocation inside a marked method did NOT fail the build with OVERFIT900." | |
| exit 1 | |
| fi | |
| if ! echo "$OUTPUT" | grep -q "OVERFIT900"; then | |
| echo "$OUTPUT" | tail -30 | |
| echo "::error::The hot-path tripwire build failed, but NOT with OVERFIT900 — investigate." | |
| exit 1 | |
| fi | |
| echo "Hot-path escalation alive: the tripwire failed the build with OVERFIT900, as required." |