From 2d3cebf17cf57ca5730fdb95124f9696a93627a3 Mon Sep 17 00:00:00 2001 From: Alexey Zimarev Date: Fri, 21 Aug 2026 13:56:44 +0200 Subject: [PATCH 1/2] ci: share sharded test jobs between PR and preview workflows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The preview publish workflow still ran the whole solution's tests in a single dotnet test on one runner — the setup PR CI was moved away from because stacking all provider containers on one box made KurrentDB container startup (and the tests under load) flaky. Recent dev pushes failed exactly that way while the sharded PR runs stayed green. Extract the unit and integration test jobs into a reusable workflow (tests.yml) called from both pull-request.yml and preview.yml, and gate the NuGet publish job on the test matrix via needs. Test results from preview runs are now published by the Test Results workflow, replacing the inline EnricoMi step (and the Python setup it needed). Co-Authored-By: Claude Fable 5 --- .github/workflows/preview.yml | 41 +++++---- .github/workflows/pull-request.yml | 126 ++------------------------- .github/workflows/test-results.yml | 2 +- .github/workflows/tests.yml | 131 +++++++++++++++++++++++++++++ 4 files changed, 157 insertions(+), 143 deletions(-) create mode 100644 .github/workflows/tests.yml diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index 4d81d6aa0..12822a0de 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -7,44 +7,43 @@ on: paths-ignore: - 'docs/**' - '*.md' + jobs: + event_file: + # Consumed by the Test Results workflow (test-results.yml) to publish results on the commit. + name: "Event File" + runs-on: ubuntu-latest + steps: + - name: Upload + uses: actions/upload-artifact@v7 + with: + name: Event File + path: ${{ github.event_path }} + + tests: + # Same sharded jobs as PR CI — running the whole solution's tests on one runner stacked all + # provider containers at once and made KurrentDB container startup (and the tests) flaky. + name: Tests + uses: ./.github/workflows/tests.yml + nuget: runs-on: ubuntu-latest -# runs-on: [ self-hosted, type-cpx52, setup-docker, volume-cache-50GB ] + needs: tests permissions: id-token: write contents: read - checks: write -# env: -# NUGET_PACKAGES: "/mnt/cache/.nuget/packages" -# DOTNET_INSTALL_DIR: "/mnt/cache/.dotnet" steps: - - - name: Setup Python - uses: actions/setup-python@v7 - with: - python-version: 3.8 - name: Checkout code uses: actions/checkout@v7 with: + # MinVer derives the package version from Git tags, so the full history is needed fetch-depth: 0 - name: Setup .NET uses: actions/setup-dotnet@v6 with: dotnet-version: '10.0.x' - - - name: Run tests - run: dotnet test --framework net10.0 - - - name: Publish test results - uses: EnricoMi/publish-unit-test-result-action/linux@v2 - if: always() - with: - files: | - test-results/**/*.xml - test-results/**/*.trx - name: NuGet trusted publishing login id: nuget-login diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 86eac69f3..ea3cfac7c 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -12,6 +12,8 @@ concurrency: jobs: event_file: + # Consumed by the Test Results workflow (test-results.yml) to publish results with the + # correct PR context. name: "Event File" runs-on: ubuntu-latest steps: @@ -21,124 +23,6 @@ jobs: name: Event File path: ${{ github.event_path }} - unit-tests: - # Container-less test projects. Run across all target frameworks — these exercise the - # library code paths, so multi-TFM coverage matters here. No Docker needed. - name: "Build and test core (${{ matrix.dotnet-version }})" - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - dotnet-version: [ '8.0', '9.0', '10.0' ] - steps: - - - name: Checkout - uses: actions/checkout@v7 - - - name: Setup .NET - uses: actions/setup-dotnet@v6 - with: - dotnet-version: | - 8.0.x - 9.0.x - 10.0.x - - - name: Run tests - run: | - set -euo pipefail - projects=" - src/Core/test/Eventuous.Tests/Eventuous.Tests.csproj - src/Core/test/Eventuous.Tests.Application/Eventuous.Tests.Application.csproj - src/Core/test/Eventuous.Tests.Subscriptions/Eventuous.Tests.Subscriptions.csproj - src/Core/test/Eventuous.Tests.Shared.Analyzers/Eventuous.Tests.Shared.Analyzers.csproj - src/Extensions/test/Eventuous.Tests.DependencyInjection/Eventuous.Tests.DependencyInjection.csproj - src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/Eventuous.Tests.Extensions.AspNetCore.csproj - src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore.Analyzers/Eventuous.Tests.Extensions.AspNetCore.Analyzers.csproj - src/Gateway/test/Eventuous.Tests.Gateway/Eventuous.Tests.Gateway.csproj - src/Experimental/test/Eventuous.Tests.Spyglass.Generators/Eventuous.Tests.Spyglass.Generators.csproj - src/Sqlite/test/Eventuous.Tests.Sqlite/Eventuous.Tests.Sqlite.csproj - src/SignalR/test/Eventuous.Tests.SignalR/Eventuous.Tests.SignalR.csproj - " - for proj in $projects; do - echo "::group::dotnet test $proj (net${{ matrix.dotnet-version }})" - dotnet test "$proj" -c "Debug CI" -f net${{ matrix.dotnet-version }} - echo "::endgroup::" - done - - - # Uses the sample apps as fixtures, which are pinned to net10.0 for the Aspire AppHost - name: Run Spyglass tests - if: matrix.dotnet-version == '10.0' - run: dotnet test src/Experimental/test/Eventuous.Tests.Spyglass/Eventuous.Tests.Spyglass.csproj -c "Debug CI" -f net10.0 - - - name: Upload Test Results - if: always() - uses: actions/upload-artifact@v7 - with: - name: Test Results core ${{ matrix.dotnet-version }} - path: | - test-results/**/*.xml - test-results/**/*.trx - test-results/**/*.json - - integration-tests: - # One job per provider so a single runner only ever starts one container family — running - # the whole solution on one runner stacked KurrentDB + Postgres + SQL Server + Mongo + Kafka - # + RabbitMQ containers at once and made container startup (and the tests) flaky. - # - # Integration suites run on a single TFM: the DB adapter behaves the same across runtimes, so - # multi-TFM runs mostly re-pull the same images for little extra signal. max-parallel throttles - # concurrent Docker Hub image pulls to stay under the pull rate limit. - # - # When adding a new integration test project (one that uses Testcontainers), add a suite entry. - name: "Build and test ${{ matrix.suite.name }}" - runs-on: ubuntu-latest - strategy: - fail-fast: false - max-parallel: 4 - matrix: - suite: - - name: kurrentdb - project: src/KurrentDB/test/Eventuous.Tests.KurrentDB/Eventuous.Tests.KurrentDB.csproj - - name: postgres - project: src/Postgres/test/Eventuous.Tests.Postgres/Eventuous.Tests.Postgres.csproj - - name: sqlserver - project: src/SqlServer/test/Eventuous.Tests.SqlServer/Eventuous.Tests.SqlServer.csproj - - name: mongo - project: src/Mongo/test/Eventuous.Tests.Projections.MongoDB/Eventuous.Tests.Projections.MongoDB.csproj - - name: kafka - project: src/Kafka/test/Eventuous.Tests.Kafka/Eventuous.Tests.Kafka.csproj - - name: rabbitmq - project: src/RabbitMq/test/Eventuous.Tests.RabbitMq/Eventuous.Tests.RabbitMq.csproj - - name: redis - project: src/Redis/test/Eventuous.Tests.Redis/Eventuous.Tests.Redis.csproj - - name: azure-servicebus - project: src/Azure/test/Eventuous.Tests.Azure.ServiceBus/Eventuous.Tests.Azure.ServiceBus.csproj - - name: googlepubsub - project: src/GooglePubSub/test/Eventuous.Tests.GooglePubSub/Eventuous.Tests.GooglePubSub.csproj - - name: signalr-integration - project: src/SignalR/test/Eventuous.Tests.SignalR.Integration/Eventuous.Tests.SignalR.Integration.csproj - steps: - - - name: Checkout - uses: actions/checkout@v7 - - - name: Setup .NET - uses: actions/setup-dotnet@v6 - with: - dotnet-version: | - 8.0.x - 9.0.x - 10.0.x - - - name: Run tests - run: dotnet test "${{ matrix.suite.project }}" -c "Debug CI" -f net10.0 - - - name: Upload Test Results - if: always() - uses: actions/upload-artifact@v7 - with: - name: Test Results ${{ matrix.suite.name }} - path: | - test-results/**/*.xml - test-results/**/*.trx - test-results/**/*.json + tests: + name: Tests + uses: ./.github/workflows/tests.yml diff --git a/.github/workflows/test-results.yml b/.github/workflows/test-results.yml index 5854186fc..41df94d40 100644 --- a/.github/workflows/test-results.yml +++ b/.github/workflows/test-results.yml @@ -2,7 +2,7 @@ name: Test Results on: workflow_run: - workflows: ["PR Build and test"] + workflows: ["PR Build and test", "Publish Preview NuGet"] types: - completed permissions: {} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 000000000..6ccc9b532 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,131 @@ +# Reusable test workflow, called from pull-request.yml (PRs) and preview.yml (pushes to dev). +# Keeping both callers on the same sharded jobs means the preview publish gate runs the tests +# under the same conditions PR CI does — one runner per container family, never the whole +# solution on a single runner. +name: Tests + +on: + workflow_call: + +jobs: + unit-tests: + # Container-less test projects. Run across all target frameworks — these exercise the + # library code paths, so multi-TFM coverage matters here. No Docker needed. + name: "Build and test core (${{ matrix.dotnet-version }})" + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + dotnet-version: [ '8.0', '9.0', '10.0' ] + steps: + - + name: Checkout + uses: actions/checkout@v7 + - + name: Setup .NET + uses: actions/setup-dotnet@v6 + with: + dotnet-version: | + 8.0.x + 9.0.x + 10.0.x + - + name: Run tests + run: | + set -euo pipefail + projects=" + src/Core/test/Eventuous.Tests/Eventuous.Tests.csproj + src/Core/test/Eventuous.Tests.Application/Eventuous.Tests.Application.csproj + src/Core/test/Eventuous.Tests.Subscriptions/Eventuous.Tests.Subscriptions.csproj + src/Core/test/Eventuous.Tests.Shared.Analyzers/Eventuous.Tests.Shared.Analyzers.csproj + src/Extensions/test/Eventuous.Tests.DependencyInjection/Eventuous.Tests.DependencyInjection.csproj + src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore/Eventuous.Tests.Extensions.AspNetCore.csproj + src/Extensions/test/Eventuous.Tests.Extensions.AspNetCore.Analyzers/Eventuous.Tests.Extensions.AspNetCore.Analyzers.csproj + src/Gateway/test/Eventuous.Tests.Gateway/Eventuous.Tests.Gateway.csproj + src/Experimental/test/Eventuous.Tests.Spyglass.Generators/Eventuous.Tests.Spyglass.Generators.csproj + src/Sqlite/test/Eventuous.Tests.Sqlite/Eventuous.Tests.Sqlite.csproj + src/SignalR/test/Eventuous.Tests.SignalR/Eventuous.Tests.SignalR.csproj + " + for proj in $projects; do + echo "::group::dotnet test $proj (net${{ matrix.dotnet-version }})" + dotnet test "$proj" -c "Debug CI" -f net${{ matrix.dotnet-version }} + echo "::endgroup::" + done + - + # Uses the sample apps as fixtures, which are pinned to net10.0 for the Aspire AppHost + name: Run Spyglass tests + if: matrix.dotnet-version == '10.0' + run: dotnet test src/Experimental/test/Eventuous.Tests.Spyglass/Eventuous.Tests.Spyglass.csproj -c "Debug CI" -f net10.0 + - + name: Upload Test Results + if: always() + uses: actions/upload-artifact@v7 + with: + name: Test Results core ${{ matrix.dotnet-version }} + path: | + test-results/**/*.xml + test-results/**/*.trx + test-results/**/*.json + + integration-tests: + # One job per provider so a single runner only ever starts one container family — running + # the whole solution on one runner stacked KurrentDB + Postgres + SQL Server + Mongo + Kafka + # + RabbitMQ containers at once and made container startup (and the tests) flaky. + # + # Integration suites run on a single TFM: the DB adapter behaves the same across runtimes, so + # multi-TFM runs mostly re-pull the same images for little extra signal. max-parallel throttles + # concurrent Docker Hub image pulls to stay under the pull rate limit. + # + # When adding a new integration test project (one that uses Testcontainers), add a suite entry. + name: "Build and test ${{ matrix.suite.name }}" + runs-on: ubuntu-latest + strategy: + fail-fast: false + max-parallel: 4 + matrix: + suite: + - name: kurrentdb + project: src/KurrentDB/test/Eventuous.Tests.KurrentDB/Eventuous.Tests.KurrentDB.csproj + - name: postgres + project: src/Postgres/test/Eventuous.Tests.Postgres/Eventuous.Tests.Postgres.csproj + - name: sqlserver + project: src/SqlServer/test/Eventuous.Tests.SqlServer/Eventuous.Tests.SqlServer.csproj + - name: mongo + project: src/Mongo/test/Eventuous.Tests.Projections.MongoDB/Eventuous.Tests.Projections.MongoDB.csproj + - name: kafka + project: src/Kafka/test/Eventuous.Tests.Kafka/Eventuous.Tests.Kafka.csproj + - name: rabbitmq + project: src/RabbitMq/test/Eventuous.Tests.RabbitMq/Eventuous.Tests.RabbitMq.csproj + - name: redis + project: src/Redis/test/Eventuous.Tests.Redis/Eventuous.Tests.Redis.csproj + - name: azure-servicebus + project: src/Azure/test/Eventuous.Tests.Azure.ServiceBus/Eventuous.Tests.Azure.ServiceBus.csproj + - name: googlepubsub + project: src/GooglePubSub/test/Eventuous.Tests.GooglePubSub/Eventuous.Tests.GooglePubSub.csproj + - name: signalr-integration + project: src/SignalR/test/Eventuous.Tests.SignalR.Integration/Eventuous.Tests.SignalR.Integration.csproj + steps: + - + name: Checkout + uses: actions/checkout@v7 + - + name: Setup .NET + uses: actions/setup-dotnet@v6 + with: + dotnet-version: | + 8.0.x + 9.0.x + 10.0.x + - + name: Run tests + run: dotnet test "${{ matrix.suite.project }}" -c "Debug CI" -f net10.0 + - + name: Upload Test Results + if: always() + uses: actions/upload-artifact@v7 + with: + name: Test Results ${{ matrix.suite.name }} + path: | + test-results/**/*.xml + test-results/**/*.trx + test-results/**/*.json From 90c6cd2236695d3402f5395b5975dea559b9f843 Mon Sep 17 00:00:00 2001 From: Alexey Zimarev Date: Fri, 21 Aug 2026 14:23:57 +0200 Subject: [PATCH 2/2] ci: add the Azurite-backed blob storage suite to the test matrix Eventuous.Tests.Azure.Storage.Blobs was only covered by the old solution-wide preview run; the sharded matrix omitted it. The other uncovered test-named projects (Persistence.Base, Subscriptions.Base, OpenTelemetry) are IsTestProject=false base libraries whose concrete tests run inside the provider suites, so they need no matrix entries. Co-Authored-By: Claude Fable 5 --- .github/workflows/tests.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 6ccc9b532..530a197f1 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -100,6 +100,8 @@ jobs: project: src/Redis/test/Eventuous.Tests.Redis/Eventuous.Tests.Redis.csproj - name: azure-servicebus project: src/Azure/test/Eventuous.Tests.Azure.ServiceBus/Eventuous.Tests.Azure.ServiceBus.csproj + - name: azure-blobs + project: src/Azure/test/Eventuous.Tests.Azure.Storage.Blobs/Eventuous.Tests.Azure.Storage.Blobs.csproj - name: googlepubsub project: src/GooglePubSub/test/Eventuous.Tests.GooglePubSub/Eventuous.Tests.GooglePubSub.csproj - name: signalr-integration