diff --git a/Makefile b/Makefile index b6cdec2..403475b 100644 --- a/Makefile +++ b/Makefile @@ -19,6 +19,10 @@ WAIT_SVCS := openzaak nrc-web acl bff # explicit teardown. See docs/runbooks/gitea-actions-gotchas.md. SEED := bash infra/seed-config.sh CFG_VOLS := rr-oz-config rr-kc-realms rr-fl-bpmn +# Local-only stack: same services but config is bind-mounted (no seed step), so a +# plain `docker compose -f infra/docker-compose.local.yml up` works on any local +# engine. This is the no-make / Windows-friendly path. See that file's header. +LOCAL_COMPOSE := infra/docker-compose.local.yml OZ_COMPOSE := infra/openzaak/docker-compose.yml OZ_BASE := http://localhost:8000 NRC_COMPOSE := infra/opennotificaties/docker-compose.yml @@ -39,7 +43,7 @@ export DOCKER_HOST := unix://$(PODMAN_SOCK) endif endif -.PHONY: ci lint build unit smoke up down changelog openzaak-up openzaak-smoke openzaak-seed openzaak-down stack-up stack-smoke stack-down keycloak-up keycloak-smoke keycloak-down flowable-up flowable-smoke flowable-down help +.PHONY: ci lint build unit smoke up down local local-down changelog openzaak-up openzaak-smoke openzaak-seed openzaak-down stack-up stack-smoke stack-down keycloak-up keycloak-smoke keycloak-down flowable-up flowable-smoke flowable-down help ## ci: run the full pipeline — lint, build, unit, smoke (mirrors Gitea Actions) ci: lint build unit smoke @@ -79,6 +83,16 @@ down: docker compose -f $(COMPOSE) down --volumes -docker volume rm -f $(CFG_VOLS) +## local: bring up the bind-mount stack (no seed step) and wait for health +## (Windows / no-make users: run `docker compose -f infra/docker-compose.local.yml up -d --build` directly) +local: + docker compose -f $(LOCAL_COMPOSE) up -d --build + WAIT_TIMEOUT=420 bash infra/wait-healthy.sh $(WAIT_SVCS) + +## local-down: stop and remove the bind-mount stack +local-down: + docker compose -f $(LOCAL_COMPOSE) down --volumes + ## changelog: regenerate CHANGELOG.md from Conventional Commits (git-cliff) changelog: git-cliff --output CHANGELOG.md diff --git a/docs/runbooks/ci.md b/docs/runbooks/ci.md index f6445d5..704a6c8 100644 --- a/docs/runbooks/ci.md +++ b/docs/runbooks/ci.md @@ -30,6 +30,26 @@ Actions resolves them from GitHub. > bare `docker compose up` no longer self-seeds; use `make up`. See > [gitea-actions-gotchas.md](gitea-actions-gotchas.md). +## Running the stack locally without `make` (Windows / Docker Desktop) + +`make` and the bash helpers assume a Unix shell. To bring the whole stack up on a +machine without them (e.g. Windows + Docker Desktop), use the **local compose +file**, which bind-mounts the config instead of seeding volumes — so it needs no +`make`, no seed step, and no bash: + +```bash +docker compose -f infra/docker-compose.local.yml up -d --build # any engine +docker compose -f infra/docker-compose.local.yml up -d --build --wait # Docker Desktop (Compose v2) +docker compose -f infra/docker-compose.local.yml down --volumes +``` + +On Linux/macOS the same thing is wrapped as `make local` / `make local-down`. + +`infra/docker-compose.local.yml` mirrors the canonical `infra/docker-compose.yml` +but swaps the external config volumes for bind mounts — valid locally because a +local daemon can see the working directory (the seed/volume dance only exists for +the containerized CI runner). Keep the two files in sync. + ## Running CI locally (`make ci`) Until the runner exists, run the full pipeline yourself before pushing: diff --git a/infra/docker-compose.local.yml b/infra/docker-compose.local.yml new file mode 100644 index 0000000..691ec77 --- /dev/null +++ b/infra/docker-compose.local.yml @@ -0,0 +1,296 @@ +# LOCAL development stack — runs with a plain `docker compose up`, no make / no +# seed step / no bash. Use this on a local engine (Docker Desktop on Windows or +# macOS, or rootless Podman on Linux). +# +# docker compose -f infra/docker-compose.local.yml up -d --build # podman +# docker compose -f infra/docker-compose.local.yml up -d --build --wait # Docker Desktop +# docker compose -f infra/docker-compose.local.yml down --volumes +# +# It is identical to infra/docker-compose.yml EXCEPT that the three config inputs +# (OpenZaak data.yaml, Keycloak realms, Flowable BPMN) are **bind-mounted** from +# the repo instead of being streamed into external volumes by infra/seed-config.sh. +# Bind mounts work here because a local daemon can see your working directory — +# the seed dance only exists for the containerized CI runner, where it can't. See +# docs/runbooks/gitea-actions-gotchas.md. +# +# `infra/docker-compose.yml` remains the CI-canonical stack; keep the two in sync. +# +# Port map (host): +# 8000 OpenZaak · 8001 Open Notificaties · 8080 BFF · 8090 Flowable REST +# 8100 ACL · 8180 Keycloak (all admin: admin / admin — dev only) + +services: + + # ── OpenZaak (S-01) ────────────────────────────────────────────────────── + oz-db: + image: docker.io/postgis/postgis:17-3.5 + environment: + POSTGRES_USER: openzaak + POSTGRES_PASSWORD: openzaak + POSTGRES_DB: openzaak + command: postgres -c max_connections=300 + volumes: + - oz-db:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U openzaak -d openzaak && psql -U openzaak -d openzaak -c 'SELECT PostGIS_Version();' -q 2>/dev/null"] + interval: 5s + timeout: 5s + retries: 30 + start_period: 15s + networks: [cg] + + oz-redis: + image: docker.io/library/redis:7 + networks: [cg] + + oz-init: + image: docker.io/openzaak/open-zaak:${OPENZAAK_TAG:-1.28.2} + environment: &oz-env + DJANGO_SETTINGS_MODULE: openzaak.conf.docker + SECRET_KEY: ${OZ_SECRET_KEY:-dev-only-not-for-production} + DB_HOST: oz-db + DB_NAME: openzaak + DB_USER: openzaak + DB_PASSWORD: openzaak + IS_HTTPS: "no" + ALLOWED_HOSTS: "*" + CACHE_DEFAULT: oz-redis:6379/0 + CACHE_AXES: oz-redis:6379/0 + CELERY_BROKER_URL: redis://oz-redis:6379/1 + CELERY_RESULT_BACKEND: redis://oz-redis:6379/1 + DISABLE_2FA: "true" + NOTIFICATIONS_DISABLED: "true" + OPENZAAK_SUPERUSER_USERNAME: admin + DJANGO_SUPERUSER_PASSWORD: admin + OPENZAAK_SUPERUSER_EMAIL: admin@localhost + RUN_SETUP_CONFIG: "true" + command: /setup_configuration.sh + # Bind mount (`:z` relabels for SELinux on Linux; a no-op on Docker Desktop). + volumes: + - ./openzaak/setup_configuration:/app/setup_configuration:ro,z + depends_on: + oz-db: + condition: service_healthy + oz-redis: + condition: service_started + networks: [cg] + + openzaak: + image: docker.io/openzaak/open-zaak:${OPENZAAK_TAG:-1.28.2} + environment: *oz-env + healthcheck: + test: ["CMD", "python", "-c", "import requests,sys; sys.exit(0 if requests.head('http://localhost:8000/admin/').status_code in (200,302) else 1)"] + interval: 10s + timeout: 5s + retries: 10 + start_period: 30s + ports: + - "8000:8000" + depends_on: + oz-init: + condition: service_completed_successfully + networks: [cg] + + oz-celery: + image: docker.io/openzaak/open-zaak:${OPENZAAK_TAG:-1.28.2} + environment: *oz-env + command: /celery_worker.sh + depends_on: + oz-init: + condition: service_completed_successfully + networks: [cg] + + # ── Open Notificaties / NRC (S-01-c) ───────────────────────────────────── + nrc-db: + image: docker.io/postgis/postgis:17-3.5 + environment: + POSTGRES_USER: opennotificaties + POSTGRES_PASSWORD: opennotificaties + POSTGRES_DB: opennotificaties + command: postgres -c max_connections=300 + volumes: + - nrc-db:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U opennotificaties -d opennotificaties"] + interval: 5s + timeout: 3s + retries: 10 + networks: [cg] + + nrc-redis: + image: docker.io/library/redis:7 + networks: [cg] + + nrc-init: + # Migrations only (see the canonical compose / ADR-0002); no config needed. + image: docker.io/openzaak/open-notificaties:${OPENNOTIFICATIES_TAG:-1.16.1} + environment: &nrc-env + DJANGO_SETTINGS_MODULE: nrc.conf.docker + SECRET_KEY: ${NRC_SECRET_KEY:-dev-only-not-for-production} + DB_HOST: nrc-db + DB_NAME: opennotificaties + DB_USER: opennotificaties + DB_PASSWORD: opennotificaties + IS_HTTPS: "no" + ALLOWED_HOSTS: "*" + CACHE_DEFAULT: nrc-redis:6379/0 + CACHE_AXES: nrc-redis:6379/0 + CELERY_BROKER_URL: redis://nrc-redis:6379/1 + CELERY_RESULT_BACKEND: redis://nrc-redis:6379/1 + DISABLE_2FA: "true" + OPENNOTIFICATIES_SUPERUSER_USERNAME: admin + DJANGO_SUPERUSER_PASSWORD: admin + OPENNOTIFICATIES_SUPERUSER_EMAIL: admin@localhost + command: ["sh", "-c", "/wait_for_db.sh && OTEL_SDK_DISABLED=True python src/manage.py migrate"] + depends_on: + nrc-db: + condition: service_healthy + nrc-redis: + condition: service_started + openzaak: + condition: service_healthy + networks: [cg] + + nrc-web: + image: docker.io/openzaak/open-notificaties:${OPENNOTIFICATIES_TAG:-1.16.1} + environment: *nrc-env + healthcheck: + test: ["CMD", "python", "-c", "import requests,sys; sys.exit(0 if requests.head('http://localhost:8000/admin/').status_code in (200,302) else 1)"] + interval: 10s + timeout: 5s + retries: 10 + start_period: 30s + ports: + - "8001:8000" + depends_on: + nrc-init: + condition: service_completed_successfully + networks: [cg] + + nrc-celery: + image: docker.io/openzaak/open-notificaties:${OPENNOTIFICATIES_TAG:-1.16.1} + environment: *nrc-env + command: /celery_worker.sh + depends_on: + nrc-init: + condition: service_completed_successfully + networks: [cg] + + # ── Keycloak (S-02) ────────────────────────────────────────────────────── + keycloak: + image: quay.io/keycloak/keycloak:26.1 + command: ["start-dev", "--import-realm"] + environment: + KC_BOOTSTRAP_ADMIN_USERNAME: admin + KC_BOOTSTRAP_ADMIN_PASSWORD: admin + KEYCLOAK_ADMIN: admin + KEYCLOAK_ADMIN_PASSWORD: admin + KC_HEALTH_ENABLED: "true" + KC_HTTP_ENABLED: "true" + ports: + - "8180:8080" + volumes: + - ./keycloak/realms:/opt/keycloak/data/import:ro,z + networks: [cg] + + # ── Flowable (S-03) ────────────────────────────────────────────────────── + flowable-db: + image: docker.io/library/postgres:16 + environment: + POSTGRES_USER: flowable + POSTGRES_PASSWORD: flowable + POSTGRES_DB: flowable + volumes: + - flowable-db:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U flowable -d flowable"] + interval: 5s + timeout: 3s + retries: 10 + networks: [cg] + + flowable-rest: + image: docker.io/flowable/flowable-rest:latest + environment: + SPRING_DATASOURCE_DRIVER-CLASS-NAME: org.postgresql.Driver + SPRING_DATASOURCE_URL: jdbc:postgresql://flowable-db:5432/flowable + SPRING_DATASOURCE_USERNAME: flowable + SPRING_DATASOURCE_PASSWORD: flowable + ports: + - "8090:8080" + depends_on: + flowable-db: + condition: service_healthy + networks: [cg] + + flowable-init: + image: docker.io/curlimages/curl:latest + restart: "no" + volumes: + - ../workflows/registratie.bpmn:/work/registratie.bpmn:ro,z + command: + - sh + - -c + - | + base=http://flowable-rest:8080/flowable-rest/service/repository/deployments + until curl -sf -u rest-admin:test "$$base" >/dev/null 2>&1; do echo "waiting for flowable-rest..."; sleep 3; done + if curl -s -u rest-admin:test "$$base?name=registratie" | grep -q '"name":"registratie"'; then + echo "registratie already deployed; skip" + else + curl -sf -u rest-admin:test -F 'file=@/work/registratie.bpmn;filename=registratie.bpmn' "$$base" >/dev/null && echo "deployed registratie" + fi + depends_on: + flowable-rest: + condition: service_started + networks: [cg] + + # ── ACL ────────────────────────────────────────────────────────────────── + acl: + build: + context: ../services/acl + dockerfile: Dockerfile + image: register-referentie/acl:dev + environment: + Acl__OpenZaak__BaseUrl: http://openzaak:8000/ + Acl__OpenZaak__ClientId: big-reference-seed + Acl__OpenZaak__Secret: insecure-dev-secret-change-me + Acl__Defaults__Bronorganisatie: "517439943" + Acl__Defaults__VerantwoordelijkeOrganisatie: "517439943" + Acl__Defaults__Vertrouwelijkheidaanduiding: openbaar + Acl__Defaults__ZaaktypeUrl: ${ACL_ZAAKTYPE_URL:-http://openzaak:8000/catalogi/api/v1/zaaktypen/00000000-0000-0000-0000-000000000000} + ports: + - "8100:8080" + healthcheck: + test: ["CMD", "curl", "-fsS", "http://localhost:8080/health"] + interval: 5s + timeout: 3s + retries: 5 + start_period: 10s + depends_on: + openzaak: + condition: service_healthy + networks: [cg] + + # ── BFF ────────────────────────────────────────────────────────────────── + bff: + build: + context: ../services/bff + dockerfile: Dockerfile + image: register-referentie/bff:dev + ports: + - "8080:8080" + healthcheck: + test: ["CMD", "curl", "-fsS", "http://localhost:8080/health"] + interval: 5s + timeout: 3s + retries: 5 + start_period: 10s + networks: [cg] + +volumes: + oz-db: + nrc-db: + flowable-db: + +networks: + cg: