214 lines
7.1 KiB
Bash
Executable File
214 lines
7.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
ROOT=$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd)
|
|
env_file=${1:-"$ROOT/.env"}
|
|
mode=${2:-}
|
|
|
|
if [[ "$env_file" != /* ]]; then
|
|
env_file="$ROOT/$env_file"
|
|
fi
|
|
|
|
if [[ "$mode" != "" && "$mode" != "--require-release" ]]; then
|
|
echo "Usage: $0 [ENV_FILE] [--require-release]" >&2
|
|
exit 2
|
|
fi
|
|
|
|
if [[ ! -f "$env_file" ]]; then
|
|
echo "Environment file does not exist: $env_file" >&2
|
|
exit 2
|
|
fi
|
|
|
|
if [[ "$(stat -c '%a' "$env_file")" != 600 ]]; then
|
|
echo "Environment file must have mode 0600: $env_file" >&2
|
|
exit 2
|
|
fi
|
|
|
|
read_value() {
|
|
local key=$1
|
|
|
|
awk -v key="$key" '
|
|
index($0, key "=") == 1 {
|
|
value = substr($0, length(key) + 2)
|
|
if (value ~ /^".*"$/ || value ~ /^'\''.*'\''$/) {
|
|
value = substr(value, 2, length(value) - 2)
|
|
}
|
|
print value
|
|
found = 1
|
|
exit
|
|
}
|
|
END { if (!found) exit 1 }
|
|
' "$env_file"
|
|
}
|
|
|
|
value() {
|
|
read_value "$1" 2>/dev/null || true
|
|
}
|
|
|
|
is_set() {
|
|
[[ -n "$(value "$1")" ]]
|
|
}
|
|
|
|
all_set() {
|
|
local key
|
|
for key in "$@"; do
|
|
is_set "$key" || return 1
|
|
done
|
|
}
|
|
|
|
all_empty() {
|
|
local key
|
|
for key in "$@"; do
|
|
is_set "$key" && return 1
|
|
done
|
|
return 0
|
|
}
|
|
|
|
contains_template_marker() {
|
|
local observed=$1
|
|
[[ "$observed" == *REPLACE* || "$observed" == *GENERATE* ||
|
|
"$observed" == *example.com* || "$observed" == *example.invalid* ]]
|
|
}
|
|
|
|
failures=0
|
|
warnings=0
|
|
|
|
ready() {
|
|
printf 'READY %-24s %s\n' "$1" "$2"
|
|
}
|
|
|
|
local_only() {
|
|
printf 'LOCAL_ONLY %-24s %s\n' "$1" "$2"
|
|
warnings=$((warnings + 1))
|
|
}
|
|
|
|
missing() {
|
|
printf 'MISSING %-24s %s\n' "$1" "$2"
|
|
failures=$((failures + 1))
|
|
}
|
|
|
|
invalid() {
|
|
printf 'INVALID %-24s %s\n' "$1" "$2"
|
|
failures=$((failures + 1))
|
|
}
|
|
|
|
partial() {
|
|
printf 'PARTIAL %-24s %s\n' "$1" "$2"
|
|
failures=$((failures + 1))
|
|
}
|
|
|
|
deployment_env=$(value DEPLOYMENT_ENV)
|
|
phx_host=$(value PHX_HOST)
|
|
phx_scheme=$(value PHX_SCHEME)
|
|
phx_port=$(value PHX_URL_PORT)
|
|
base_url=$(value WNH_BASE_URL)
|
|
debug_base_url=$(value WNH_DEBUG_BASE_URL)
|
|
|
|
if all_set DEPLOYMENT_ENV PHX_HOST PHX_SCHEME PHX_URL_PORT WNH_BASE_URL WNH_DEBUG_BASE_URL &&
|
|
[[ "$base_url" == "$debug_base_url" ]] &&
|
|
[[ "$base_url" == "$phx_scheme://$phx_host" ||
|
|
"$base_url" == "$phx_scheme://$phx_host:$phx_port" ]] &&
|
|
! contains_template_marker "$base_url"; then
|
|
ready "public origin" "deployment=$deployment_env; one canonical Android/web origin"
|
|
else
|
|
invalid "public origin" "DEPLOYMENT_ENV/PHX_*/WNH_*_BASE_URL are incomplete or inconsistent"
|
|
fi
|
|
|
|
if all_set SECRET_KEY_BASE HANDOVER_SECRET RELEASE_COOKIE METRICS_TOKEN; then
|
|
ready "application secrets" "four required independent values are present"
|
|
else
|
|
missing "application secrets" "SECRET_KEY_BASE, HANDOVER_SECRET, RELEASE_COOKIE, METRICS_TOKEN"
|
|
fi
|
|
|
|
smtp_relay=$(value SMTP_RELAY)
|
|
email_delivery_provider=$(value EMAIL_DELIVERY_PROVIDER)
|
|
email_delivery_provider=${email_delivery_provider:-smtp}
|
|
if [[ "$email_delivery_provider" != "smtp" ]]; then
|
|
invalid "transactional email" "EMAIL_DELIVERY_PROVIDER must be smtp"
|
|
elif ! all_set SMTP_RELAY SMTP_PORT SMTP_AUTH SMTP_TLS SMTP_SSL EMAIL_FROM_ADDRESS; then
|
|
missing "transactional email" "SMTP transport and sender fields"
|
|
elif [[ "$smtp_relay" == "mailpit" ]]; then
|
|
local_only "transactional email" "Mailpit captures messages locally; it cannot deliver public email"
|
|
elif [[ "$(value SMTP_AUTH)" == "always" ]] && ! all_set SMTP_USERNAME SMTP_PASSWORD; then
|
|
partial "transactional email" "authenticated SMTP requires both username and password"
|
|
else
|
|
ready "transactional email" "external SMTP transport is configured"
|
|
fi
|
|
|
|
if is_set SUPPORT_INBOX_ADDRESS; then
|
|
ready "support inbox" "operator destination is configured"
|
|
else
|
|
missing "support inbox" "SUPPORT_INBOX_ADDRESS"
|
|
fi
|
|
|
|
if all_empty GOOGLE_OAUTH_CLIENT_ID GOOGLE_OAUTH_CLIENT_SECRET; then
|
|
missing "Google sign-in" "GOOGLE_OAUTH_CLIENT_ID and GOOGLE_OAUTH_CLIENT_SECRET"
|
|
elif all_set GOOGLE_OAUTH_CLIENT_ID GOOGLE_OAUTH_CLIENT_SECRET; then
|
|
ready "Google sign-in" "client ID and secret are both configured"
|
|
else
|
|
partial "Google sign-in" "client ID and secret must be configured together"
|
|
fi
|
|
|
|
if all_empty WEB_PUSH_VAPID_PUBLIC_KEY WEB_PUSH_VAPID_PRIVATE_KEY WEB_PUSH_VAPID_SUBJECT; then
|
|
missing "browser Web Push" "VAPID public/private keys and subject"
|
|
elif all_set WEB_PUSH_VAPID_PUBLIC_KEY WEB_PUSH_VAPID_PRIVATE_KEY WEB_PUSH_VAPID_SUBJECT; then
|
|
case "$(value WEB_PUSH_VAPID_SUBJECT)" in
|
|
mailto:* | https://*) ready "browser Web Push" "complete VAPID configuration" ;;
|
|
*) invalid "browser Web Push" "WEB_PUSH_VAPID_SUBJECT must use mailto: or https://" ;;
|
|
esac
|
|
else
|
|
partial "browser Web Push" "all three VAPID values are required together"
|
|
fi
|
|
|
|
if all_empty WNH_FIREBASE_APPLICATION_ID WNH_FIREBASE_API_KEY \
|
|
WNH_FIREBASE_PROJECT_ID WNH_FIREBASE_GCM_SENDER_ID; then
|
|
missing "Android Firebase client" "four WNH_FIREBASE_* Android client values"
|
|
elif all_set WNH_FIREBASE_APPLICATION_ID WNH_FIREBASE_API_KEY \
|
|
WNH_FIREBASE_PROJECT_ID WNH_FIREBASE_GCM_SENDER_ID; then
|
|
ready "Android Firebase client" "complete client configuration"
|
|
else
|
|
partial "Android Firebase client" "all four WNH_FIREBASE_* values are required together"
|
|
fi
|
|
|
|
fcm_file=$(value FCM_SERVICE_ACCOUNT_FILE)
|
|
fcm_base64=$(value FCM_SERVICE_ACCOUNT_JSON_BASE64)
|
|
if [[ -z "$(value FCM_PROJECT_ID)" && -z "$fcm_file" && -z "$fcm_base64" ]]; then
|
|
missing "Android FCM delivery" "FCM project ID and one service-account source"
|
|
elif [[ -z "$(value FCM_PROJECT_ID)" || (-n "$fcm_file" && -n "$fcm_base64") ||
|
|
(-z "$fcm_file" && -z "$fcm_base64") ]]; then
|
|
partial "Android FCM delivery" "project ID and exactly one credential source are required"
|
|
elif [[ -n "$fcm_file" ]]; then
|
|
if [[ "$fcm_file" == /* && -r "$fcm_file" ]]; then
|
|
ready "Android FCM delivery" "readable service-account file is configured"
|
|
else
|
|
invalid "Android FCM delivery" "FCM_SERVICE_ACCOUNT_FILE must be an absolute readable file"
|
|
fi
|
|
elif printf '%s' "$fcm_base64" | base64 --decode 2>/dev/null |
|
|
jq -e '.type == "service_account" and (.project_id | type == "string")' >/dev/null 2>&1; then
|
|
ready "Android FCM delivery" "valid Base64 service-account document is configured"
|
|
else
|
|
invalid "Android FCM delivery" "Base64 credential is not a service-account JSON document"
|
|
fi
|
|
|
|
if all_empty ANDROID_APP_LINKS_PACKAGE_NAME ANDROID_APP_LINKS_SHA256_CERT_FINGERPRINTS; then
|
|
missing "Android App Links" "package name and signing certificate fingerprint"
|
|
elif all_set ANDROID_APP_LINKS_PACKAGE_NAME ANDROID_APP_LINKS_SHA256_CERT_FINGERPRINTS; then
|
|
ready "Android App Links" "package and signing fingerprints are configured"
|
|
else
|
|
partial "Android App Links" "package and signing fingerprints must be configured together"
|
|
fi
|
|
|
|
if all_set WNH_ANDROID_VERSION_CODE WNH_ANDROID_VERSION_NAME \
|
|
WNH_ANDROID_SIGNING_KEY_ALIAS WNH_ANDROID_STAGING_SIGNING_KEY_ALIAS; then
|
|
ready "Android release inputs" "version and separate production/staging signing aliases are present"
|
|
else
|
|
missing "Android release inputs" "version code/name and both signing aliases"
|
|
fi
|
|
|
|
printf '\nEnvironment readiness: %d blocking item(s), %d local-only warning(s).\n' \
|
|
"$failures" "$warnings"
|
|
|
|
if [[ "$mode" == "--require-release" && ($failures -ne 0 || $warnings -ne 0) ]]; then
|
|
exit 1
|
|
fi
|