Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,35 @@ COMPOSE_DOMAIN=display.local.itkdev.dk
# itkdev-docker template identifier.
ITKDEV_TEMPLATE=symfony-6

# Built-in services to start (read natively by docker compose).
# traefik bundled dev traefik on :80/:443 with a self-signed cert
# generated by `task dev:cert`. Default for fresh clones.
# Leave empty (`COMPOSE_PROFILES=`) in .env.local on itkdev hosts running
# a host-level traefik on a shared `frontend` network — see opt-in below.
COMPOSE_PROFILES=traefik

# Bundled-traefik port mapping. Override if 80/443 are taken on the host.
# HTTP_PORT=80
# HTTPS_PORT=443

# The frontend network's engine name + the matching `traefik.docker.network`
# label both read from this var. Default is `<project>_frontend` (scoped
# so switching modes doesn't collide with itkdev's external `frontend`).
# COMPOSE_FRONTEND_NETWORK=display_frontend

# Itkdev devs running a host-level traefik on a shared `frontend` network:
# opt out of the bundled traefik AND opt in to the shared-frontend overlay
# in .env.local (Task forwards dotenv vars to subprocesses, including
# `docker compose`):
#
# COMPOSE_PROFILES=
# COMPOSE_FILE=docker-compose.yml:docker-compose.shared-frontend.yml
# COMPOSE_FRONTEND_NETWORK=frontend
#
# Also set `TASK_DOCKER_COMPOSE=itkdev-docker-compose` in .env.local if you
# want the legacy wrapper back — the Taskfile default is plain
# `docker compose`.

# In all environments, the following files are loaded if they exist,
# the latter taking precedence over the former:
#
Expand Down
134 changes: 134 additions & 0 deletions .github/workflows/docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
### ### Compose
###
### Validates the local dev compose stack — config-lint in every operational
### mode, plus a smoke-test that the default-mode bundled traefik actually
### serves nginx over HTTPS with the self-signed dev cert.
###
### Scoped to changes that can break the dev stack: compose files,
### bundled traefik config, the dev-cert script, and `.env` (whose
### COMPOSE_PROFILES default decides which services run).

on:
pull_request:
paths:
- "docker-compose.yml"
- "docker-compose.shared-frontend.yml"
- "docker-compose.postgres.yml"
- "traefik/**"
- "scripts/dev-cert.sh"
- ".env"
- ".github/workflows/docker-compose.yaml"

name: Docker Compose

jobs:
lint:
runs-on: ubuntu-latest
name: Lint compose (all modes)
steps:
- name: Checkout
uses: actions/checkout@v6

- name: Default mode (traefik profile)
env:
COMPOSE_PROFILES: traefik
run: docker compose config --quiet

- name: Default + dev tooling profile
env:
COMPOSE_PROFILES: traefik,dev
run: docker compose config --quiet

- name: Postgres portability overlay
env:
COMPOSE_PROFILES: traefik
COMPOSE_FILE: docker-compose.yml:docker-compose.postgres.yml
run: docker compose config --quiet

- name: Itkdev opt-in (shared-frontend overlay, bundled traefik off)
env:
COMPOSE_PROFILES: ""
COMPOSE_FILE: docker-compose.yml:docker-compose.shared-frontend.yml
COMPOSE_FRONTEND_NETWORK: frontend
run: |
docker network create frontend
docker compose config --quiet

smoke-default:
runs-on: ubuntu-latest
name: Smoke test (bundled traefik)
env:
COMPOSE_PROFILES: traefik
steps:
- name: Checkout
uses: actions/checkout@v6

- name: Generate dev cert
run: bash scripts/dev-cert.sh

# Limited service set — mariadb + phpfpm + nginx + traefik + redis.
# node + playwright are intentionally excluded; they're heavy pulls
# and not needed to validate the routing path.
- name: Bring up core services
run: docker compose up --detach --wait mariadb redis nginx traefik

