Files
glpi-neuroforge-mega/scripts/preflight.sh
jbergner 8c67c7a7fa
All checks were successful
release-tag / release-image (push) Successful in 10m52s
Update 1.5.0
2026-08-27 07:51:39 +02:00

91 lines
3.8 KiB
Bash

#!/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"