#!/usr/bin/env bash # Send a release bundle and environment to server B. No application .env is written. # Requires Bash/OpenSSH locally and Bash/tar/base64/Docker Compose on server B. # Returns the remote deployment status; credentials never appear in SSH arguments. set -Eeuo pipefail umask 077 cd "$(dirname "${BASH_SOURCE[0]}")/.." for key in PROD_HOST PROD_USER DEPLOY_PATH PROD_SSH_KEY PROD_KNOWN_HOSTS IMAGE_PREFIX DEPLOY_TAG REGISTRY_USERNAME REGISTRY_PASSWORD WQ_EMAIL WQ_PASSWORD DATABASE_URL ADMIN_PASSWORD ENCRYPTION_KEY PUBLIC_ORIGIN; do if [[ -z "${!key:-}" ]]; then echo "Missing required Gitea configuration: $key" >&2 exit 1 fi done if [[ "$DEPLOY_PATH" != /* || ! "$DEPLOY_TAG" =~ ^[a-zA-Z0-9_][a-zA-Z0-9_.-]{0,127}$ ]]; then echo 'DEPLOY_PATH must be absolute and DEPLOY_TAG must be a valid Docker tag.' >&2 exit 1 fi ssh_dir=$(mktemp -d) trap 'rm -rf -- "$ssh_dir"' EXIT trap 'exit 130' INT trap 'exit 143' TERM trap 'exit 129' HUP printf '%s\n' "$PROD_SSH_KEY" > "$ssh_dir/id" printf '%s\n' "$PROD_KNOWN_HOSTS" > "$ssh_dir/known_hosts" unset PROD_SSH_KEY PROD_KNOWN_HOSTS ssh_options=( -F /dev/null -T -i "$ssh_dir/id" -p "${PROD_PORT:-22}" -l "$PROD_USER" -o BatchMode=yes -o IdentitiesOnly=yes -o StrictHostKeyChecking=yes -o "UserKnownHostsFile=$ssh_dir/known_hosts" -o GlobalKnownHostsFile=/dev/null -o ConnectTimeout=15 -o ServerAliveInterval=15 -o ServerAliveCountMax=4 ) # Bash %q preserves quotes, dollar signs and newlines without evaluating values. # Only this allowlist crosses SSH; private keys and the runner's environment stay local. { printf 'set -Eeuo pipefail\numask 077\n' for key in DEPLOY_PATH IMAGE_PREFIX DEPLOY_TAG REGISTRY_USERNAME REGISTRY_PASSWORD WQ_EMAIL WQ_PASSWORD DATABASE_URL ADMIN_PASSWORD ENCRYPTION_KEY PUBLIC_ORIGIN ADMIN_USERNAME MCP_ENABLED; do printf 'export %s=%q\n' "$key" "${!key:-}" done cat <<'REMOTE' mkdir -p -- "$DEPLOY_PATH/releases" release_dir=$(mktemp -d "$DEPLOY_PATH/releases/$DEPLOY_TAG.XXXXXX") cd "$release_dir" base64 -d <<'WQ_RELEASE_BUNDLE' | tar -xzf - REMOTE # A small base64 archive lets one SSH connection carry files and shell-quoted env. # Each attempt gets its own directory, so competing jobs cannot overwrite files. COPYFILE_DISABLE=1 tar -czf - compose.production.yaml scripts/deploy-production.sh | base64 printf '\nWQ_RELEASE_BUNDLE\n' # Docker must not consume the SSH script input. B does not need a Git checkout. printf 'exec bash scripts/deploy-production.sh