The dev stack ran schema work from the api entrypoint while the pull and airgap stacks ran it from a dedicated migrate service. Same repo, two different init paths, and the dev one was missing a step. That gap was real, not theoretical: the first run of this service on a stack that had been up for days created 13 default settings. The entrypoint only ran `db upgrade` and `plugin upgrade-all`, never the seeds, so any patch adding a permission or a setting reached pull/airgap sites and silently skipped dev. endpoints.agentupdate was exactly that shape of change. Running init from the api entrypoint has two other problems. Under `restart: unless-stopped` a failing migration crash-loops, re-attempting partially-applied schema work and burying the original error under retry noise; the migrate service uses `restart: "no"` so a failure exits once and stays readable. And `plugin upgrade-all` was tolerated as a warning, so the app would start with plugin tables missing - the shape of the endpointlogcollections bug. Chained with && it now stops the rollout instead. No fallback path is kept in the entrypoint. Compose honours service_completed_successfully even when a single service is named, so `up -d api` still runs migrate to completion first - verified, api stays in Created until migrate exits 0. A skip-flag would have meant the safe behaviour depended on remembering to set a variable, and deployment here goes through an install script that invokes compose, so there is no second caller to protect. The entrypoint keeps only the database wait, which is about this container's readiness to serve rather than schema state. The seeds are all create-if-missing, so running them on every start is a no-op once current: the second run reported 0 permissions, 0 settings and scopes already up to date. Ordering in the chain is load-bearing - plugin tables cannot be created before the plugins are installed, and seedscopes only links permissions that already exist as rows, so seeds run last. migrate carries the full app environment including CORS_ORIGINS even though it never serves a request, because create_app() validates config before any CLI command dispatches and the production config refuses to build without an explicit allowlist. It mounts instance_data because bootstrap-plugins.sh writes plugins.json there; it and api never run concurrently, so the shared mount is not contended. The Dockerfile's standalone `docker run` note now states that the image does not migrate itself and lists the sequence to run first.
37 lines
1.3 KiB
Bash
37 lines
1.3 KiB
Bash
#!/bin/bash
|
|
# Docker container entrypoint for ShopDB Flask.
|
|
# Waits for the database to answer, then starts whatever it was given.
|
|
#
|
|
# This deliberately does NOT run migrations, plugin bootstrap or seeds. That
|
|
# sequence lives in the `migrate` service in docker-compose.yml (and its
|
|
# equivalents in the pull/airgap compose files) so there is exactly one place
|
|
# it happens. Running it here as well would mean every api restart re-attempted
|
|
# schema work, and a failing migration under `restart: unless-stopped` would
|
|
# crash-loop and bury the original error under retry noise.
|
|
#
|
|
# `up -d api` is still safe on its own: compose honours the
|
|
# service_completed_successfully dependency even when a single service is
|
|
# named, so the migrate service runs to completion first.
|
|
|
|
set -e
|
|
|
|
echo "==> ShopDB Flask Container Starting"
|
|
|
|
# Wait for database to be ready. Kept here because it is about this container's
|
|
# own readiness to serve, not about schema state. `flask db current` is a cheap
|
|
# round-trip that proves the connection works.
|
|
if [ -n "$DATABASE_URL" ]; then
|
|
echo "==> Waiting for database..."
|
|
for i in {1..30}; do
|
|
if flask db current &>/dev/null; then
|
|
echo "==> Database ready"
|
|
break
|
|
fi
|
|
echo " Attempt $i/30: database not ready, waiting..."
|
|
sleep 2
|
|
done
|
|
fi
|
|
|
|
echo "==> Starting application"
|
|
exec "$@"
|