name: CI on: push: branches: [main] pull_request: branches: [main] permissions: contents: read # Self-hosted runner — see docs/runbooks/ci.md for the runner setup. # `uses:` are absolute, tag-pinned URLs (CLAUDE.md §8.7 / §15). # Each job calls a `make` target — the same one developers run locally # (`make ci`). The Makefile is the single source of truth; see docs/runbooks/ci.md. jobs: lint: runs-on: ubuntu-latest steps: - uses: https://github.com/actions/checkout@v4 - uses: https://github.com/actions/setup-dotnet@v4 with: dotnet-version: '10.0.x' - run: make lint build: runs-on: ubuntu-latest steps: - uses: https://github.com/actions/checkout@v4 - uses: https://github.com/actions/setup-dotnet@v4 with: dotnet-version: '10.0.x' - run: make build unit: runs-on: ubuntu-latest steps: - uses: https://github.com/actions/checkout@v4 - uses: https://github.com/actions/setup-dotnet@v4 with: dotnet-version: '10.0.x' - run: make unit # Frontend (Nx/Angular) lane: install with pnpm, then Nx lint + test + build. frontend: runs-on: ubuntu-latest steps: - uses: https://github.com/actions/checkout@v4 - uses: https://github.com/pnpm/action-setup@v4 with: version: 11 - uses: https://github.com/actions/setup-node@v4 with: node-version: '24' cache: 'pnpm' - run: make frontend mutation: runs-on: ubuntu-latest steps: - uses: https://github.com/actions/checkout@v4 - uses: https://github.com/actions/setup-dotnet@v4 with: dotnet-version: '10.0.x' - run: make mutation # Publish the Stryker HTML reports. `if: always()` uploads them even when the # ratchet fails — that is exactly when you want to inspect the survivors. # `continue-on-error` keeps the upload best-effort: the mutation *gate* is the # ratchet (make mutation's exit code), not the report, so a Gitea artifact-backend # 500 must not fail the job (gitea-actions-gotchas.md §4). Glob handles Stryker's # non-deterministic StrykerOutput// dir. Pinned @v3: @v4's bundled # @actions/artifact hard-aborts on non-github.com (GHES guard) — see the runbook. - uses: https://github.com/actions/upload-artifact@v3 if: always() continue-on-error: true with: name: acl-mutation-report path: services/acl/StrykerOutput/**/reports/mutation-report.html if-no-files-found: warn - uses: https://github.com/actions/upload-artifact@v3 if: always() continue-on-error: true with: name: event-subscriber-mutation-report path: services/event-subscriber/StrykerOutput/**/reports/mutation-report.html if-no-files-found: warn - uses: https://github.com/actions/upload-artifact@v3 if: always() continue-on-error: true with: name: domain-mutation-report path: services/domain/StrykerOutput/**/reports/mutation-report.html if-no-files-found: warn - uses: https://github.com/actions/upload-artifact@v3 if: always() continue-on-error: true with: name: bff-mutation-report path: services/bff/StrykerOutput/**/reports/mutation-report.html if-no-files-found: warn # One stage for every check that needs the live stack. On the single self-hosted # runner jobs run sequentially, so booting OpenZaak once (instead of once per job) # is the cheapest layout (issue #58). No setup-dotnet: the ACL test runs in a built # image and everything reaches services by container IP. Needs Docker + egress # (base images, nuget, selectielijst.openzaak.nl). verify-stack: runs-on: ubuntu-latest steps: - uses: https://github.com/actions/checkout@v4 # Bring the full stack up + wait for health — this also is the DoD "compose up # reaches green health" smoke (it replaces the old compose-smoke job). - name: Bring up the full stack & wait for health run: make verify-up - name: ACL ↔ OpenZaak integration tests run: make verify-acl - name: OpenZaak → NRC notification delivery run: make verify-nrc - name: OpenZaak → NRC → Event Subscriber → projection-api run: make verify-projection - name: Domain → Flowable → ACL → OpenZaak run: make verify-domain - name: BFF → Keycloak + domain + projection run: make verify-bff # Log dump must precede teardown (which removes the containers). - name: Dump container logs on failure if: failure() run: docker compose -f infra/docker-compose.yml logs --no-color --tail=100 oz-init openzaak nrc-init nrc-web nrc-celery nrc-beat flowable-db flowable-rest flowable-init keycloak acl bff domain projection-db event-subscriber projection-api 2>&1 || true - name: Tear down if: always() run: make down