-
Notifications
You must be signed in to change notification settings - Fork 2
243 lines (223 loc) · 10.9 KB
/
Copy pathci.yml
File metadata and controls
243 lines (223 loc) · 10.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
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."