docker-entrypoint.sh 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
  1. #!/usr/bin/env bash
  2. # R01-N22: deploy-time migrations.
  3. #
  4. # Apply any pending SQL migrations before Apache starts to serve traffic, so
  5. # the request path can simply CHECK the schema state and refuse to serve when
  6. # something is unexpectedly out of date — no half-applied DDL hazard.
  7. #
  8. # Failure here aborts the container start (we exit non-zero) so the operator
  9. # notices in `docker logs`. Apache otherwise picks up the trailing args
  10. # verbatim (CMD `apache2-foreground`).
  11. set -euo pipefail
  12. APP_ROOT="${APP_ROOT:-/var/www/html}"
  13. DATA_PATH="${DATA_PATH:-/var/www/data}"
  14. # R01-N27: session-storage path (defaults match Dockerfile + .env.example).
  15. SESSION_PATH="${SESSION_PATH:-/var/www/data/sessions}"
  16. # R01-N27: session-file lifetime in minutes. Default 480 (= 8h), matching
  17. # `session.gc_maxlifetime` set by `SessionGuard::start()`. Operators may
  18. # override via the container env (e.g. tighten on a public deployment).
  19. SESSION_GC_MAX_AGE_MINUTES="${SESSION_GC_MAX_AGE_MINUTES:-480}"
  20. # R01-N27: how often to sweep, in seconds. Default 3600 (= once per hour).
  21. SESSION_GC_INTERVAL_SECONDS="${SESSION_GC_INTERVAL_SECONDS:-3600}"
  22. # Self-heal data-dir ownership: when a host bind mount is attached at
  23. # /var/www/data (dev compose) the host's uid/gid masks the Dockerfile's
  24. # `chown www-data` and SQLite + session writes fail with a confusing
  25. # "readonly database". Fix it on every start while we're still root —
  26. # once before migrate so it can open the DB, and once after, because
  27. # migrate runs as root and would otherwise leave new SQLite files
  28. # (and the WAL/SHM siblings) owned by root.
  29. mkdir -p "${DATA_PATH}" "${SESSION_PATH}"
  30. chown -R www-data:www-data "${DATA_PATH}"
  31. echo "[entrypoint] running deploy-time migrations…"
  32. php "${APP_ROOT}/bin/migrate.php"
  33. chown -R www-data:www-data "${DATA_PATH}"
  34. # R01-N27: PHP's built-in session GC fires probabilistically off request
  35. # traffic, so a low-traffic deployment keeps stale session files for days
  36. # past `gc_maxlifetime`. This backgrounded loop deletes session files
  37. # older than $SESSION_GC_MAX_AGE_MINUTES every $SESSION_GC_INTERVAL_SECONDS.
  38. # It is a child of this script's PID 1, so a `docker stop` propagates and
  39. # tears it down cleanly along with Apache. No new package dependency — only
  40. # coreutils' `find`, already present in the php:8.3-apache base image.
  41. if [ -d "${SESSION_PATH}" ]; then
  42. echo "[entrypoint] starting session GC loop (path=${SESSION_PATH}, max-age=${SESSION_GC_MAX_AGE_MINUTES}m, every ${SESSION_GC_INTERVAL_SECONDS}s)"
  43. (
  44. while true; do
  45. sleep "${SESSION_GC_INTERVAL_SECONDS}"
  46. # `-mmin +N` matches files older than N minutes; `-type f` avoids
  47. # touching the directory itself; errors swallowed so a transient
  48. # filesystem hiccup does not kill the loop.
  49. find "${SESSION_PATH}" -mindepth 1 -type f -mmin +"${SESSION_GC_MAX_AGE_MINUTES}" -delete 2>/dev/null || true
  50. done
  51. ) &
  52. fi
  53. echo "[entrypoint] starting: $*"
  54. exec "$@"