#!/usr/bin/env bash set -euo pipefail umask 077 ROOT=$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd) domain=${1:-} target=${2:-"$ROOT/.env"} if [[ -z "$domain" ]]; then echo "Usage: $0 DOMAIN [OUTPUT_FILE]" >&2 exit 1 fi if [[ ! "$domain" =~ ^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?(\.[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)+$ ]]; then echo "DOMAIN must be a lowercase ASCII DNS hostname without a scheme, port, or path." >&2 exit 1 fi if [[ "$target" != /* ]]; then target="$ROOT/$target" fi for command in awk docker git mktemp openssl stat; do command -v "$command" >/dev/null 2>&1 || { echo "Required command is unavailable: $command" >&2 exit 1 } done [[ -S /var/run/docker.sock ]] || { echo "/var/run/docker.sock is unavailable; run this on the target Docker host." >&2 exit 1 } [[ ! -e "$target" ]] || { echo "Refusing to overwrite existing test environment: $target" >&2 exit 1 } target_dir=$(dirname -- "$target") [[ -d "$target_dir" ]] || { echo "Output directory does not exist: $target_dir" >&2 exit 1 } compose_project_name=${TEST_COMPOSE_PROJECT_NAME:-who_need_help_test} public_edge_network=${TEST_PUBLIC_EDGE_NETWORK:-who_need_help_public_edge} public_upstream_name=${TEST_PUBLIC_UPSTREAM_NAME:-who-need-help-test} http_bind_address=${TEST_HTTP_BIND_ADDRESS:-127.0.0.1} http_port=${TEST_HTTP_PORT:-4011} mailpit_bind_address=${TEST_MAILPIT_BIND_ADDRESS:-127.0.0.1} mailpit_port=${TEST_MAILPIT_PORT:-8027} codex_session_id=${TEST_CODEX_SESSION_ID:-} google_oauth_client_id=${TEST_GOOGLE_OAUTH_CLIENT_ID:-} google_oauth_client_secret=${TEST_GOOGLE_OAUTH_CLIENT_SECRET:-} support_inbox_address=${TEST_SUPPORT_INBOX_ADDRESS:-} git_sha=$(git -C "$ROOT" rev-parse --short=12 HEAD) [[ "$compose_project_name" =~ ^[a-zA-Z0-9_-]+$ ]] || { echo "TEST_COMPOSE_PROJECT_NAME contains unsupported characters." >&2 exit 1 } [[ "$public_edge_network" =~ ^[a-zA-Z0-9_-]+$ ]] || { echo "TEST_PUBLIC_EDGE_NETWORK contains unsupported characters." >&2 exit 1 } [[ "$public_upstream_name" =~ ^[a-z0-9]([a-z0-9-]*[a-z0-9])?$ ]] || { echo "TEST_PUBLIC_UPSTREAM_NAME must be a lowercase Docker DNS alias." >&2 exit 1 } [[ -n "$codex_session_id" ]] || { echo "TEST_CODEX_SESSION_ID is required for the Build Week feedback page." >&2 exit 1 } if [[ -n "$google_oauth_client_id" || -n "$google_oauth_client_secret" ]]; then [[ -n "$google_oauth_client_id" && -n "$google_oauth_client_secret" ]] || { echo "Test Google OAuth client ID and secret must either both be set or both be empty." >&2 exit 1 } fi for pair in "HTTP:$http_port" "MAILPIT:$mailpit_port"; do value=${pair#*:} if [[ ! "$value" =~ ^[0-9]+$ ]] || ((value < 1 || value > 65535)); then echo "${pair%%:*} port must be between 1 and 65535." >&2 exit 1 fi done [[ "$http_port" != "$mailpit_port" ]] || { echo "Test HTTP and Mailpit ports must be different." >&2 exit 1 } postgres_password=$(openssl rand -hex 32) secret_key_base=$(openssl rand -hex 64) handover_secret=$(openssl rand -hex 64) release_cookie=$(openssl rand -hex 64) metrics_token=$(openssl rand -hex 32) docker_socket_gid=$(stat -c '%g' /var/run/docker.sock) postgres_db=who_need_help_test database_url="ecto://postgres:$postgres_password@db/$postgres_db" tmp=$(mktemp "$target_dir/.test-env.XXXXXX") trap 'rm -f "$tmp"' EXIT HUP INT TERM chmod 600 "$tmp" DOMAIN=$domain \ GIT_SHA_VALUE=$git_sha \ COMPOSE_PROJECT_NAME_VALUE=$compose_project_name \ PUBLIC_EDGE_NETWORK_VALUE=$public_edge_network \ PUBLIC_UPSTREAM_NAME_VALUE=$public_upstream_name \ HTTP_BIND_ADDRESS_VALUE=$http_bind_address \ HTTP_PORT_VALUE=$http_port \ MAILPIT_BIND_ADDRESS_VALUE=$mailpit_bind_address \ MAILPIT_PORT_VALUE=$mailpit_port \ DOCKER_SOCKET_GID_VALUE=$docker_socket_gid \ POSTGRES_DB_VALUE=$postgres_db \ POSTGRES_PASSWORD_VALUE=$postgres_password \ DATABASE_URL_VALUE=$database_url \ SECRET_KEY_BASE_VALUE=$secret_key_base \ HANDOVER_SECRET_VALUE=$handover_secret \ RELEASE_COOKIE_VALUE=$release_cookie \ METRICS_TOKEN_VALUE=$metrics_token \ GOOGLE_OAUTH_CLIENT_ID_VALUE=$google_oauth_client_id \ GOOGLE_OAUTH_CLIENT_SECRET_VALUE=$google_oauth_client_secret \ SUPPORT_INBOX_ADDRESS_VALUE=$support_inbox_address \ CODEX_SESSION_ID_VALUE=$codex_session_id \ awk ' BEGIN { replacement["DEPLOYMENT_TARGET"] = "compose" replacement["DEPLOYMENT_ENV"] = "test" replacement["COMPOSE_PROJECT_NAME"] = ENVIRON["COMPOSE_PROJECT_NAME_VALUE"] replacement["APP_IMAGE"] = "who-need-help:test-" ENVIRON["GIT_SHA_VALUE"] replacement["SOCKET_PROXY_IMAGE"] = "who-need-help:socket-proxy-test-" ENVIRON["GIT_SHA_VALUE"] replacement["POSTGIS_IMAGE"] = "who-need-help:postgis-test-" ENVIRON["GIT_SHA_VALUE"] replacement["APP_TOPOLOGY"] = "compact" replacement["DATABASE_MODE"] = "container" replacement["HTTP_BIND_ADDRESS"] = ENVIRON["HTTP_BIND_ADDRESS_VALUE"] replacement["HTTP_PORT"] = ENVIRON["HTTP_PORT_VALUE"] replacement["PUBLIC_EDGE_ENABLED"] = "true" replacement["PUBLIC_EDGE_NETWORK"] = ENVIRON["PUBLIC_EDGE_NETWORK_VALUE"] replacement["PUBLIC_UPSTREAM_NAME"] = ENVIRON["PUBLIC_UPSTREAM_NAME_VALUE"] replacement["MAILPIT_BIND_ADDRESS"] = ENVIRON["MAILPIT_BIND_ADDRESS_VALUE"] replacement["MAILPIT_PORT"] = ENVIRON["MAILPIT_PORT_VALUE"] replacement["DOCKER_SOCKET_GID"] = ENVIRON["DOCKER_SOCKET_GID_VALUE"] replacement["TRAEFIK_TRUSTED_IPS"] = "127.0.0.1/32" replacement["TRAEFIK_PROJECT_CONSTRAINT"] = ENVIRON["COMPOSE_PROJECT_NAME_VALUE"] replacement["TRAEFIK_APP_NAME"] = "who-need-help-test" replacement["TRAEFIK_DOCKER_NETWORK"] = ENVIRON["COMPOSE_PROJECT_NAME_VALUE"] "_ingress" replacement["PHX_HOST"] = ENVIRON["DOMAIN"] replacement["PHX_SCHEME"] = "https" replacement["PHX_URL_PORT"] = "443" replacement["WNH_DEBUG_BASE_URL"] = "https://" ENVIRON["DOMAIN"] replacement["WNH_BASE_URL"] = "https://" ENVIRON["DOMAIN"] replacement["POSTGRES_DB"] = ENVIRON["POSTGRES_DB_VALUE"] replacement["POSTGRES_USER"] = "postgres" replacement["POSTGRES_PASSWORD"] = ENVIRON["POSTGRES_PASSWORD_VALUE"] replacement["DATABASE_URL"] = ENVIRON["DATABASE_URL_VALUE"] replacement["DATABASE_SOCKET_DIR"] = "" replacement["SECRET_KEY_BASE"] = ENVIRON["SECRET_KEY_BASE_VALUE"] replacement["HANDOVER_SECRET"] = ENVIRON["HANDOVER_SECRET_VALUE"] replacement["RELEASE_COOKIE"] = ENVIRON["RELEASE_COOKIE_VALUE"] replacement["METRICS_TOKEN"] = ENVIRON["METRICS_TOKEN_VALUE"] replacement["EMAIL_DELIVERY_PROVIDER"] = "smtp" replacement["SMTP_RELAY"] = "mailpit" replacement["SMTP_PORT"] = "1025" replacement["SMTP_USERNAME"] = "" replacement["SMTP_PASSWORD"] = "" replacement["SMTP_AUTH"] = "never" replacement["SMTP_TLS"] = "never" replacement["SMTP_SSL"] = "false" replacement["EMAIL_FROM_NAME"] = "\"Who Need Help Test\"" replacement["EMAIL_FROM_ADDRESS"] = "test@" ENVIRON["DOMAIN"] replacement["SUPPORT_INBOX_ADDRESS"] = ENVIRON["SUPPORT_INBOX_ADDRESS_VALUE"] replacement["GOOGLE_OAUTH_CLIENT_ID"] = ENVIRON["GOOGLE_OAUTH_CLIENT_ID_VALUE"] replacement["GOOGLE_OAUTH_CLIENT_SECRET"] = ENVIRON["GOOGLE_OAUTH_CLIENT_SECRET_VALUE"] replacement["CODEX_SESSION_ID"] = ENVIRON["CODEX_SESSION_ID_VALUE"] } { separator = index($0, "=") key = separator > 1 ? substr($0, 1, separator - 1) : "" print (key in replacement) ? key "=" replacement[key] : $0 } ' "$ROOT/.env.example" >"$tmp" mv "$tmp" "$target" chmod 600 "$target" trap - EXIT HUP INT TERM unset postgres_password secret_key_base handover_secret release_cookie metrics_token unset google_oauth_client_secret "$ROOT/scripts/compose.sh" "$target" config --quiet echo "Generated independent test secrets without printing them." echo "Created the single mode-0600 test configuration: $target"