#!/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 [--live-revision ] 准备构建/部署的提交(完整或前缀) --live-revision 仅测试用:直接指定线上提交,跳过 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" = "" ]; 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" = "" ]; 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} 的祖先"