sciagent code + Gitea Actions CI/CD
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Executable
+115
@@ -0,0 +1,115 @@
|
||||
#!/usr/bin/env bash
|
||||
# Validates variables required by docker-compose.prod.yml before compose runs.
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
cd "$ROOT"
|
||||
|
||||
ENV_FILE="${ENV_FILE:-$ROOT/.env}"
|
||||
if [[ ! -f "$ENV_FILE" ]]; then
|
||||
printf 'Missing %s\nCopy .env.example to .env and fill in secrets.\n' "$ENV_FILE" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||
line="${line%$'\r'}"
|
||||
[[ "$line" =~ ^[[:space:]]*# ]] && continue
|
||||
[[ "$line" =~ ^[[:space:]]*$ ]] && continue
|
||||
if [[ "$line" =~ ^([A-Za-z_][A-Za-z0-9_]*)=(.*)$ ]]; then
|
||||
key="${BASH_REMATCH[1]}"
|
||||
val="${BASH_REMATCH[2]}"
|
||||
val="${val#"${val%%[![:space:]]*}"}"
|
||||
val="${val%"${val##*[![:space:]]}"}"
|
||||
export "${key}=${val}"
|
||||
fi
|
||||
done <"$ENV_FILE"
|
||||
|
||||
reject_leading_space_after_equals() {
|
||||
local key rhs
|
||||
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||
line="${line%$'\r'}"
|
||||
[[ "$line" =~ ^[[:space:]]*# ]] && continue
|
||||
[[ "$line" =~ ^[[:space:]]*$ ]] && continue
|
||||
if [[ "$line" =~ ^[[:space:]]*(POSTGRES_USER|POSTGRES_PASSWORD|POSTGRES_DB|MINIO_ROOT_USER|MINIO_ROOT_PASSWORD|MINIO_API_PORT|MINIO_CONSOLE_PORT|FE_PORT|PUBLIC_HOST)=(.*)$ ]]; then
|
||||
key="${BASH_REMATCH[1]}"
|
||||
rhs="${BASH_REMATCH[2]}"
|
||||
[[ "$rhs" =~ ^[[:space:]]+ ]] || continue
|
||||
printf 'Invalid format in %s: "%s" has a space (or tab) immediately after "=".\n' "$ENV_FILE" "$key" >&2
|
||||
printf 'Use KEY=value without a space after the equals sign.\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
done <"$ENV_FILE"
|
||||
}
|
||||
reject_leading_space_after_equals
|
||||
MISSING=()
|
||||
need() {
|
||||
local n="$1"
|
||||
local v="${!n-}"
|
||||
if [[ -z "${v//[:space:]}" ]]; then
|
||||
MISSING+=("$n")
|
||||
fi
|
||||
}
|
||||
|
||||
need PUBLIC_HOST
|
||||
need FE_PORT
|
||||
need MINIO_API_PORT
|
||||
need MINIO_CONSOLE_PORT
|
||||
need MINIO_ROOT_USER
|
||||
need MINIO_ROOT_PASSWORD
|
||||
need POSTGRES_USER
|
||||
need POSTGRES_PASSWORD
|
||||
need POSTGRES_DB
|
||||
need JWT_SECRET
|
||||
need MINIO_API_CORS_ALLOW_ORIGIN
|
||||
|
||||
if [[ ${#MISSING[@]} -gt 0 ]]; then
|
||||
printf 'Unset or whitespace-only variables in %s:\n %s\n' "$ENV_FILE" "${MISSING[*]}" >&2
|
||||
printf '(Blank values break MinIO: MINIO_BROWSER_REDIRECT_URL would have an empty host.)\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! [[ "${MINIO_CONSOLE_PORT}" =~ ^[0-9]+$ ]] || ! [[ "${MINIO_API_PORT}" =~ ^[0-9]+$ ]] || ! [[ "${FE_PORT}" =~ ^[0-9]+$ ]]; then
|
||||
printf 'Ports must be numeric (FE_PORT, MINIO_*_PORT).\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Postgres role/database names in URLs/scripts: avoid quoting and “role does not exist” churn.
|
||||
if ! [[ "${POSTGRES_USER}" =~ ^[a-zA-Z_][a-zA-Z0-9_]{0,62}$ ]]; then
|
||||
printf 'POSTGRES_USER must be an unquoted SQL identifier (e.g. initiative): letters, digits, underscore; max 63 chars.\n' >&2
|
||||
printf 'Avoid special characters like "!". Docker only creates this role when the Postgres data directory is EMPTY.\n' >&2
|
||||
printf 'Full guide: docs/deploy-production-docker.md (Postgres).\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! [[ "${POSTGRES_DB}" =~ ^[a-zA-Z_][a-zA-Z0-9_]{0,62}$ ]]; then
|
||||
printf 'POSTGRES_DB must be an unquoted SQL identifier (e.g. initiatives): letters, digits, underscore; max 63 chars.\n' >&2
|
||||
printf 'INITIATIVE_DATABASE_URL is built without escaping; exotic names break URLs.\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# INITIATIVE_DATABASE_URL is assembled without encoding; :, @, / break the URL if unescaped.
|
||||
if [[ "${POSTGRES_PASSWORD}" == *"@"* ]] || [[ "${POSTGRES_PASSWORD}" == *":"* ]] || [[ "${POSTGRES_PASSWORD}" == *"/"* ]] || [[ "${POSTGRES_PASSWORD}" == *"%"* ]]; then
|
||||
printf 'POSTGRES_PASSWORD contains @, :, / or %%. URL-unsafe for INITIATIVE_DATABASE_URL; use alphanumeric + punctuation like + or = from openssl rand -base64.\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "${MINIO_API_CORS_ALLOW_ORIGIN}" == "*" ]]; then
|
||||
printf 'MINIO_API_CORS_ALLOW_ORIGIN must not be * in production — use your SPA origin (e.g. https://www.rcc-ump.com).\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ${#JWT_SECRET} -lt 32 ]]; then
|
||||
printf 'JWT_SECRET must be at least 32 characters (generate: openssl rand -base64 48).\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
preview="http://${PUBLIC_HOST}:${MINIO_CONSOLE_PORT}"
|
||||
|
||||
echo "OK — required vars set; MINIO_BROWSER_REDIRECT_URL preview: ${preview}"
|
||||
echo " CORS for public UI: http://${PUBLIC_HOST}:${FE_PORT} (+ localhost defaults in be0; optional CORS_ORIGINS_EXTRA in .env)"
|
||||
if [[ -n "${S3_PUBLIC_ENDPOINT_URL:-}" && "${S3_PUBLIC_ENDPOINT_URL}" == https:* ]]; then
|
||||
echo " HTTPS MinIO (S3_PUBLIC_ENDPOINT_URL): ensure TLS proxy → 127.0.0.1:${MINIO_API_PORT}; see docs/minio-behind-https.md"
|
||||
fi
|
||||
if [[ -n "${MINIO_SERVER_URL:-}" && "${MINIO_SERVER_URL}" == https:* && -z "${S3_PUBLIC_ENDPOINT_URL:-}" ]]; then
|
||||
echo " Warn: MINIO_SERVER_URL is https but S3_PUBLIC_ENDPOINT_URL is unset — be0 defaults may still presign HTTP. Set both to match."
|
||||
fi
|
||||
Reference in New Issue
Block a user