Speed up CI and format and lint CLI commands#895
Conversation
0440bae to
1da4403
Compare
0440bae to
acacc4b
Compare
acacc4b to
69d9c50
Compare
0592515 to
719edf2
Compare
f80592d to
9587ec5
Compare
…w with scope detection
…detect-changes to detect-scope
9587ec5 to
3d75764
Compare
…ests so Features and Beta sections render on default seed
E2E test fix piggy-backed on this branchTwo e2e tests inherited from the just-merged feature-flags PR were failing on a default-seeded database:
Root cause: Fix: Tests now activate the kill-switch flags via the back-office activate endpoint during setup. No production code touched, no seeder change, no conditional branches on flag state in the tests. Result locally: 35/35 passed. Strictly speaking this is a feature-flags PR follow-up, not a CI-perf change, but landing it on this branch unblocks CI green here. |
…ation race across parallel test assemblies
|



Summary & Motivation
Three coordinated changes that cut CI critical path roughly in half and speed up local format and lint runs.
Test infrastructure
BackOfficeEndpointBaseTestandEndpointBaseTest<TContext>each constructed a freshWebApplicationFactory<Program>per test instance. Combined withMeziantou.Xunit.ParallelTestFramework, every account-API test booted a complete ASP.NET Core host: full DI container, all singletons, test server. With 27 BackOffice classes and ~60 Account API classes, every CI run paid hundreds of cold-starts, and the cost grew with every test added.Both test bases now share one host per test class via xUnit's
IClassFixture<...>:BackOfficeWebApplicationFactory+BackOfficeTestContextroute per-test state (SqliteConnection, telemetry collector, MockStripeState) through anAsyncLocal. 26 derived test classes use the fixture via primary constructor +IClassFixture<BackOfficeWebApplicationFactory>.BackOfficeBlobProxyTestscarries a derived factory to keep itsIBlobStorageClientsubstitute.AccountWebApplicationFactory+AccountTestContextmirror the same pattern (adds per-testIEmailClientrouting). 60 derived test classes converted.CompleteEmailSignupTestscarries a derived factory for its logger override.MockStripeStateis re-registered as transient in the test host so per-request resolution reads the per-test instance from the accessor.TestServer.PreserveExecutionContextenabled so the AsyncLocal flows from the calling test into request handling.Single Code Style workflow
A new
code-style.ymlreplaces the per-SCS code-style work inaccount.yml,main.yml,app-gateway.yml. One workflow, four jobs:detect-scope— small first job that classifies the diff and outputs the backend scope and format mode for the downstream jobs (inlinegit diff, no third-party action).code-linting— backend inspectcode + frontend build + frontend lint (oxlint).code-formatting— backend cleanupcode + frontend format check (oxfmt).sonarcloud— runs once per push againstapplication/PlatformPlatform.slnx(was previously running three times per push, once per SCS).Backend lint and format are scoped to a single SCS via
--self-contained-system <name>when the diff touches only that SCS; cross-cutting changes (shared-kernel, AppGateway, multi-SCS) fall back to the full solution.Faster format and lint
formatcommand defaults to changed-only. Cleanupcode runs against.csfiles diffed vsorigin/main; explicit--all-filesfor full sweep. CI auto-flips to--all-fileswhenapplication/dotnet-tools.jsonchanges (JetBrains tool upgrades reset the format ruleset, so accumulated drift in untouched files needs a full pass).lint --changed-onlyflag is opt-in (default = full). Recommended for routine local runs; CI always lints the full solution because inspectcode has cross-file rules. The lint skill documents when to omit--changed-only(cross-cutting changes that affect untouched files)..slnffrom each.slnxto feedcleanupcode— JetBrains 2026.1 accepts.slnxdirectly.Database Plan reuses Build and Test artifacts
build-and-testuploadsapplication/**/binandapplication/**/objas a<scs>-buildartifact (only when staging is enabled)._migrate-database.ymldownloads it intoapplication/and runsdotnet restorefor the NuGet cache, skipping the redundant checkout + setup +dotnet buildchain.CI benchmark
Comparing real PR-sync workflow durations on a comparable sister branch (PR #888, n=8 per workflow) against this branch (n=3 per workflow, mean):
The AppGateway delta is large because
code-lintingandcode-formattingwere removed fromapp-gateway.ymlentirely — they live in the consolidatedcode-style.ymlnow. AppGateway PR runs are just build + test.Note for downstream projects
Downstream projects with a substantial Main SCS will see a bigger lift than this benchmark suggests. The slowest single CI step used to be backend
cleanupcode; it now only inspects changed.csfiles. Evidence: Main and Account workflows now sit at roughly the same wall-clock (5m47s vs 6m16s) even though Account has materially more code than Main. The fixed-cost setup (npm + dotnet restore + build) dominates once the format pass is changed-only, so SCS size matters less than it used to.Test-step benchmark (isolated)
To isolate the impact of the test-host refactor alone, I temporarily disabled the Code Style and Database Staging jobs and measured just
Run Tests with SonarScanner Analysis(5 samples per side):Δ −37% on the test step alone, and stddev more than halves — the runs are markedly more stable too.
Checklist