Files
easyai-ai-gateway/scripts/deploy-production-release.sh
T
wangbo 2c7f8c14eb chore(release): 明确生产发布授权并支持集群发布
将发布与上线授权边界调整为一次明确的生产发布指令可连续执行 publish 和 deploy,同时保留仅构建镜像时的停止边界。

为现有发布脚本增加 compose/kubernetes helper 选择和显式 SSH 私钥支持,保持完整 SHA、digest 与生产基线校验不变。

验证:bash -n、ShellCheck、manual-release-test。
2026-07-28 04:36:53 +08:00

120 lines
4.0 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:-compose}
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.
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"
# 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"