GeoIntel release gates / Compile, test, contracts and builds (push) Successful in 1m49s
GeoIntel release gates / Python and npm vulnerability policy (push) Successful in 21s
GeoIntel release gates / Production AI image, SBOM and container scan (push) Successful in 5m39s
GeoIntel release gates / Deploy exact gated revision to Unraid (push) Failing after 58m43s
110 lines
3.9 KiB
Bash
110 lines
3.9 KiB
Bash
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
export POSTGRES_DB="${GEOINTEL_POSTGRES_DB:-${POSTGRES_DB:-geointel}}"
|
|
export POSTGRES_USER="${GEOINTEL_POSTGRES_USER:-${POSTGRES_USER:-geointel}}"
|
|
export POSTGRES_PASSWORD="${GEOINTEL_POSTGRES_PASSWORD:-${POSTGRES_PASSWORD:-geointel}}"
|
|
export PGDATA="${PGDATA:-/var/lib/postgresql/data}"
|
|
export STORAGE_ROOT="${STORAGE_ROOT:-${GEOINTEL_STORAGE_ROOT:-/app/storage}}"
|
|
export DATABASE_URL="${DATABASE_URL:-postgresql+psycopg://${POSTGRES_USER}:${POSTGRES_PASSWORD}@127.0.0.1:5432/${POSTGRES_DB}}"
|
|
export CORS_ORIGINS="${GEOINTEL_CORS_ORIGINS:-${CORS_ORIGINS:-http://localhost:1202,http://127.0.0.1:1202}}"
|
|
export MAX_UPLOAD_MB="${GEOINTEL_MAX_UPLOAD_MB:-${MAX_UPLOAD_MB:-500}}"
|
|
export YOLO_MODELS_DIR="${YOLO_MODELS_DIR:-/app/models}"
|
|
export YOLO_CONFIG_DIR="${YOLO_CONFIG_DIR:-$STORAGE_ROOT/ultralytics}"
|
|
export GEOINTEL_RECONCILE_INTERRUPTED_RUNS_ON_STARTUP="${GEOINTEL_RECONCILE_INTERRUPTED_RUNS_ON_STARTUP:-true}"
|
|
|
|
case "$MAX_UPLOAD_MB" in
|
|
''|*[!0-9]*)
|
|
echo "GEOINTEL_MAX_UPLOAD_MB must be a whole number between 1 and 2048." >&2
|
|
exit 2
|
|
;;
|
|
esac
|
|
if [ "$MAX_UPLOAD_MB" -lt 1 ] || [ "$MAX_UPLOAD_MB" -gt 2048 ]; then
|
|
echo "GEOINTEL_MAX_UPLOAD_MB must be between 1 and 2048." >&2
|
|
exit 2
|
|
fi
|
|
|
|
case "${GEOINTEL_ENV:-production}:${POSTGRES_PASSWORD}" in
|
|
production:|production:geointel|production:postgres|production:password|production:changeme|production:change-me-before-shared-use)
|
|
echo "Refusing to start production with an empty or known-default PostGIS password." >&2
|
|
exit 2
|
|
;;
|
|
esac
|
|
|
|
sed -i "s/__GEOINTEL_MAX_UPLOAD_MB__/${MAX_UPLOAD_MB}/g" /etc/nginx/conf.d/default.conf
|
|
nginx -t
|
|
|
|
mkdir -p "$PGDATA" "$STORAGE_ROOT" "$YOLO_CONFIG_DIR" /run/nginx /var/log/nginx
|
|
# The persisted database can contain millions of relation files. A recursive
|
|
# chown on every start rewrites metadata even when ownership is already right,
|
|
# delaying startup and creating avoidable recovery I/O.
|
|
chown postgres:postgres "$PGDATA"
|
|
|
|
postgres_pid=""
|
|
backend_pid=""
|
|
|
|
shutdown() {
|
|
if [ -n "$backend_pid" ] && kill -0 "$backend_pid" 2>/dev/null; then
|
|
kill "$backend_pid" 2>/dev/null || true
|
|
fi
|
|
if [ -n "$postgres_pid" ] && kill -0 "$postgres_pid" 2>/dev/null; then
|
|
kill "$postgres_pid" 2>/dev/null || true
|
|
wait "$postgres_pid" 2>/dev/null || true
|
|
fi
|
|
}
|
|
trap shutdown INT TERM EXIT
|
|
|
|
echo "Starting embedded PostGIS..."
|
|
/usr/local/bin/docker-entrypoint.sh postgres &
|
|
postgres_pid="$!"
|
|
|
|
echo "Waiting for embedded PostGIS..."
|
|
# Large persistent PostGIS volumes can require several minutes of crash
|
|
# recovery after an interrupted container replacement. Keep the wait bounded,
|
|
# but do not terminate recovery at the former two-minute ceiling.
|
|
for attempt in $(seq 1 450); do
|
|
if pg_isready -h 127.0.0.1 -U "$POSTGRES_USER" -d "$POSTGRES_DB" >/dev/null 2>&1; then
|
|
echo "PostGIS is ready after attempt ${attempt}."
|
|
break
|
|
fi
|
|
if ! kill -0 "$postgres_pid" 2>/dev/null; then
|
|
echo "PostGIS process exited before becoming ready."
|
|
wait "$postgres_pid"
|
|
fi
|
|
sleep 2
|
|
done
|
|
|
|
if ! pg_isready -h 127.0.0.1 -U "$POSTGRES_USER" -d "$POSTGRES_DB" >/dev/null 2>&1; then
|
|
echo "PostGIS did not become ready within 15 minutes."
|
|
exit 1
|
|
fi
|
|
|
|
echo "Applying Alembic migrations..."
|
|
python -m alembic upgrade head
|
|
|
|
echo "Starting GeoIntel backend..."
|
|
uvicorn app.main:app --host 127.0.0.1 --port 8000 &
|
|
backend_pid="$!"
|
|
|
|
echo "Waiting for GeoIntel backend..."
|
|
python - <<'PY'
|
|
import time
|
|
import urllib.request
|
|
|
|
last_error = None
|
|
for attempt in range(1, 61):
|
|
try:
|
|
urllib.request.urlopen("http://127.0.0.1:8000/health/ready", timeout=3).read()
|
|
print(f"Backend is ready after attempt {attempt}.")
|
|
break
|
|
except Exception as exc:
|
|
last_error = exc
|
|
print(f"Backend not ready yet ({attempt}/60): {exc}")
|
|
time.sleep(1)
|
|
else:
|
|
raise SystemExit(f"Backend did not become ready: {last_error}")
|
|
PY
|
|
|
|
echo "Starting nginx frontend on container port 80..."
|
|
exec nginx -g "daemon off;"
|