69 lines
2.9 KiB
Bash
Executable File
69 lines
2.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Builds and starts the production stack under a throwaway project name with
|
|
# generated secrets, checks it, then removes it (containers and volumes).
|
|
# deploy/check.sh from the repository root or deploy/
|
|
# Exits non-zero if any check fails. Needs Docker with Compose v2.
|
|
set -euo pipefail
|
|
|
|
cd "$(dirname "$0")"
|
|
project="deadhead-check"
|
|
env_file="$(mktemp)"
|
|
secret() { node -e 'console.log(require("crypto").randomBytes(+process.argv[1]).toString("base64url"))' "$1"; }
|
|
port="${CHECK_PORT:-8088}"
|
|
site="http://localhost:$port"
|
|
cat > "$env_file" <<EOF
|
|
PUBLIC_URL=$site
|
|
SITE_ADDRESS=:80
|
|
HTTP_PORT=$port
|
|
HTTPS_PORT=${CHECK_TLS_PORT:-8443}
|
|
POSTGRES_PASSWORD=$(secret 18)
|
|
BETTER_AUTH_SECRET=$(secret 32)
|
|
EOF
|
|
compose() { docker compose -p "$project" --env-file "$env_file" "$@"; }
|
|
cleanup() {
|
|
compose down -v --remove-orphans >/dev/null 2>&1 || true
|
|
rm -f "$env_file"
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
echo "Building and starting the stack…"
|
|
compose up -d --build --wait --wait-timeout 180
|
|
|
|
fail=0
|
|
check() {
|
|
local name="$1" url="$2" expect="$3" body
|
|
body="$(compose exec -T server node -e "fetch('$url').then(r => r.text().then(t => console.log(r.status + ' ' + t)))")"
|
|
if [[ "$body" == $expect ]]; then
|
|
echo "ok $name: $body"
|
|
else
|
|
echo "FAIL $name: $body"
|
|
fail=1
|
|
fi
|
|
}
|
|
|
|
# Step 13.1: the server, with migrations applied, answers health and time.
|
|
check "health" "http://127.0.0.1:3000/api/health" '200 {"status":"ok"}'
|
|
check "server time" "http://127.0.0.1:3000/api/time" '200 {"realTime":*'
|
|
|
|
# Step 13.2: Caddy serves the app and proxies /api on one origin.
|
|
expect() {
|
|
local name="$1" got="$2" pattern="$3"
|
|
if [[ "$got" == $pattern ]]; then echo "ok $name"; else echo "FAIL $name: $got"; fail=1; fi
|
|
}
|
|
expect "app shell" "$(curl -s "$site/")" '*<title>Deadhead: Space Freight Tycoon</title>*'
|
|
expect "client route falls back to the app" "$(curl -s "$site/fleet")" '*<div id="root">*'
|
|
expect "API through the proxy" "$(curl -s "$site/api/health")" '{"status":"ok"}'
|
|
expect "service worker is never cached" "$(curl -sI "$site/sw.js" | tr -d '\r' | grep -i '^cache-control')" '*no-cache*'
|
|
bundle="$(curl -s "$site/" | grep -o '/assets/index-[A-Za-z0-9_-]*\.js' | head -1)"
|
|
expect "hashed bundle is cached for a year" "$(curl -sI "$site$bundle" | tr -d '\r' | grep -i '^cache-control')" '*immutable*'
|
|
|
|
# A guest can start a company and dispatch a tutorial run (Step 13.2's manual check, scripted).
|
|
jar="$(mktemp)"
|
|
trap 'cleanup; rm -f "$jar"' EXIT
|
|
post() { curl -s -b "$jar" -c "$jar" -H "Origin: $site" -H 'content-type: application/json' -X POST "$site$1" -d "$2"; }
|
|
expect "guest sign-up" "$(post /api/guest '{}')" '*"tutorial":{*'
|
|
contract="$(curl -s -b "$jar" "$site/api/tutorial/board" | node -e 'let d="";process.stdin.on("data",c=>d+=c).on("end",()=>console.log(JSON.parse(d).contracts[0].id))')"
|
|
expect "tutorial run dispatched" "$(post /api/dispatches "{\"kind\":\"tutorial\",\"contractId\":\"$contract\"}")" '*"dispatch":{*'
|
|
|
|
exit "$fail"
|