conductor/scripts/test-production.sh
Victor Wiebe ade2b10033
Some checks failed
Release production image / production-image (push) Has been cancelled
Add production packaging and operations
2026-08-07 16:48:40 -04:00

40 lines
3.1 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
umask 077
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
IMAGE="${CONDUCTOR_TEST_IMAGE:-conductor:slice8-test}"
PROJECT="conductor-release-test-${RANDOM}"
PORT="${CONDUCTOR_TEST_PORT:-3202}"
work="$(mktemp -d)"
env_file="${work}/.env"
backup_dir="${work}/backups"
compose=(docker compose -p "$PROJECT" --env-file "$env_file" -f "$ROOT/compose.production.yml")
cleanup() { "${compose[@]}" down -v --remove-orphans >/dev/null 2>&1 || true; rm -rf -- "$work"; }
trap cleanup EXIT
if [[ "${1:-}" != "--skip-build" ]]; then docker build -f "$ROOT/Dockerfile.production" -t "$IMAGE" "$ROOT"; fi
if docker run --rm "$IMAGE" >/dev/null 2>&1; then echo "Production image started without required keys." >&2; exit 1; fi
cat > "$env_file" <<EOF
CONDUCTOR_SECRET_KEY=1111111111111111111111111111111111111111111111111111111111111111
CONDUCTOR_SESSION_KEY=22222222222222222222222222222222
CONDUCTOR_IMAGE=${IMAGE%:*}
CONDUCTOR_VERSION=${IMAGE##*:}
CONDUCTOR_BIND_ADDRESS=127.0.0.1
CONDUCTOR_PORT=${PORT}
EOF
"${compose[@]}" up -d --no-build
curl --fail --silent --show-error --retry 20 --retry-delay 1 --retry-all-errors "http://127.0.0.1:${PORT}/api/health" >/dev/null
curl --fail --silent --show-error "http://127.0.0.1:${PORT}/" | grep -qi '<!doctype html>'
curl --fail --silent --show-error "http://127.0.0.1:${PORT}/apps/test/details" | grep -qi '<!doctype html>'
api_status="$(curl --silent --output /dev/null --write-out '%{http_code}' "http://127.0.0.1:${PORT}/api/not-a-route")"
[[ "$api_status" == 404 ]] || { echo "Unknown API routes must return 404, not the SPA." >&2; exit 1; }
"${compose[@]}" exec -T conductor node -e 'const D=require("better-sqlite3");const d=new D("/data/conductor.db");d.prepare("INSERT INTO projects(name,description,project_json) VALUES(?,?,?)").run("release-restore-marker","test","{}");d.close()'
COMPOSE_PROJECT_NAME="$PROJECT" "$ROOT/scripts/production/backup-conductor.sh" --compose-file "$ROOT/compose.production.yml" --env-file "$env_file" --backup-dir "$backup_dir" --retention-days 1 >/dev/null
archive="$(find "$backup_dir" -name 'conductor_*.tar.gz' -type f | head -n1)"
"${compose[@]}" exec -T conductor node -e 'const D=require("better-sqlite3");const d=new D("/data/conductor.db");d.prepare("DELETE FROM projects WHERE name=?").run("release-restore-marker");d.close()'
COMPOSE_PROJECT_NAME="$PROJECT" "$ROOT/scripts/production/restore-conductor.sh" --compose-file "$ROOT/compose.production.yml" --env-file "$env_file" --backup-dir "$backup_dir" --backup-file "$archive" --skip-safety-backup --force >/dev/null
count="$("${compose[@]}" exec -T conductor node -e 'const D=require("better-sqlite3");const d=new D("/data/conductor.db",{readonly:true});process.stdout.write(String(d.prepare("SELECT count(*) FROM projects WHERE name=?").pluck().get("release-restore-marker")));d.close()')"
[[ "$count" == 1 ]] || { echo "Restore marker was not recovered." >&2; exit 1; }
docker inspect "$("${compose[@]}" ps -q conductor)" --format '{{.Config.User}} {{.HostConfig.ReadonlyRootfs}}' | grep -q '^conductor true$'
echo '{"productionValidation":"passed"}'