Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: multica-agent <github@multica.ai>
127 lines
3.9 KiB
Bash
Executable File
127 lines
3.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# 部署基线门禁(HEL-238):候选提交必须包含当前线上提交的全部历史。
|
||
# 线上提交号从运行中的容器镜像 label 读取,禁止人工填写“看起来正确”的基线。
|
||
set -euo pipefail
|
||
|
||
HOST_DEFAULT="moxiaobai@192.168.200.11"
|
||
CONTAINER_DEFAULT="xiaobai-review"
|
||
|
||
usage() {
|
||
cat <<'EOF'
|
||
用法: tools/check_deploy_baseline.sh <candidate_commit> [--live-revision <sha>]
|
||
<candidate_commit> 准备构建/部署的提交(完整或前缀)
|
||
--live-revision <sha> 仅测试用:直接指定线上提交,跳过 SSH 读取
|
||
环境变量:
|
||
XB_BUILD_HOST 部署机 SSH(默认 moxiaobai@192.168.200.11)
|
||
XB_LIVE_CONTAINER 运行中容器名(默认 xiaobai-review)
|
||
XB_LIVE_REVISION 若已设置则视为线上提交,不再 SSH
|
||
EOF
|
||
exit 2
|
||
}
|
||
|
||
[ $# -ge 1 ] || usage
|
||
CANDIDATE="$1"
|
||
shift
|
||
LIVE_OVERRIDE=""
|
||
while [ $# -gt 0 ]; do
|
||
case "$1" in
|
||
--live-revision)
|
||
[ $# -ge 2 ] || usage
|
||
LIVE_OVERRIDE="$2"
|
||
shift 2
|
||
;;
|
||
-h|--help)
|
||
usage
|
||
;;
|
||
*)
|
||
echo "拒绝:未知参数 $1" >&2
|
||
usage
|
||
;;
|
||
esac
|
||
done
|
||
|
||
HOST="${XB_BUILD_HOST:-$HOST_DEFAULT}"
|
||
CONTAINER="${XB_LIVE_CONTAINER:-$CONTAINER_DEFAULT}"
|
||
|
||
case "$HOST" in
|
||
*192.168.200.36*)
|
||
echo "拒绝:192.168.200.36 已永久废弃,严禁在其上构建或部署。" >&2
|
||
exit 1
|
||
;;
|
||
esac
|
||
|
||
cd "$(git rev-parse --show-toplevel)"
|
||
|
||
read_live_revision() {
|
||
if [ -n "${LIVE_OVERRIDE}" ]; then
|
||
printf '%s\n' "${LIVE_OVERRIDE}"
|
||
return
|
||
fi
|
||
if [ -n "${XB_LIVE_REVISION:-}" ]; then
|
||
printf '%s\n' "${XB_LIVE_REVISION}"
|
||
return
|
||
fi
|
||
ssh -o BatchMode=yes "$HOST" bash -s -- "$CONTAINER" <<'REMOTE'
|
||
set -euo pipefail
|
||
container="$1"
|
||
revision="$(docker inspect --format '{{index .Config.Labels "org.opencontainers.image.revision"}}' "$container" 2>/dev/null || true)"
|
||
if [ -z "$revision" ] || [ "$revision" = "<no value>" ]; then
|
||
image="$(docker inspect --format '{{.Image}}' "$container" 2>/dev/null || true)"
|
||
if [ -n "$image" ]; then
|
||
revision="$(docker inspect --format '{{index .Config.Labels "org.opencontainers.image.revision"}}' "$image" 2>/dev/null || true)"
|
||
fi
|
||
fi
|
||
if [ -z "$revision" ] || [ "$revision" = "<no value>" ]; then
|
||
echo "拒绝:无法从线上容器 ${container} 读取 org.opencontainers.image.revision。" >&2
|
||
exit 1
|
||
fi
|
||
printf '%s\n' "$revision"
|
||
REMOTE
|
||
}
|
||
|
||
LIVE_RAW="$(read_live_revision)"
|
||
LIVE_RAW="$(printf '%s' "$LIVE_RAW" | tr -d '[:space:]')"
|
||
if [ -z "$LIVE_RAW" ]; then
|
||
echo "拒绝:线上提交号为空,禁止继续构建或部署。" >&2
|
||
exit 1
|
||
fi
|
||
|
||
CANDIDATE_SHA="$(git rev-parse --verify --quiet "${CANDIDATE}^{commit}" || true)"
|
||
if [ -z "$CANDIDATE_SHA" ]; then
|
||
echo "拒绝:候选提交 ${CANDIDATE} 无法解析。" >&2
|
||
exit 1
|
||
fi
|
||
|
||
LIVE_SHA="$(git rev-parse --verify --quiet "${LIVE_RAW}^{commit}" || true)"
|
||
if [ -z "$LIVE_SHA" ]; then
|
||
echo "拒绝:线上提交 ${LIVE_RAW} 在本地仓库无法解析;请先 git fetch,禁止手工填写替代基线。" >&2
|
||
exit 1
|
||
fi
|
||
|
||
echo "==> 部署基线对照"
|
||
echo " 线上提交: ${LIVE_SHA}"
|
||
echo " 候选提交: ${CANDIDATE_SHA}"
|
||
|
||
echo "==> 候选相对线上的文件差异"
|
||
DIFF_FILES="$(git diff --name-only "$LIVE_SHA" "$CANDIDATE_SHA" || true)"
|
||
if [ -z "$DIFF_FILES" ]; then
|
||
echo " (无文件差异)"
|
||
else
|
||
printf '%s\n' "$DIFF_FILES" | sed 's/^/ /'
|
||
fi
|
||
|
||
echo "==> 候选将丢失的提交(线上有、候选没有)"
|
||
LOST="$(git log --oneline "$CANDIDATE_SHA".."$LIVE_SHA" || true)"
|
||
if [ -z "$LOST" ]; then
|
||
echo " (无)"
|
||
else
|
||
printf '%s\n' "$LOST" | sed 's/^/ /'
|
||
fi
|
||
|
||
if ! git merge-base --is-ancestor "$LIVE_SHA" "$CANDIDATE_SHA"; then
|
||
echo "拒绝:候选提交不是当前线上提交的后继,部署会丢失线上已有提交。禁止构建或部署。" >&2
|
||
exit 1
|
||
fi
|
||
|
||
echo "==> 祖先关系通过:线上 ${LIVE_SHA:0:7} 是候选 ${CANDIDATE_SHA:0:7} 的祖先"
|