# CI for the v2 branch — runs on every push + PR to the v2-akka-fuse / master # branches. Layered into two jobs: # build dotnet restore + build (fast feedback on compile errors) # unit-tests the WHOLE solution minus the env-gated tiers (self-maintaining) # # Skips E2E (Category=E2E) and LiveIntegration (needs a real gateway/PLC/VPN) — both # are env-gated tiers with no hosted-runner fixtures. (The skip-gated *.IntegrationTests # still run in the unit-tests leg and Assert.Skip when their fixtures are unreachable.) # # Compatible with both GitHub Actions and Gitea Actions (act_runner). The .NET 10 # SDK is pinned via global.json at the repo root; if no global.json exists, the # setup-dotnet step falls back to dotnet-version below. name: v2-ci on: push: branches: [v2-akka-fuse, master] pull_request: branches: [v2-akka-fuse, master] workflow_dispatch: {} env: DOTNET_NOLOGO: "1" DOTNET_CLI_TELEMETRY_OPTOUT: "1" jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-dotnet@v4 with: dotnet-version: 10.0.x # #606: code-reviews/README.md must be regenerated from the per-module findings.md # files; --check exits non-zero on drift without writing (see REVIEW-PROCESS.md section 5). - name: code-reviews README drift check run: python3 code-reviews/regen-readme.py --check - name: dotnet restore run: dotnet restore ZB.MOM.WW.OtOpcUa.slnx - name: dotnet build run: dotnet build ZB.MOM.WW.OtOpcUa.slnx --no-restore --configuration Release unit-tests: needs: build runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-dotnet@v4 with: dotnet-version: 10.0.x - name: dotnet restore run: dotnet restore ZB.MOM.WW.OtOpcUa.slnx # #606: the runner's size is the context every timing-sensitive failure needs and the log never # recorded it (load average included: the two Gitea runners may be running jobs side by side). - name: runner capacity run: | echo "nproc=$(nproc)" free -m || true uptime || true # 07/S-1: ONE whole-solution leg instead of a hand-maintained 5-project matrix that # silently dropped Client (388 tests), Analyzers (31), and every driver + most Core suite. # Self-maintaining — a new *.Tests project is covered automatically, no matrix to widen — # and matches CLAUDE.md's own `dotnet test ZB.MOM.WW.OtOpcUa.slnx` guidance. Excludes only # the env-gated tiers: E2E (nightly, needs the docker-dev fleet) and LiveIntegration (needs # a real gateway/PLC/VPN). Category=SlowUnit (Mqtt socket-timeout tests, #606) is deliberately NOT # excluded here — it is a local-dev-loop opt-out only. The skip-gated *.IntegrationTests Assert.Skip when their fixtures # are unreachable, so they run harmlessly here (and are counted by the skip-gate below). - name: dotnet test (whole solution, minus E2E + LiveIntegration) # #606: Akka TestKit time dilation for the Runtime + ControlPlane actor suites (read by their # harnesses, tests/Server/*/Harness/TestTimeFactor.cs). This leg runs every test assembly at once # on the runner, and their 2–8 s presence budgets — sized on an idle dev box — failed in a long # tail, a different test per run. Dilation only stretches waits: a presence wait still fails if # the message never comes, and absence windows get longer (stricter). Local runs stay at 1. env: AKKA_TEST_TIMEFACTOR: "3" run: > dotnet test ZB.MOM.WW.OtOpcUa.slnx --configuration Release --no-restore --filter "Category!=E2E&Category!=LiveIntegration" --logger "trx" --results-directory artifacts/trx # 07/S-2 fail-on-skip: a whole-solution run that executed ZERO tests is a masked outage # (bad filter, broken discovery), not a pass — fail loudly. Runs even on test failure so # the diagnostic is always emitted. - name: assert not-all-skipped if: always() # #606: a whole-solution run executes ~13,000 tests; 12000 catches a project silently dropping out # of discovery, not just the all-skipped case. Raise it as the suite grows. env: MIN_EXECUTED: "12000" run: bash scripts/ci/assert-not-all-skipped.sh artifacts/trx/*.trx # #606: pinned to v3. Gitea's act_runner reports itself as GHES, and @actions/artifact v2+ # (upload-artifact@v4+) refuses GHES outright ("GHESNotSupportedError") — so on Gitea the v4 # step failed EVERY run and the job could never go green, independent of the tests. v3 speaks # the artifact API Gitea implements. continue-on-error because the trx upload is evidence, not a # gate: the test step and the skip-gate above decide the job, and an artifact-store hiccup must # not turn a green run red. - name: upload trx if: always() continue-on-error: true uses: actions/upload-artifact@v3 with: name: unit-trx path: artifacts/trx/*.trx if-no-files-found: warn # NOTE (07/P-5): the former `integration` matrix job (Host.IntegrationTests + # OpcUaServer.IntegrationTests) was removed as fully redundant — the unit-tests leg # above already runs BOTH projects under the identical filter, fixtureless, and gates # them at MIN_EXECUTED=1. Its only unique output was a report-only skip tally the unit # leg's trx already contains. FOLLOW-UP (S-2 option #2): if a fixture-backed integration # leg is wanted, add it as a NEW job that starts the one public-image fixture as a # `services:` block (mcr.microsoft.com/iotedge/opc-plc; the modbus/S7/AB/FOCAS sims are # locally-built `otopcua-*` images not pullable here), sets OPCUA_SIM_ENDPOINT, and gates # at MIN_EXECUTED=1 so a fixture outage turns the job red — covering something the unit # leg cannot, rather than re-running the same fixtureless tests.