## What & why S-16b, second of the S-16 split, on top of the #125 backplane. The five .NET services now emit OpenTelemetry traces so a request is **one connected trace** across them. - Each host wires `AddOpenTelemetry().WithTracing(...)` with `AddAspNetCoreInstrumentation` (incoming) + `AddHttpClientInstrumentation` (outgoing) + `AddOtlpExporter` to **Tempo**. - Because every cross-service call already goes through a typed `HttpClient` (§8 boundaries), the W3C `traceparent` propagates with no manual code — bff → domain → acl → openzaak and bff → projection-api stitch into a single trace. - Service name + OTLP endpoint come from `OTEL_*` env set per app service in compose. `/health` is filtered out so liveness polls don't flood the traces. No new ADR — ADR-0023 already records the stack + the two documented gaps (browser-side tracing is out of scope, so the trace begins at the BFF; the async Flowable-poll boundary is a separate trace). Closes #123 ## Definition of Done - [x] Failing test committed first (`verify-tracing` fails with no instrumentation). - [x] Implementation makes it pass — **validated locally end to end**: a real connected trace spanning `bff` + `projection-api` was found in Tempo (BFF→projection→db + Tempo subset, no OpenZaak/egress). - [x] Conventional Commits referencing the issue (`refs #123`). - [ ] CI green — awaiting Gitea Actions (verify-tracing added to verify-stack after verify-bff). - [x] `docker compose up` health unaffected — services boot healthy even when Tempo is unreachable (exporter no-ops; verified). - [x] Docs — demo-script + BACKLOG. - [x] ADR — none needed (covered by ADR-0023). ## Notes for reviewers - **Per-service wiring, no shared lib:** the block is duplicated across the five hosts by design — services don't share code across boundaries here (§8), same as the duplicated typed clients. - **Packages:** OpenTelemetry.Extensions.Hosting / Instrumentation.AspNetCore / Instrumentation.Http / Exporter.OpenTelemetryProtocol, all 1.17.0, pinned per-csproj (no central props file). - **The check** generates anonymous BFF→projection traffic (no auth, no OpenZaak), then queries Tempo (TraceQL search → fetch trace → assert both service.names present) from a python:3-slim container in-network — same idiom as run-projection-check.sh. - **Next:** #124 (S-16c) adds `/metrics` + Prometheus scrape targets + golden-signal Grafana dashboards. Reviewed-on: #126
168 lines
6.7 KiB
YAML
168 lines
6.7 KiB
YAML
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'
|
|
# Cache the NuGet package store so each .NET job restores from disk, not the network. There are
|
|
# no lock files (so setup-dotnet's built-in cache doesn't apply); key on the project files. @v3
|
|
# avoids the GHES guard that breaks @v4 on Gitea (gitea-actions-gotchas.md); cache is best-effort
|
|
# — a miss just restores from the network. See issue #73.
|
|
- uses: https://github.com/actions/cache@v3
|
|
with:
|
|
path: ~/.nuget/packages
|
|
key: nuget-${{ runner.os }}-${{ hashFiles('**/*.csproj') }}
|
|
restore-keys: |
|
|
nuget-${{ runner.os }}-
|
|
- 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'
|
|
- uses: https://github.com/actions/cache@v3
|
|
with:
|
|
path: ~/.nuget/packages
|
|
key: nuget-${{ runner.os }}-${{ hashFiles('**/*.csproj') }}
|
|
restore-keys: |
|
|
nuget-${{ runner.os }}-
|
|
- 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'
|
|
- uses: https://github.com/actions/cache@v3
|
|
with:
|
|
path: ~/.nuget/packages
|
|
key: nuget-${{ runner.os }}-${{ hashFiles('**/*.csproj') }}
|
|
restore-keys: |
|
|
nuget-${{ runner.os }}-
|
|
- 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'
|
|
- uses: https://github.com/actions/cache@v3
|
|
with:
|
|
path: ~/.nuget/packages
|
|
key: nuget-${{ runner.os }}-${{ hashFiles('**/*.csproj') }}
|
|
restore-keys: |
|
|
nuget-${{ runner.os }}-
|
|
- 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/<timestamp>/ 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: Observability backplane (Grafana + Tempo + Prometheus datasources)
|
|
run: OBS_TIMEOUT=180 make verify-observability
|
|
- 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
|
|
- name: Distributed traces reach Tempo (one connected trace across services)
|
|
run: TRACING_TIMEOUT=120 make verify-tracing
|
|
- name: Self-service e2e (Playwright, login → submit → success)
|
|
run: make verify-e2e
|
|
# 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 self-service openbaar behandel tempo prometheus grafana 2>&1 || true
|
|
- name: Tear down
|
|
if: always()
|
|
run: make down
|