147 lines
5.1 KiB
Bash
Executable File
147 lines
5.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
test_dir=${1:-}
|
|
production_dir=${2:-}
|
|
|
|
if [[ -z "$test_dir" || -z "$production_dir" ]]; then
|
|
echo "Usage: $0 TEST_CHECKOUT PRODUCTION_CHECKOUT" >&2
|
|
exit 1
|
|
fi
|
|
|
|
test_dir=$(realpath "$test_dir")
|
|
production_dir=$(realpath "$production_dir")
|
|
[[ "$test_dir" != "$production_dir" ]] || {
|
|
echo "Test and production must be different directories." >&2
|
|
exit 1
|
|
}
|
|
|
|
for dir in "$test_dir" "$production_dir"; do
|
|
[[ -d "$dir/.git" ]] || {
|
|
echo "Independent Git metadata is missing in $dir." >&2
|
|
exit 1
|
|
}
|
|
[[ -f "$dir/.env" ]] || {
|
|
echo "The single runtime configuration is missing: $dir/.env" >&2
|
|
exit 1
|
|
}
|
|
[[ "$(stat -c '%a' "$dir/.env")" == 600 ]] || {
|
|
echo "$dir/.env must have mode 0600." >&2
|
|
exit 1
|
|
}
|
|
if find "$dir" -maxdepth 1 -type f \
|
|
\( -name '.env.edge' -o -name '.env.test' -o -name '.env.staging' -o -name '.env.production' \) \
|
|
-print -quit | grep -q .; then
|
|
echo "$dir contains a second runtime environment file." >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
test_git=$(git -C "$test_dir" rev-parse --absolute-git-dir)
|
|
production_git=$(git -C "$production_dir" rev-parse --absolute-git-dir)
|
|
[[ "$test_git" != "$production_git" ]] || {
|
|
echo "Test and production must not share a Git worktree." >&2
|
|
exit 1
|
|
}
|
|
|
|
read_env() {
|
|
local file=$1 key=$2
|
|
awk -v key="$key" '
|
|
index($0, key "=") == 1 {
|
|
print substr($0, length(key) + 2)
|
|
found = 1
|
|
exit
|
|
}
|
|
END { if (!found) exit 1 }
|
|
' "$file"
|
|
}
|
|
|
|
test_env="$test_dir/.env"
|
|
production_env="$production_dir/.env"
|
|
|
|
require_different() {
|
|
local key=$1 test_value production_value
|
|
test_value=$(read_env "$test_env" "$key")
|
|
production_value=$(read_env "$production_env" "$key")
|
|
[[ -n "$test_value" && -n "$production_value" && "$test_value" != "$production_value" ]] || {
|
|
echo "$key must be present and different between test and production." >&2
|
|
exit 1
|
|
}
|
|
}
|
|
|
|
[[ "$(read_env "$test_env" DEPLOYMENT_ENV)" == test ]] || {
|
|
echo "Test .env must declare DEPLOYMENT_ENV=test." >&2
|
|
exit 1
|
|
}
|
|
[[ "$(read_env "$production_env" DEPLOYMENT_ENV)" == production ]] || {
|
|
echo "Production .env must declare DEPLOYMENT_ENV=production." >&2
|
|
exit 1
|
|
}
|
|
|
|
for key in COMPOSE_PROJECT_NAME APP_IMAGE SOCKET_PROXY_IMAGE POSTGIS_IMAGE \
|
|
PHX_HOST WNH_BASE_URL PUBLIC_UPSTREAM_NAME DATABASE_URL EMAIL_FROM_ADDRESS \
|
|
SECRET_KEY_BASE HANDOVER_SECRET RELEASE_COOKIE METRICS_TOKEN; do
|
|
require_different "$key"
|
|
done
|
|
|
|
test_google_id=$(read_env "$test_env" GOOGLE_OAUTH_CLIENT_ID 2>/dev/null || true)
|
|
production_google_id=$(read_env "$production_env" GOOGLE_OAUTH_CLIENT_ID 2>/dev/null || true)
|
|
if [[ -n "$test_google_id" || -n "$production_google_id" ]]; then
|
|
[[ -n "$test_google_id" && -n "$production_google_id" &&
|
|
"$test_google_id" != "$production_google_id" ]] || {
|
|
echo "Configured test and production Google OAuth clients must be different." >&2
|
|
exit 1
|
|
}
|
|
fi
|
|
|
|
test_email_provider=$(read_env "$test_env" EMAIL_DELIVERY_PROVIDER)
|
|
production_email_provider=$(read_env "$production_env" EMAIL_DELIVERY_PROVIDER)
|
|
|
|
if [[ "$test_email_provider" == unisender_go &&
|
|
"$production_email_provider" == unisender_go ]]; then
|
|
require_different UNISENDER_GO_API_KEY
|
|
fi
|
|
|
|
if [[ "$test_email_provider" == smtp &&
|
|
"$(read_env "$test_env" SMTP_RELAY)" != mailpit &&
|
|
"$production_email_provider" == smtp &&
|
|
"$(read_env "$test_env" SMTP_RELAY)" == "$(read_env "$production_env" SMTP_RELAY)" ]]; then
|
|
require_different SMTP_USERNAME
|
|
require_different SMTP_PASSWORD
|
|
fi
|
|
[[ "$(read_env "$production_env" EMAIL_DELIVERY_PROVIDER)" != smtp ||
|
|
"$(read_env "$production_env" SMTP_RELAY)" != mailpit ]] || {
|
|
echo "Production email must not target test Mailpit." >&2
|
|
exit 1
|
|
}
|
|
|
|
test_edge_network=$(read_env "$test_env" PUBLIC_EDGE_NETWORK)
|
|
production_edge_network=$(read_env "$production_env" PUBLIC_EDGE_NETWORK)
|
|
[[ "$test_edge_network" == "$production_edge_network" ]] || {
|
|
echo "Both deployments must intentionally join the same public edge network." >&2
|
|
exit 1
|
|
}
|
|
test_upstream=$(read_env "$test_env" PUBLIC_UPSTREAM_NAME)
|
|
production_edge_test_upstream=$(read_env "$production_env" TEST_UPSTREAM)
|
|
[[ "$production_edge_test_upstream" == "$test_upstream:4000" ]] || {
|
|
echo "Production edge TEST_UPSTREAM does not point to the test alias." >&2
|
|
exit 1
|
|
}
|
|
production_upstream=$(read_env "$production_env" PUBLIC_UPSTREAM_NAME)
|
|
production_edge_primary_upstream=$(read_env "$production_env" PRIMARY_UPSTREAM)
|
|
[[ "$production_edge_primary_upstream" == "$production_upstream:4000" ]] || {
|
|
echo "Production edge PRIMARY_UPSTREAM does not point to production." >&2
|
|
exit 1
|
|
}
|
|
|
|
edge_project=$(read_env "$production_env" EDGE_COMPOSE_PROJECT_NAME)
|
|
test_project=$(read_env "$test_env" COMPOSE_PROJECT_NAME)
|
|
production_project=$(read_env "$production_env" COMPOSE_PROJECT_NAME)
|
|
[[ "$edge_project" != "$test_project" && "$edge_project" != "$production_project" ]] || {
|
|
echo "The edge Compose project must be independent from both application projects." >&2
|
|
exit 1
|
|
}
|
|
|
|
echo "Verified two independent Git checkouts and one isolated .env per deployment."
|
|
echo "Verified separate Compose projects, images, databases, OAuth clients, email paths, and secrets."
|