#!/usr/bin/env bash # LEGO Instructions Manager - container entrypoint. # # Runs as root just long enough to fix volume ownership, then waits for the # database, creates any missing tables, and drops to the unprivileged user. # # Created by: Jess Rogerson (yelling commands at Claude.AI) set -euo pipefail APP_USER=lim log() { echo "[entrypoint] $*"; } # --- volume ownership ------------------------------------------------------- # A freshly created named volume mounts as root:root, so the app user cannot # write to it until we hand it over. Cheap to repeat on every start. if [ "$(id -u)" = '0' ]; then mkdir -p "${UPLOAD_FOLDER:-/data/uploads}" "${BACKUP_FOLDER:-/data/backups}" \ "${LOG_FOLDER:-/data/logs}" chown -R "${APP_USER}:${APP_USER}" /data fi # --- wait for the database -------------------------------------------------- if [ "${DATABASE_URL:-}" != "" ] && [ "${SKIP_DB_WAIT:-0}" != "1" ]; then log "Waiting for database..." for attempt in $(seq 1 60); do if python - <<'PY' import os, sys from sqlalchemy import create_engine, text try: engine = create_engine(os.environ['DATABASE_URL'], pool_pre_ping=True) with engine.connect() as conn: conn.execute(text('SELECT 1')) except Exception as exc: print(f' not ready: {exc.__class__.__name__}', file=sys.stderr) sys.exit(1) PY then log "Database is up." break fi if [ "$attempt" = "60" ]; then log "ERROR: database did not become available in time." exit 1 fi sleep 2 done fi # --- schema ----------------------------------------------------------------- # db.create_all() is additive: it creates missing tables and leaves existing # ones untouched. It does NOT alter columns, so a schema change still needs a # migration — see docs/DOCKER_DEPLOYMENT.md. if [ "${SKIP_DB_INIT:-0}" != "1" ]; then log "Ensuring schema exists..." # No fetcher thread in this short-lived bootstrap process. INSTRUCTION_FETCH_WORKER=0 python - <<'PY' import os from app import create_app, db app = create_app(os.getenv('FLASK_ENV', 'production')) with app.app_context(): db.create_all() print('[entrypoint] Schema ready.') PY fi # --- hand over -------------------------------------------------------------- # Re-chown: create_app() ran as root above and will have created the # uploads subdirectories (pdfs/, images/, covers/, thumbnails/, extra_files/) # owned by root. Without this the app user cannot write uploads. if [ "$(id -u)" = '0' ]; then chown -R "${APP_USER}:${APP_USER}" /data log "Starting as ${APP_USER}: $*" if command -v runuser >/dev/null 2>&1; then exec runuser -u "${APP_USER}" -- "$@" else exec su -s /bin/bash -c 'exec "$0" "$@"' "${APP_USER}" -- "$@" fi fi log "Starting: $*" exec "$@"