- name: Probe nginx health via bundled traefik (HTTPS)
# `--resolve` aligns URL host, TLS SNI, and HTTP Host header — all
# `display.local.itkdev.dk` (matches the router rule labelled on
# nginx). Mismatched SNI vs Host makes traefik v3 return 421
# (misdirected request) even when `sniStrict` is off.
# `-k` skips cert verification — the dev cert is self-signed.
run: |
status=$(curl -kfsS -o /dev/null -w '%{http_code}' \
--resolve display.local.itkdev.dk:443:127.0.0.1 \
https://display.local.itkdev.dk/health)
echo "HTTP status: $status"
test "$status" = "200"

- name: Dump logs on failure
if: failure()
run: docker compose logs --no-color

- name: Tear down
if: always()
run: docker compose down --volumes --remove-orphans

smoke-itkdev:
runs-on: ubuntu-latest
name: Smoke test (itkdev opt-in)
steps:
- name: Checkout
uses: actions/checkout@v6

- name: Pre-create external frontend network
run: docker network create frontend

# Mirror how an itkdev dev actually opts out of the bundled traefik:
# an `.env.local` that overrides the committed `.env` defaults. The
# dev-cert.sh script re-sources `.env` (with `.env.local` last so it
# wins), so this is the only way to make the override stick.
- name: Write itkdev .env.local override
run: |
cat > .env.local <<'EOF'
COMPOSE_PROFILES=
COMPOSE_FILE=docker-compose.yml:docker-compose.shared-frontend.yml
COMPOSE_FRONTEND_NETWORK=frontend
EOF

- name: Dev cert script no-ops when bundled traefik is off
run: |
out=$(bash scripts/dev-cert.sh)
echo "$out"
echo "$out" | grep -q "skipping dev cert"
test ! -f traefik/ssl/dev.crt

- name: Bring up core services (no bundled traefik)
run: docker compose up --detach --wait mariadb redis nginx

- name: Dump logs on failure
if: failure()
run: docker compose logs --no-color

- name: Tear down
if: always()
run: docker compose down --volumes --remove-orphans
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ All notable changes to this project will be documented in this file.

## [Unreleased]

- Decoupled the dev compose stack from `itkdev-docker`: dropped the wrapper overlays in
favour of a self-contained stack with bundled traefik (opt-in via `COMPOSE_PROFILES=traefik`)
and a `docker-compose.shared-frontend.yml` overlay for devs keeping a host-level traefik.
- Added project-shared Claude Code configuration (hooks, skills, subagents, MCP servers, plugins) under `.claude/`
for consistent AI-assisted workflows on this codebase. Requires `npm install -g intelephense` once per developer
to activate the PHP language server (the `playwright` and `context7` plugins need no prerequisites).
Expand Down
39 changes: 36 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,11 +170,44 @@ The fixtures have an editor user: <editor@example.com> with the password: "apass

The fixtures have the image-text template, and two screen layouts: "full screen" and "two boxes".

### Reverse proxy & local HTTPS

A fresh clone bundles its own traefik (compose profile `traefik`, enabled by default via
`COMPOSE_PROFILES` in `.env`) that terminates TLS on `:80`/`:443` with a self-signed dev cert.
`task site-install` runs `task dev:cert` for you on first install; re-run it manually with `FORCE=1`
to regenerate (e.g. after changing `COMPOSE_DOMAIN`). The cert covers `COMPOSE_DOMAIN` plus
the `node-` variant and `localhost`. Browsers warn the first time — accept the cert once.

If 80/443 are taken on the host, override via `.env.local`:

```text
HTTP_PORT=8080
HTTPS_PORT=8443
```

### Itkdev developers: host-level traefik opt-in

Itkdev hosts already run a shared traefik on an external `frontend` network. Disable the bundled
traefik and switch to the shared-frontend overlay in `.env.local`:

```text
COMPOSE_PROFILES=
COMPOSE_FILE=docker-compose.yml:docker-compose.shared-frontend.yml
COMPOSE_FRONTEND_NETWORK=frontend
# Optional — restores the legacy wrapper:
TASK_DOCKER_COMPOSE=itkdev-docker-compose
```

Switching modes is safe: the default network is `<project>_frontend`, scoped so it never collides
with itkdev's shared `frontend` (override via `COMPOSE_FRONTEND_NETWORK`).

See `docker-compose.shared-frontend.yml` for the pre-existing-network requirement.

### Frontend dev server

The Vite dev server runs automatically inside the `node` container — it is started by `docker compose up` (and
therefore by `task compose-up` and `task site-install`) via the `command: npm run dev` entry in
`docker-compose.override.yml`. There is no separate command to run.
`docker-compose.yml`. There is no separate command to run.

HMR is served from `node-display.local.itkdev.dk` over WSS on port 443 (host configurable via `COMPOSE_DOMAIN` in
`.env`; see `vite.config.js`).
Expand Down Expand Up @@ -416,7 +449,7 @@ To test the React apps we use playwright.

It is important that the versions of the playwright container and the library imported in package.json align.

See the `docker-compose.override.yml` playwright entry and the version imported in package.json.
See the `docker-compose.yml` playwright entry and the version imported in package.json.

#### Testing on the built files

Expand Down Expand Up @@ -658,7 +691,7 @@ rather than the friendly Symfony validator message. Keep them ordered as: **PHP-
| Layer | Knob | Where it lives |
|---|---|---|
| App (Symfony validator + Admin UI) | `MEDIA_MAX_UPLOAD_SIZE_MB` (megabytes, integer) | `.env` (committed default `200`) — override in `.env.local` for development or in the deployment environment for production |
| Nginx request body | `NGINX_MAX_BODY_SIZE` (nginx size string, e.g. `200m`) | `docker-compose.yml` and `docker-compose.server.yml`; image default is `200m` (set in `infrastructure/nginx/Dockerfile`) |
| Nginx request body | `NGINX_MAX_BODY_SIZE` (nginx size string, e.g. `200m`) | `docker-compose.yml`; image default is `200m` (set in `infrastructure/nginx/Dockerfile`) |
| PHP-FPM upload + post body | `PHP_UPLOAD_MAX_FILESIZE`, `PHP_POST_MAX_SIZE` (PHP size strings, e.g. `200M`) | Operator-managed env vars on the php-fpm container (supported by the `itkdev/php8.4-fpm` base image). Not set in this repo by default — base image defaults apply unless overridden |

The app reads `MEDIA_MAX_UPLOAD_SIZE_MB` per-request, so a deploy / php-fpm worker reload is enough to pick up
Expand Down
16 changes: 15 additions & 1 deletion Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ dotenv: [".env.local", ".env"]
vars:
# https://taskfile.dev/reference/templating/
BASE_URL: "{{.TASK_BASE_URL | default .COMPOSE_SERVER_DOMAIN | default .COMPOSE_DOMAIN }}"
DOCKER_COMPOSE: '{{ .TASK_DOCKER_COMPOSE | default "itkdev-docker-compose" }}'
# Plain `docker compose` by default — works on any host with Docker
# Compose v2 installed, no itkdev wrapper required. Itkdev hosts that
# want the legacy `itkdev-docker-compose` wrapper can opt in via
# `TASK_DOCKER_COMPOSE=itkdev-docker-compose` in `.env.local`.
DOCKER_COMPOSE: '{{ .TASK_DOCKER_COMPOSE | default "docker compose" }}'

tasks:
default:
Expand Down Expand Up @@ -49,6 +53,15 @@ tasks:
- task site-open
silent: true

dev:cert:
desc: |
Generate a self-signed cert for the bundled dev traefik
(traefik/ssl/dev.{crt,key}). Idempotent — no-op if dev.crt exists;
set FORCE=1 to overwrite. Has no effect when COMPOSE_PROFILES
doesn't include `traefik` (itkdev devs using a host-level traefik).
cmds:
- sh scripts/dev-cert.sh

site-open:
desc: "Opens admin, docs and client."
cmds:
Expand Down Expand Up @@ -92,6 +105,7 @@ tasks:
compose-up:
desc: "Runs docker compose up."
cmds:
- task dev:cert
- task compose -- up --detach --remove-orphans
silent: true

Expand Down
27 changes: 0 additions & 27 deletions docker-compose.dev.yml

This file was deleted.

73 changes: 0 additions & 73 deletions docker-compose.override.yml

This file was deleted.

15 changes: 0 additions & 15 deletions docker-compose.redirect.yml

This file was deleted.

Loading
Loading