Skip to content

CI runtime review (S22a)

Measured 2026-11-11 from gh run view <id> --json jobs over the eight latest successful ci and security runs (S19 to S22), then per step for the S22 pull request run. Decision: D-56.

Python parity: not applicable (CI only; no Python behaviour is replaced).

Before

Wall time per job in minutes (pull request runs and pushes to main):

Job Range Where the time went (S22 PR run)
dotnet 6.9 to 10.7 dotnet test 304 s, of which MicroDCS.IntegrationTests 4:58 (every unit assembly under 6 s); format 57 s; build 52 s; Sonar end 33 s; audit 17 s
integration 5.1 to 10.4 test image build 70 s (SDK pull 17 s, restore 15 s, build 29 s; no cache), the same integration suite again 4:51, replica tests 142 s (with their own runtime AOT build)
container (linux-x64) 3.0 to 7.7 image build 264 s
container (linux-arm64) 3.3 to 6.3 image build 224 s
kubernetes 3.5 its own image build 112 s, kind 83 s
trivy-image (security) 2.6 to 3.9 a fourth x64 image build, scan
gitleaks, workflows, trivy-fs 0.1 to 0.8

Critical path of a PR: 8 to 11 min (dotnet or integration). The runtime image was built five times per PR (two container builds, kubernetes, trivy-image, replica tests).

Why the integration suite took 5 minutes

Per class (local TRX, 335 test-seconds in all): SfcHostTests 295 s for 28 tests, nine of them about 30 s each. The time sat in MicroDcsHostedService.StopAsync (29.7 s, every other hosted service under 20 ms): shutdown phase 5 of D-37 waits for the expiry timers of open commands until the grace period ends, the recipes in those tests leave commands with TimeoutSeconds: 30 open, and the rig stopped the host with CancellationToken.None. Production is not affected (grace period 30 s, terminationGracePeriodSeconds: 45). With a 1 s grace period in the rig the suite takes 35 s locally instead of 6:26; the whole solution with coverage takes 48 s (1035 tests, 0 skipped).

scripts/aot-publish and the lock file

dotnet publish -r <rid> restores with the RID as a global property, which reaches every referenced project and writes the RID into src/MicroDCS.Generators/packages.lock.json (netstandard2.0, no RIDs on purpose); the next locked restore (Docker build) then fails. Reproduced on main. The script now restores without a RID first and publishes with --no-restore, as the Dockerfile does; the src/ projects declare both RIDs, so the runtime packs are restored.

Options per job

Job Option Taken
all Fix the 30 s waits in the SFC rig yes
dotnet + integration Run the integration suite once (host, with coverage) yes (D-15 revised)
docs-only PRs Skip tests and images, keep the Sonar analysis yes
container (linux-arm64) PRs only for image inputs; main and daily always yes (D-32 revised)
kubernetes PRs only for manifest, options or image inputs yes
kubernetes, integration Reuse the x64 image of container no: waits for both matrix legs (arm64 included), two builds in a row on the critical path, and a one-day artifact breaks a later re-run of the job alone (tried, dropped after /code-review high)
trivy-image Move into ci.yml to reuse the image no: belongs to the daily security scan; off the critical path
dotnet Split format and audit into a job no: after the test fix dotnet is under the container build
compose test image buildx type=gha cache in compose no: needs the runtime token exposed (marketplace action) for about 30 s

After

From the S22a pull request run (it changes ci.yml, so every job ran) and a documentation-only probe PR (#74, closed). Minutes:

Job Before After, full run After, docs-only
changes — 0.0 0.0
dotnet 6.9 to 10.7 3.2 2.3 (build and Sonar only)
integration 5.1 to 10.4 4.2 (replica tests) skipped
container (linux-x64) 3.0 to 7.7 4.0 steps skipped
container (linux-arm64) 3.3 to 6.3 3.3 steps skipped
kubernetes 3.5 3.6 skipped
trivy-image 2.6 to 3.9 3.1 steps skipped
Critical path (ci) 8 to 11 4.4 2.5

A code PR that touches neither the image inputs nor the manifest runs dotnet, container (linux-x64), integration and trivy-image: the same 4 to 4.5 min, with three runtime image builds instead of five. The critical path is now the AOT image builds (container, the replica tests in integration).