diff --git a/docs/getting-started.md b/docs/getting-started.md index 4664c03..d4d7cc6 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -28,7 +28,7 @@ Each directory holds one compose file per source of images: A third path, [manual setup](#manual-setup), runs the binaries without containers. !!! info "Both environments can run at once" - They use separate Compose projects (`ignis-http`, `ignis-https`), separate generated container names and separate database volumes, so both can run at once. Each has its own database and so needs its own seeding. + They use separate Compose projects (`ignis-http`, `ignis-https`), separate generated container names and separate database volumes, so both can run at once. Each has its own database and seeds it on its own `up`. !!! warning "Upgrading from a checkout made before the project rename" The Compose project names are now `ignis-http` and `ignis-https`, previously `building-simulation` for both, and the fixed container names are gone. An existing stack has to come down first, because the old containers still hold those names. Remove them by name, which reaches nothing but ignis: @@ -40,7 +40,7 @@ A third path, [manual setup](#manual-setup), runs the binaries without container Do not use `docker compose -p building-simulation down`. That targets the project rather than this repository, so on a machine where another service still declares the old project name it removes that service's containers as well, naming neither. - Each environment now has its own database volume, so seed each one once after starting it. The previous shared volume, `building-simulation_ignis-db-data`, is left in place and no longer mounted; remove it with `docker volume rm building-simulation_ignis-db-data` once the new ones are populated. + Each environment now has its own database volume, and each seeds itself on its first `up`. The previous shared volume, `building-simulation_ignis-db-data`, is left in place and no longer mounted; remove it with `docker volume rm building-simulation_ignis-db-data` once the new ones are populated. If `tentacron-net` already exists from `make tentacron-stack`, `up` fails with a label mismatch, because a network made by `docker network create` carries no Compose labels. Remove it with `docker network rm tentacron-net` while nothing is attached, and the first stack up recreates it correctly. @@ -107,7 +107,7 @@ To build from this checkout instead, so local code changes are picked up, drop t ### 2. Verify -`up` seeds an empty database before it starts `ignis`, so there is no separate seed step (see [Seeding the database](#seeding-the-database)). Follow [Verifying](#verifying). The base URL is `http://localhost:8088`. +`up` seeds an empty database before it starts `ignis` (see [Seeding the database](#seeding-the-database)). Follow [Verifying](#verifying). The base URL is `http://localhost:8088`. --- @@ -144,7 +144,7 @@ docker compose -f docker-compose.quickstart.yml up -d This starts `db`, then `ignis` once the database reports healthy, then `proxy` once the app reports healthy. Only the proxy publishes a host port (`HOST_HTTPS_PORT`, default `443`). -Then [Seeding the database](#seeding-the-database) and [Verifying](#verifying). +`up` seeds an empty database before it starts `ignis` (see [Seeding the database](#seeding-the-database)). Then [Verifying](#verifying). ### From source, with a trusted certificate @@ -170,7 +170,7 @@ docker compose up -d !!! warning "CADDY_DATA_DIR" A wrong value silently produces an untrusted certificate. A missing one fails validation instead, reported by name: `required variable CADDY_DATA_DIR is missing a value`. -Then [Seeding the database](#seeding-the-database) and [Verifying](#verifying). +`up` seeds an empty database before it starts `ignis` (see [Seeding the database](#seeding-the-database)). Then [Verifying](#verifying). --- @@ -178,28 +178,16 @@ Then [Seeding the database](#seeding-the-database) and [Verifying](#verifying). The TABULA workbook is baked into the `build-db` image, so there is nothing to download. A seed runs in one transaction: if it fails, nothing is committed and any previous tables stay. -`` below is `docker compose` for `docker-compose.yml`, or `docker compose -f ` for the others. - -### environment/http - `up` runs `build-db -if-empty` and starts `ignis` only once it exits successfully. An empty database is seeded, a populated one is left unchanged, and a failed seed fails `up` with `service "build-db" didn't complete successfully`. -To rebuild a populated database from the workbook, which drops and recreates all country tables: - -```bash - run --rm build-db -if-empty=false -``` +!!! warning "Rebuilding is destructive" + Rebuilding a populated database from the workbook drops and recreates all country tables: -### environment/https - -!!! warning "Required before first use, and destructive" - A fresh `db` volume is empty. Seeding drops and recreates all country tables, so it is gated behind the `seed` profile and never runs automatically. Until it has run once, every endpoint that reads the schema will fail. - -```bash - --profile seed run --rm build-db -``` + ```bash + run --rm build-db -if-empty=false + ``` -On a path that pulls images, `--profile seed` is also needed on the `pull`, since profile-gated services are otherwise skipped. + `` is `docker compose` for `docker-compose.yml`, or `docker compose -f ` for the others. ## Verifying @@ -222,20 +210,20 @@ Both return `200`. Add `-k` on the https quickstart path, where the certificate down -v ``` -The `-v` removes the database volume, so the next start seeds again (http) or needs seeding again (https). Omit it to keep the seeded data. +The `-v` removes the database volume, so the next `up` seeds again. Omit it to keep the seeded data. ## Pinning a version The paths that pull images default to the `latest` published release. To pin a specific one, set `IGNIS_IMAGE_TAG` before starting: ```bash -export IGNIS_IMAGE_TAG=0.2.4-alpha - --profile seed pull +export IGNIS_IMAGE_TAG=0.7.0 + pull up -d ``` !!! info "Tag format" - Published image tags carry no `v` prefix, even though the Git tags do. Release `v0.2.4-alpha` publishes as `0.2.4-alpha`. Export the variable rather than prefixing a single command, so the app and seed images come from the same release. + Published image tags carry no `v` prefix, even though the Git tags do. Release `v0.7.0` publishes as `0.7.0`, the first release whose `build-db` accepts `-if-empty`. Export the variable rather than prefixing a single command, so the app and seed images come from the same release. --- @@ -313,7 +301,7 @@ Copy across the whole directory: the compose file, `.env`, the `env/` directory, | Compose service | Published image | Role | |---|---|---| | `ignis` | `ghcr.io/thd-spatial-ai/ignis` | HTTP API server | - | `build-db` | `ghcr.io/thd-spatial-ai/ignis-build-db` | TABULA seeder: on `up` in `environment/http`, `seed` profile only in `environment/https` | + | `build-db` | `ghcr.io/thd-spatial-ai/ignis-build-db` | TABULA seeder, runs on `up` and exits | | `db` | `postgres:17-alpine` (not built here) | PostgreSQL database | Service names and image names are independent: the seeder's image keeps the `ignis-` prefix it publishes under. `IGNIS_IMAGE_TAG` pins both `ghcr.io` images to one release. The service is named `ignis` because a service name is registered as a DNS alias on every network it joins, `tentacron-net` included, so it has to be unique across the workspace; `db` and `build-db` never join it and so stay short. @@ -340,27 +328,17 @@ Set `IGNIS_IMAGE_TAG` to pin a release. This is strongly advised for any deploym !!! warning "Set IGNIS_SITE_ADDRESS before deploying" It defaults to `localhost`. Set it in `env/proxy.env` to the deployment's real domain, or Caddy will neither serve it nor provision a certificate for it. TLS-ALPN-01 (Caddy's default ACME challenge) works entirely over port 443, which is all the compose file publishes, so no port change is needed. -### 4. Pull, start, seed +### 4. Pull and start -For `environment/http`, `up` seeds an empty database itself: +`up` seeds an empty database itself. ```bash docker compose -f docker-compose.prod.yml pull docker compose -f docker-compose.prod.yml up -d ``` -For `environment/https`: - -```bash -docker compose -f docker-compose.prod.yml --profile seed pull -docker compose -f docker-compose.prod.yml up -d -docker compose -f docker-compose.prod.yml --profile seed run --rm build-db -``` - !!! warning "Pull first" - With `IGNIS_IMAGE_TAG` unset, a host that already has `latest` cached keeps running the old build after a release, with nothing on that host revealing it. On `environment/https`, the `--profile seed` on the pull is what fetches `build-db`, since profile-gated services are otherwise skipped. - -On `environment/https`, seed only on first deployment. Running it against a populated database drops every country table. + With `IGNIS_IMAGE_TAG` unset, a host that already has `latest` cached keeps running the old build after a release, with nothing on that host revealing it. ### 5. Verify diff --git a/environment/https/docker-compose.prod.yml b/environment/https/docker-compose.prod.yml index e3f4f53..9c10004 100644 --- a/environment/https/docker-compose.prod.yml +++ b/environment/https/docker-compose.prod.yml @@ -38,12 +38,11 @@ # The Caddyfile's site address must match the deployment's real domain, not # `localhost`, or Caddy will not serve or provision a certificate for it. # -# Seeding is a one-off admin operation, gated behind the `seed` profile so it -# never runs on `up`. It DROPS and recreates all country tables from the -# TABULA workbook baked into the ignis-build-db image, so it needs no source -# checkout, but run it deliberately and never against a populated production -# database you care about: -# docker compose -f docker-compose.prod.yml --profile seed run --rm build-db +# `up` seeds an empty database from the TABULA workbook baked into the +# ignis-build-db image and leaves a populated one alone. ignis starts only +# after build-db exits successfully, so a failed seed fails `up`. Rebuilding a +# populated database DROPS and recreates all country tables: +# docker compose -f docker-compose.prod.yml run --rm build-db -if-empty=false # # No endpoint carries an API key. This is deployed only on a network whose # access is controlled, and callers are already authenticated by the platform @@ -79,6 +78,8 @@ services: depends_on: db: condition: service_healthy + build-db: + condition: service_completed_successfully healthcheck: test: ["CMD-SHELL", "wget -q -O /dev/null http://localhost:$$APP_PORT/ignis/health"] interval: 30s @@ -107,7 +108,7 @@ services: build-db: image: ghcr.io/thd-spatial-ai/ignis-build-db:${IGNIS_IMAGE_TAG:-latest} - profiles: [seed] + command: ["-if-empty"] # needs an image from v0.7.0 or later env_file: [env/app.env] depends_on: db: diff --git a/environment/https/docker-compose.quickstart.yml b/environment/https/docker-compose.quickstart.yml index 228719c..421a9ef 100644 --- a/environment/https/docker-compose.quickstart.yml +++ b/environment/https/docker-compose.quickstart.yml @@ -8,16 +8,14 @@ # # --- # Usage: docker compose -f docker-compose.quickstart.yml up -d -# docker compose -f docker-compose.quickstart.yml --profile seed run --rm build-db # --- # -# Seed the database once after first start, using the command above. -# build-db is gated behind the `seed` profile so it never runs on `up`: -# it DROPS and recreates all country tables from the TABULA workbook baked -# into its image. The workbook is in the image itself (see -# ignis-db.dockerfile), so seeding needs no source checkout and no manual -# download. Until it has run once, the schema is empty and every endpoint -# that reads it will fail. +# `up` seeds an empty database from the TABULA workbook baked into the +# ignis-build-db image (see ignis-db.dockerfile), so seeding needs no source +# checkout and no manual download. A populated database is left alone, and +# ignis starts only after build-db exits successfully. Rebuilding a populated +# database DROPS and recreates all country tables: +# docker compose -f docker-compose.quickstart.yml run --rm build-db -if-empty=false # # Trade-off vs. docker-compose.prod.yml: Caddy's local CA lives in a # Docker-managed volume (caddy-data) instead of a host bind mount, so it is @@ -62,6 +60,8 @@ services: depends_on: db: condition: service_healthy + build-db: + condition: service_completed_successfully healthcheck: test: ["CMD-SHELL", "wget -q -O /dev/null http://localhost:$$APP_PORT/ignis/health"] interval: 30s @@ -87,7 +87,7 @@ services: build-db: image: ghcr.io/thd-spatial-ai/ignis-build-db:${IGNIS_IMAGE_TAG:-latest} - profiles: [seed] + command: ["-if-empty"] # needs an image from v0.7.0 or later env_file: [env/app.env] depends_on: db: diff --git a/environment/https/docker-compose.yml b/environment/https/docker-compose.yml index 2e06c94..ff38ce2 100644 --- a/environment/https/docker-compose.yml +++ b/environment/https/docker-compose.yml @@ -5,7 +5,6 @@ # # --- # Usage: docker compose up -d -# docker compose --profile seed run --rm build-db # --- # # Caddy terminates TLS in front of the app and does nothing else: ignis handles @@ -66,6 +65,8 @@ services: depends_on: db: condition: service_healthy + build-db: + condition: service_completed_successfully # No `ports:` mapping here on purpose — the app is not reachable from the # host directly. Only the reverse proxy is exposed; everyone else must go through it. # No `command:` override needed — the image's own CMD runs the server @@ -77,14 +78,12 @@ services: retries: 3 start_period: 10s - # One-off DB seed/rebuild job, NOT part of the normal `up` path (see - # `profiles` below). build_db DROPS and recreates all country tables from - # the Excel workbook — invoke deliberately, never automatically on startup: - # docker compose --profile seed run --rm build-db - # No `container_name`: this is only ever started via `run`, which assigns - # its own name, and a fixed one would collide rather than help. + # Seeds an empty database on `up` and exits; a populated one is left alone + # (-if-empty) and ignis waits for it to succeed. Rebuilding a populated + # database DROPS and recreates all country tables: + # docker compose run --rm build-db -if-empty=false build-db: - profiles: ["seed"] + command: ["-if-empty"] build: context: ./../.. dockerfile: environment/ignis-db.dockerfile diff --git a/environment/ignis-db.dockerfile b/environment/ignis-db.dockerfile index a5350c2..bf8edc2 100644 --- a/environment/ignis-db.dockerfile +++ b/environment/ignis-db.dockerfile @@ -2,8 +2,8 @@ # One-off DB rebuild job, not a long-running service. With no arguments # build_db DROPS and recreates all country tables from the Excel workbook. -# environment/http runs it on `up` with -if-empty, which leaves a populated -# database alone; the other environments gate it behind the `seed` profile. +# Every compose file runs it on `up` with -if-empty, which leaves a populated +# database alone. # # Despite the filename, this builds the `ignis-build-db` seeder image, not # the database. The database is the `ignis-db` service, plain postgres with