@@ -0,0 +1,90 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
ENV_FILE=${ENV_FILE:-.env}
|
||||
STATIC_ONLY=false
|
||||
[ "${1:-}" = "--static" ] && STATIC_ONLY=true
|
||||
|
||||
fail() { echo "preflight: ERROR: $*" >&2; exit 1; }
|
||||
info() { echo "preflight: $*"; }
|
||||
|
||||
[ -f docker-compose.yml ] || fail "run from the repository root"
|
||||
|
||||
# Production compose must never silently fall back to source builds or broad .env injection.
|
||||
if grep -Eq '^[[:space:]]+build:' docker-compose.yml; then fail "production docker-compose.yml contains build:"; fi
|
||||
if grep -Eq '^[[:space:]]+env_file:' docker-compose.yml; then fail "production docker-compose.yml contains env_file:"; fi
|
||||
for image in neuroforge neuroforge-worker agent agent-data-init knowledge control; do
|
||||
grep -Fq "git.send.nrw/sendnrw/glpi-neuroforge-mega-${image}:\${IMAGE_TAG:" docker-compose.yml || fail "registry image mapping missing for ${image}"
|
||||
done
|
||||
|
||||
if [ "$STATIC_ONLY" = true ]; then
|
||||
if grep -Eq '^IMAGE_TAG[[:space:]]*=[[:space:]]*latest([[:space:]]|$)' .env.example; then fail ".env.example sets IMAGE_TAG=latest"; fi
|
||||
info "static compose/source checks passed"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
[ -f "$ENV_FILE" ] || fail "$ENV_FILE not found (copy .env.example and replace every placeholder)"
|
||||
getv() { sed -n "s/^$1=//p" "$ENV_FILE" | tail -n 1 | tr -d '\r'; }
|
||||
check_not_placeholder() {
|
||||
name=$1; value=$(getv "$name")
|
||||
[ -n "$value" ] || fail "$name is empty"
|
||||
upper=$(printf '%s' "$value" | tr '[:lower:]' '[:upper:]')
|
||||
case "$upper" in *CHANGE_ME*|*CHANGEME*|*PLACEHOLDER*) fail "$name still contains a placeholder";; esac
|
||||
}
|
||||
check_secret() {
|
||||
name=$1; min=$2
|
||||
check_not_placeholder "$name"
|
||||
value=$(getv "$name")
|
||||
[ "${#value}" -ge "$min" ] || fail "$name must contain at least $min characters"
|
||||
}
|
||||
|
||||
tag=$(getv IMAGE_TAG)
|
||||
[ -n "$tag" ] || fail "IMAGE_TAG is empty"
|
||||
[ "$tag" != latest ] || fail "IMAGE_TAG=latest is forbidden for production"
|
||||
case "$tag" in *[!A-Za-z0-9._-]*) fail "IMAGE_TAG contains invalid characters";; esac
|
||||
|
||||
for spec in \
|
||||
NEUROFORGE_ADMIN_TOKEN:24 \
|
||||
NEUROFORGE_APP_API_KEY:24 \
|
||||
NEUROFORGE_INTEGRATION_TOKEN:24 \
|
||||
NEUROFORGE_CONTROL_READ_TOKEN:24 \
|
||||
NEUROFORGE_WORKER_TOKEN:24 \
|
||||
NEUROFORGE_METRICS_TOKEN:24 \
|
||||
KB_INTEGRATION_TOKEN:24 \
|
||||
CONTROL_READ_TOKEN:24 \
|
||||
BASIC_AUTH_PASSWORD:12 \
|
||||
CONTROL_BASIC_AUTH_PASSWORD:12 \
|
||||
WEB_PASSWORD:12; do
|
||||
check_secret "${spec%%:*}" "${spec##*:}"
|
||||
done
|
||||
|
||||
for name in GLPI_URL GLPI_CLIENT_ID GLPI_CLIENT_SECRET GLPI_USERNAME GLPI_PASSWORD; do
|
||||
check_not_placeholder "$name"
|
||||
done
|
||||
web_anon=$(printf '%s' "$(getv WEB_ALLOW_ANONYMOUS)" | tr '[:upper:]' '[:lower:]')
|
||||
if [ "$web_anon" != "true" ]; then
|
||||
check_not_placeholder WEB_USERNAME
|
||||
fi
|
||||
research=$(printf '%s' "$(getv NEUROFORGE_SEARXNG_ENABLED)" | tr '[:upper:]' '[:lower:]')
|
||||
if [ "$research" = "true" ]; then
|
||||
check_secret SEARXNG_SECRET 24
|
||||
fi
|
||||
|
||||
# Trust-boundary tokens must not be reused across roles.
|
||||
seen=''
|
||||
for name in NEUROFORGE_ADMIN_TOKEN NEUROFORGE_APP_API_KEY NEUROFORGE_INTEGRATION_TOKEN NEUROFORGE_CONTROL_READ_TOKEN NEUROFORGE_WORKER_TOKEN NEUROFORGE_METRICS_TOKEN KB_INTEGRATION_TOKEN CONTROL_READ_TOKEN; do
|
||||
value=$(getv "$name")
|
||||
case "|$seen|" in *"|$value|"*) fail "$name reuses another service token";; esac
|
||||
seen=${seen:+$seen|}$value
|
||||
done
|
||||
|
||||
command -v docker >/dev/null 2>&1 || fail "docker is not installed"
|
||||
docker compose version >/dev/null 2>&1 || fail "Docker Compose v2 is unavailable"
|
||||
docker compose --env-file "$ENV_FILE" config -q || fail "docker compose config validation failed"
|
||||
images=$(docker compose --env-file "$ENV_FILE" config --images)
|
||||
for image in neuroforge neuroforge-worker agent agent-data-init knowledge control; do
|
||||
expected="git.send.nrw/sendnrw/glpi-neuroforge-mega-${image}:${tag}"
|
||||
printf '%s\n' "$images" | grep -Fxq "$expected" || fail "resolved image missing: $expected"
|
||||
done
|
||||
printf '%s\n' "$images" | grep -E 'git\.send\.nrw/sendnrw/glpi-neuroforge-mega-.*:latest$' >/dev/null && fail "a project image resolved to latest"
|
||||
info "production preflight passed for IMAGE_TAG=$tag"
|
||||
Reference in New Issue
Block a user