#!/usr/bin/env bash set -euo pipefail root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) production_host=${AI_GATEWAY_PRODUCTION_HOST:-root@110.42.51.33} remote_helper=${AI_GATEWAY_REMOTE_RELEASE_HELPER:-/usr/local/sbin/easyai-ai-gateway-release} action=deploy value= usage() { cat <<'EOF' Usage: scripts/deploy-production-release.sh dist/releases/.json scripts/deploy-production-release.sh --rollback scripts/deploy-production-release.sh --status This command changes production. Run it only after a separate successful publish command and explicit user confirmation. EOF } case ${1:-} in --status) action=status [[ $# -eq 1 ]] || { usage >&2; exit 64; } ;; --rollback) action=rollback [[ $# -eq 2 ]] || { usage >&2; exit 64; } value=$2 ;; -h|--help|'') usage [[ ${1:-} == -h || ${1:-} == --help ]] && exit 0 exit 64 ;; *) [[ $# -eq 1 ]] || { usage >&2; exit 64; } value=$1 ;; esac [[ $production_host =~ ^[A-Za-z0-9._-]+@[A-Za-z0-9.-]+$ ]] || { echo 'AI_GATEWAY_PRODUCTION_HOST must use user@host syntax' >&2 exit 1 } [[ $remote_helper =~ ^/[A-Za-z0-9._/-]+$ ]] || { echo 'AI_GATEWAY_REMOTE_RELEASE_HELPER must be an absolute safe path' >&2 exit 1 } if [[ $action == status ]]; then exec ssh -o BatchMode=yes "$production_host" "$remote_helper status" fi if [[ $action == rollback ]]; then [[ $value =~ ^[0-9a-f]{40}$ ]] || { echo 'rollback release must be a full lowercase Git SHA' >&2 exit 1 } ssh -o BatchMode=yes "$production_host" "$remote_helper rollback $value" echo "production_rollback=PASS release=$value" exit 0 fi manifest=$(cd "$(dirname "$value")" 2>/dev/null && pwd)/$(basename "$value") [[ -f $manifest && ! -L $manifest ]] || { echo 'release manifest must be a regular file' >&2 exit 1 } node "$root/scripts/release-manifest.mjs" validate "$manifest" source_sha=$(node "$root/scripts/release-manifest.mjs" get "$manifest" sourceSha) base_sha=$(node "$root/scripts/release-manifest.mjs" get "$manifest" baseReleaseSha) remote_manifest=/tmp/easyai-ai-gateway-release-$source_sha.json status_file=$(mktemp) cleanup() { rm -f "$status_file" ssh -o BatchMode=yes "$production_host" "rm -f $remote_manifest" >/dev/null 2>&1 || true } trap cleanup EXIT HUP INT TERM if ssh -o BatchMode=yes "$production_host" "$remote_helper status" >"$status_file" 2>/dev/null; then node "$root/scripts/release-manifest.mjs" validate "$status_file" >/dev/null current_sha=$(node "$root/scripts/release-manifest.mjs" get "$status_file" sourceSha) [[ $current_sha == "$base_sha" ]] || { printf 'production base changed after publish: current=%s manifest_base=%s\n' \ "$current_sha" "$base_sha" >&2 exit 1 } else [[ -z $base_sha ]] || { echo 'production release status is unavailable for a non-bootstrap manifest' >&2 exit 1 } fi scp -q "$manifest" "$production_host:$remote_manifest" ssh -o BatchMode=yes "$production_host" "$remote_helper deploy $remote_manifest" echo "production_deploy=PASS release=$source_sha"