Files
easyai-ai-gateway/scripts/deploy-production-release.sh
T
wangbo 9bcfec890b fix(release): 默认使用 Kubernetes 生产通道
原因:旧 Compose 生产环境已退役,默认通道仍指向 Compose 会在每次发布时先命中无效数据库。\n\n影响:发布和部署脚本默认读取 Kubernetes release helper;仍可通过显式 AI_GATEWAY_DEPLOY_MODE=compose 操作遗留环境。\n\n验证:bash -n、ShellCheck 和 manual-release-test 全部通过。
2026-08-04 18:44:19 +08:00

126 lines
4.2 KiB
Bash
Executable File

#!/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}
deploy_mode=${AI_GATEWAY_DEPLOY_MODE:-kubernetes}
case $deploy_mode in
compose) default_remote_helper=/usr/local/sbin/easyai-ai-gateway-release ;;
kubernetes) default_remote_helper=/usr/local/sbin/easyai-ai-gateway-cluster-release ;;
*) echo 'AI_GATEWAY_DEPLOY_MODE must be compose or kubernetes' >&2; exit 1 ;;
esac
remote_helper=${AI_GATEWAY_REMOTE_RELEASE_HELPER:-$default_remote_helper}
production_ssh_key=${AI_GATEWAY_PRODUCTION_SSH_KEY:-}
action=deploy
value=
usage() {
cat <<'EOF'
Usage:
scripts/deploy-production-release.sh dist/releases/<git-sha>.json
scripts/deploy-production-release.sh --rollback <historical-git-sha>
scripts/deploy-production-release.sh --status
This command changes production. Run it only after a successful publish
command and an explicit user request to release or deploy to production.
AI_GATEWAY_DEPLOY_MODE defaults to kubernetes. Set it to compose only for an
explicit legacy-environment operation.
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
}
ssh_options=(-o BatchMode=yes)
scp_options=(-q -o BatchMode=yes)
if [[ -n $production_ssh_key ]]; then
[[ $production_ssh_key == /* && -f $production_ssh_key && ! -L $production_ssh_key ]] || {
echo 'AI_GATEWAY_PRODUCTION_SSH_KEY must be an absolute regular file' >&2
exit 1
}
ssh_options+=(-i "$production_ssh_key")
scp_options+=(-i "$production_ssh_key")
fi
if [[ $action == status ]]; then
exec ssh "${ssh_options[@]}" "$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
}
# shellcheck disable=SC2029 # Both arguments are validated safe tokens above.
ssh "${ssh_options[@]}" "$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 "${ssh_options[@]}" "$production_host" rm -f -- "$remote_manifest" >/dev/null 2>&1 || true
}
trap cleanup EXIT HUP INT TERM
if ssh "${ssh_options[@]}" "$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 "${scp_options[@]}" "$manifest" "$production_host:$remote_manifest"
if [[ $deploy_mode == kubernetes ]]; then
"$root/scripts/cluster/install-release-helper.sh"
fi
# shellcheck disable=SC2029 # remote_manifest is derived from a validated full SHA.
ssh "${ssh_options[@]}" "$production_host" "$remote_helper" deploy "$remote_manifest"
echo "production_deploy=PASS release=$source_sha"