M9-S6: seller obligation fulfillment #348
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| push: | |
| branches: [ "main" ] | |
| pull_request: | |
| branches: [ "main" ] | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| pull-requests: read | |
| # Cancel in-progress runs for the same PR/branch when a new commit arrives. | |
| # Pushes to `main` are allowed to run to completion so we don't lose history. | |
| concurrency: | |
| group: ci-${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: ${{ github.event_name == 'pull_request' }} | |
| env: | |
| DOTNET_VERSION: '10.0.x' | |
| CONFIGURATION: Release | |
| DOTNET_NOLOGO: true | |
| DOTNET_CLI_TELEMETRY_OPTOUT: true | |
| jobs: | |
| changes: | |
| name: Detect changes | |
| runs-on: ubuntu-latest | |
| outputs: | |
| code: ${{ steps.filter.outputs.code }} | |
| frontend: ${{ steps.filter.outputs.frontend }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Filter paths | |
| uses: dorny/paths-filter@v3 | |
| id: filter | |
| with: | |
| # The `code` filter is true when any file relevant to building or testing | |
| # the .NET solution has changed; the `frontend` filter is true when the | |
| # client/ SPA workspace changes. Doc-only PRs (e.g. README, docs/**, *.md, | |
| # LICENSE, CODE_OF_CONDUCT) match neither, and the build/test/frontend jobs | |
| # are skipped — the final `ci` aggregator job then succeeds so branch | |
| # protection still passes. A change to this workflow file trips both filters | |
| # so the full suite re-validates when CI itself changes. | |
| filters: | | |
| code: | |
| - 'src/**' | |
| - 'tests/**' | |
| - '**/*.csproj' | |
| - '**/*.slnx' | |
| - '**/*.sln' | |
| - 'Directory.Build.props' | |
| - 'Directory.Packages.props' | |
| - 'global.json' | |
| - 'nuget.config' | |
| - '.editorconfig' | |
| - '.github/workflows/ci.yml' | |
| frontend: | |
| - 'client/**' | |
| - '.github/workflows/ci.yml' | |
| build: | |
| name: Build | |
| needs: changes | |
| if: needs.changes.outputs.code == 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@v4 | |
| with: | |
| dotnet-version: ${{ env.DOTNET_VERSION }} | |
| - name: Cache NuGet packages | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.nuget/packages | |
| key: ${{ runner.os }}-nuget-${{ hashFiles('**/Directory.Packages.props', '**/*.csproj') }} | |
| restore-keys: | | |
| ${{ runner.os }}-nuget- | |
| - name: Restore dependencies | |
| run: dotnet restore CritterBids.slnx | |
| - name: Build | |
| run: dotnet build CritterBids.slnx --no-restore --configuration ${{ env.CONFIGURATION }} | |
| - name: Publish API | |
| run: dotnet publish src/CritterBids.Api/CritterBids.Api.csproj --no-build --configuration ${{ env.CONFIGURATION }} --output ./publish | |
| - name: Upload API artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: critterbids-api | |
| path: ./publish | |
| retention-days: 5 | |
| integration-tests: | |
| name: Integration — ${{ matrix.name }} | |
| needs: changes | |
| if: needs.changes.outputs.code == 'true' | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - name: Contracts | |
| project: tests/CritterBids.Contracts.Tests/CritterBids.Contracts.Tests.csproj | |
| trx: contracts-tests.trx | |
| - name: Api | |
| project: tests/CritterBids.Api.Tests/CritterBids.Api.Tests.csproj | |
| trx: api-tests.trx | |
| - name: Participants | |
| project: tests/CritterBids.Participants.Tests/CritterBids.Participants.Tests.csproj | |
| trx: participants-tests.trx | |
| - name: Selling | |
| project: tests/CritterBids.Selling.Tests/CritterBids.Selling.Tests.csproj | |
| trx: selling-tests.trx | |
| - name: Auctions | |
| project: tests/CritterBids.Auctions.Tests/CritterBids.Auctions.Tests.csproj | |
| trx: auctions-tests.trx | |
| - name: Listings | |
| project: tests/CritterBids.Listings.Tests/CritterBids.Listings.Tests.csproj | |
| trx: listings-tests.trx | |
| - name: Settlement | |
| project: tests/CritterBids.Settlement.Tests/CritterBids.Settlement.Tests.csproj | |
| trx: settlement-tests.trx | |
| - name: Obligations | |
| project: tests/CritterBids.Obligations.Tests/CritterBids.Obligations.Tests.csproj | |
| trx: obligations-tests.trx | |
| - name: Relay | |
| project: tests/CritterBids.Relay.Tests/CritterBids.Relay.Tests.csproj | |
| trx: relay-tests.trx | |
| - name: Operations | |
| project: tests/CritterBids.Operations.Tests/CritterBids.Operations.Tests.csproj | |
| trx: operations-tests.trx | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@v4 | |
| with: | |
| dotnet-version: ${{ env.DOTNET_VERSION }} | |
| - name: Cache NuGet packages | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.nuget/packages | |
| key: ${{ runner.os }}-nuget-${{ hashFiles('**/Directory.Packages.props', '**/*.csproj') }} | |
| restore-keys: | | |
| ${{ runner.os }}-nuget- | |
| - name: Restore dependencies | |
| run: dotnet restore CritterBids.slnx | |
| - name: Build | |
| run: dotnet build CritterBids.slnx --no-restore --configuration ${{ env.CONFIGURATION }} | |
| - name: Test — ${{ matrix.name }} | |
| run: > | |
| dotnet test ${{ matrix.project }} | |
| --no-build --configuration ${{ env.CONFIGURATION }} | |
| --verbosity normal | |
| --logger "trx;LogFileName=${{ matrix.trx }}" | |
| --results-directory ./test-results | |
| - name: Upload test results | |
| uses: actions/upload-artifact@v4 | |
| if: always() | |
| with: | |
| name: integration-test-results-${{ matrix.name }} | |
| path: ./test-results | |
| retention-days: 5 | |
| frontend: | |
| name: Frontend | |
| needs: changes | |
| if: needs.changes.outputs.frontend == 'true' | |
| runs-on: ubuntu-latest | |
| defaults: | |
| run: | |
| working-directory: client | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup Node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '22' | |
| cache: 'npm' | |
| cache-dependency-path: client/package-lock.json | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Typecheck shared (tsc strict) | |
| run: npx tsc --noEmit -p shared/tsconfig.json | |
| - name: Build bidder (tsc strict + vite build) | |
| run: npm run build -w @critterbids/bidder | |
| - name: Test bidder (vitest) | |
| run: npm test -w @critterbids/bidder | |
| - name: Build ops (tsc strict + vite build) | |
| run: npm run build -w @critterbids/ops | |
| - name: Test ops (vitest) | |
| run: npm test -w @critterbids/ops | |
| - name: Build seller (tsc strict + vite build) | |
| run: npm run build -w @critterbids/seller | |
| - name: Test seller (vitest) | |
| run: npm test -w @critterbids/seller | |
| # The Playwright e2e workspace member (client/e2e) is deliberately NOT run here | |
| # (M8-S7 recorded deferral): it needs the full Aspire-orchestrated stack — Postgres, | |
| # RabbitMQ, the API host, and the bidder dev server — live, which is its own piece of | |
| # CI infrastructure work, not a step addition. It runs locally pre-merge; see | |
| # client/e2e/README.md. Its tsconfig still type-checks against the shared strict base. | |
| - name: Typecheck e2e (tsc strict, no run) | |
| run: npm run typecheck -w @critterbids/e2e | |
| # Aggregator job suitable for use as the single required status check in | |
| # branch protection. A dependent job result of `success` or `skipped` is | |
| # acceptable (a job skips when its path filter did not match — e.g. a | |
| # frontend-only PR skips the .NET jobs, a backend-only PR skips Frontend, | |
| # a doc-only PR skips all). The aggregator fails only when a job that | |
| # actually ran reports `failure` or `cancelled`. | |
| ci: | |
| name: CI | |
| needs: [ changes, build, integration-tests, frontend ] | |
| if: always() | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Verify required jobs | |
| env: | |
| BUILD_RESULT: ${{ needs.build.result }} | |
| INTEGRATION_RESULT: ${{ needs.integration-tests.result }} | |
| FRONTEND_RESULT: ${{ needs.frontend.result }} | |
| run: | | |
| set -euo pipefail | |
| failed=0 | |
| for entry in \ | |
| "build:${BUILD_RESULT}" \ | |
| "integration-tests:${INTEGRATION_RESULT}" \ | |
| "frontend:${FRONTEND_RESULT}"; do | |
| name="${entry%%:*}" | |
| result="${entry##*:}" | |
| # `success` and `skipped` pass; only a job that ran and did not | |
| # succeed (failure/cancelled) blocks the aggregator. | |
| if [ "${result}" = "failure" ] || [ "${result}" = "cancelled" ]; then | |
| echo "::error::Required job '${name}' did not succeed (result: ${result})" | |
| failed=1 | |
| fi | |
| done | |
| if [ "${failed}" -ne 0 ]; then | |
| exit 1 | |
| fi | |
| echo "All required jobs passed or were skipped." |