删除 Gitea Actions、Tag/Main 自动流水线和旧 Runner 配置,取消 Git 操作与发布授权的绑定。\n\n新增本地镜像发布、固定生产部署助手、digest manifest、迁移安全检查、simulation 冒烟及显式回滚流程。\n\n验证:pnpm lint、pnpm test、pnpm build、Go 全量测试、ShellCheck、Compose 配置、人工发布测试和 linux/amd64 完整栈冒烟。
101 lines
3.0 KiB
Bash
Executable File
101 lines
3.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}
|
|
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/<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 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"
|