fix: the documented install path pulled :dev, not :latest (#3270)
Build images / sign-extension (push) Successful in 4s
CI / lint (push) Successful in 4s
CI / extension-version (push) Successful in 2s
Build images / build-agent (push) Successful in 7s
Build images / build-ml (push) Successful in 9s
Build images / build-web (push) Successful in 7s
CI / frontend-build (push) Successful in 21s
CI / backend-lint-and-test (push) Successful in 31s
CI / integration (push) Successful in 3m44s
Build images / sign-extension (push) Successful in 4s
CI / lint (push) Successful in 4s
CI / extension-version (push) Successful in 2s
Build images / build-agent (push) Successful in 7s
Build images / build-ml (push) Successful in 9s
Build images / build-web (push) Successful in 7s
CI / frontend-build (push) Successful in 21s
CI / backend-lint-and-test (push) Successful in 31s
CI / integration (push) Successful in 3m44s
docker-compose.yml pinned :dev on all five app services — web, worker, scheduler, maintenance-long, ml-worker. The README documents `docker compose -f docker-compose.yml up -d` as the production path, and -f means "use only this file", skipping the override and its build: directives. So Compose pulled image:, and image: was the rolling development channel. The documented way to install this product shipped development builds. It went unnoticed for a structural reason rather than a careless one: nobody who works on the project takes that path. The operator deploys from a swarm stack file; contributors get docker-compose.override.yml, which sets build: for all five services, and build: wins over image:. The broken path is reachable only by a stranger following the README — which is exactly the audience that did not exist until now. :latest, per rule 147: main IS production. It is also what the agent stack (agent/docker-compose.yml) already pinned, so this makes the two stacks agree rather than introducing a new convention. Both paths verified with `docker compose config`, which merges and prints without starting anything: dev path — build: present on all five, image: not pulled -f production — 0 build: directives, five :latest images resolved Also checked the base file for anything a stranger could not satisfy: no host-absolute volume paths, no operator-specific port bindings, no device mappings. The tag was the only defect in the consumer path. The comment on web.image is deliberately long (rule 32). A line reading :latest inside a file a developer is debugging with is exactly the line someone flips back to :dev to test something and then commits, and the consequence — strangers silently installing bleeding edge — is invisible to everyone who works here. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017QHszn9H8VBvx5Ke8x1hvw
This commit is contained in:
@@ -69,9 +69,19 @@ or a `.env` file (see `.env.example` for the variable names) and use:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker compose -f docker-compose.yml up -d
|
docker compose -f docker-compose.yml up -d
|
||||||
# (skips the override so containers pull registry images)
|
# (skips the dev override, so containers pull published :latest images)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
`-f` is doing real work there: it tells Compose to use *only* that file, which
|
||||||
|
skips `docker-compose.override.yml` and its local builds. What you get is the
|
||||||
|
`:latest` images — the stable channel, built from `main`. This is the install
|
||||||
|
path, and it is the one to use if you are running FabledCurator rather than
|
||||||
|
working on it.
|
||||||
|
|
||||||
|
`:dev` is the other channel: rebuilt from the `dev` branch several times a day,
|
||||||
|
bleeding edge, no stability promise. Nothing in this repo points an installer at
|
||||||
|
it, and nothing should.
|
||||||
|
|
||||||
The GPU agent is deployed separately, on the machine with the card —
|
The GPU agent is deployed separately, on the machine with the card —
|
||||||
`agent/docker-compose.yml`, not this stack.
|
`agent/docker-compose.yml`, not this stack.
|
||||||
|
|
||||||
|
|||||||
+23
-5
@@ -74,7 +74,25 @@ services:
|
|||||||
retries: 5
|
retries: 5
|
||||||
|
|
||||||
web:
|
web:
|
||||||
image: git.fabledsword.com/bvandeusen/fabledcurator:dev
|
# :latest, NOT :dev — this file IS the install path.
|
||||||
|
#
|
||||||
|
# `docker compose up -d` merges docker-compose.override.yml, which sets
|
||||||
|
# build: for all five app services, and a build: wins over image:. So a
|
||||||
|
# contributor never pulls this tag and is unaffected by what it says.
|
||||||
|
#
|
||||||
|
# The tag is consulted only on `docker compose -f docker-compose.yml up -d`
|
||||||
|
# — the documented production path, which skips the override. That is a
|
||||||
|
# stranger installing the product, and they must land on the stable channel.
|
||||||
|
#
|
||||||
|
# :latest is main, which IS production (rule 147). :dev is the rolling
|
||||||
|
# bleeding-edge channel we work out of, republished several times a day with
|
||||||
|
# no stability promise. This file pinned :dev on all five services until
|
||||||
|
# 2026-08-31 (#3270), so the documented install shipped development builds.
|
||||||
|
# It went unnoticed because nobody who works on the project takes this path:
|
||||||
|
# the operator deploys from a swarm stack file, contributors get the
|
||||||
|
# override. Do not "fix" this back to :dev while debugging — use the
|
||||||
|
# override, or -f with an explicit tag on the command line.
|
||||||
|
image: git.fabledsword.com/bvandeusen/fabledcurator:latest
|
||||||
command: ["web"]
|
command: ["web"]
|
||||||
# Graceful shutdown: give the container time to drain in-flight work on a
|
# Graceful shutdown: give the container time to drain in-flight work on a
|
||||||
# deploy (docker SIGTERMs, then SIGKILLs after this window — default is only
|
# deploy (docker SIGTERMs, then SIGKILLs after this window — default is only
|
||||||
@@ -122,7 +140,7 @@ services:
|
|||||||
redis: { condition: service_healthy }
|
redis: { condition: service_healthy }
|
||||||
|
|
||||||
worker:
|
worker:
|
||||||
image: git.fabledsword.com/bvandeusen/fabledcurator:dev
|
image: git.fabledsword.com/bvandeusen/fabledcurator:latest
|
||||||
command: ["worker"]
|
command: ["worker"]
|
||||||
# Drain in-flight import/thumbnail/download tasks before SIGKILL on deploy.
|
# Drain in-flight import/thumbnail/download tasks before SIGKILL on deploy.
|
||||||
stop_grace_period: 90s
|
stop_grace_period: 90s
|
||||||
@@ -142,7 +160,7 @@ services:
|
|||||||
redis: { condition: service_healthy }
|
redis: { condition: service_healthy }
|
||||||
|
|
||||||
scheduler:
|
scheduler:
|
||||||
image: git.fabledsword.com/bvandeusen/fabledcurator:dev
|
image: git.fabledsword.com/bvandeusen/fabledcurator:latest
|
||||||
command: ["scheduler"]
|
command: ["scheduler"]
|
||||||
# Quick maintenance/scan lane + beat — short tasks, modest drain window.
|
# Quick maintenance/scan lane + beat — short tasks, modest drain window.
|
||||||
stop_grace_period: 60s
|
stop_grace_period: 60s
|
||||||
@@ -163,7 +181,7 @@ services:
|
|||||||
# 30-min backup or a multi-chunk audit can never starve the 5-min recovery
|
# 30-min backup or a multi-chunk audit can never starve the 5-min recovery
|
||||||
# sweeps / vacuum (operator-flagged 2026-06-07). One slot — these are heavy.
|
# sweeps / vacuum (operator-flagged 2026-06-07). One slot — these are heavy.
|
||||||
maintenance-long:
|
maintenance-long:
|
||||||
image: git.fabledsword.com/bvandeusen/fabledcurator:dev
|
image: git.fabledsword.com/bvandeusen/fabledcurator:latest
|
||||||
command: ["worker"]
|
command: ["worker"]
|
||||||
# Longest lane (DB backups, library audits, translation backfill) — give it
|
# Longest lane (DB backups, library audits, translation backfill) — give it
|
||||||
# the most room to finish a chunk gracefully. Chunked + idempotent, so a job
|
# the most room to finish a chunk gracefully. Chunked + idempotent, so a job
|
||||||
@@ -184,7 +202,7 @@ services:
|
|||||||
redis: { condition: service_healthy }
|
redis: { condition: service_healthy }
|
||||||
|
|
||||||
ml-worker:
|
ml-worker:
|
||||||
image: git.fabledsword.com/bvandeusen/fabledcurator-ml:dev
|
image: git.fabledsword.com/bvandeusen/fabledcurator-ml:latest
|
||||||
command: ["ml-worker"]
|
command: ["ml-worker"]
|
||||||
# A single GPU inference pass can run tens of seconds — let it finish.
|
# A single GPU inference pass can run tens of seconds — let it finish.
|
||||||
stop_grace_period: 120s
|
stop_grace_period: 120s
|
||||||
|
|||||||
Reference in New Issue
Block a user