95 lines
3.5 KiB
Bash
Executable File
95 lines
3.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# 小白复盘唯一安全构建入口(HEL-235 固化)
|
||
# 方式:从明确 Git 提交 git archive 流式传输到部署机 docker build,不使用任何服务器工作树。
|
||
# 铁律:禁止在服务器目录(如 /opt/1panel/docker/compose/xiaobaifupan)里 docker build;
|
||
# 禁止构建 latest 等不带提交短号的 tag;严禁向 192.168.200.36 构建或部署。
|
||
set -euo pipefail
|
||
|
||
HOST_DEFAULT="moxiaobai@192.168.200.11"
|
||
REPO_NAME="xiaobai-review"
|
||
|
||
usage() {
|
||
cat <<'EOF'
|
||
用法: tools/build_image.sh <commit> <tag>
|
||
<commit> 提交号(完整或前缀),必须能被 origin 解析;构建前会自动 fetch
|
||
<tag> 镜像 tag,必须以 -<提交短号7位> 结尾,锁定镜像来源;禁止 latest、rollback-*
|
||
示例: tools/build_image.sh cefc86917d89 verify-hel235-cefc869
|
||
说明: 仅构建镜像,不启动、不替换任何容器;换版与回滚另行人工执行。
|
||
EOF
|
||
exit 2
|
||
}
|
||
|
||
[ $# -eq 2 ] || usage
|
||
COMMIT="$1"
|
||
TAG="$2"
|
||
HOST="${XB_BUILD_HOST:-$HOST_DEFAULT}"
|
||
|
||
case "$HOST" in
|
||
*192.168.200.36*)
|
||
echo "拒绝:192.168.200.36 已永久废弃,严禁在其上构建或部署。" >&2
|
||
exit 1
|
||
;;
|
||
esac
|
||
|
||
cd "$(git rev-parse --show-toplevel)"
|
||
|
||
echo "==> 同步远端引用"
|
||
git fetch origin --prune --quiet
|
||
|
||
FULL_SHA="$(git rev-parse --verify --quiet "${COMMIT}^{commit}" || true)"
|
||
if [ -z "$FULL_SHA" ]; then
|
||
echo "拒绝:提交 ${COMMIT} 无法解析。构建源必须锁定到已推送 origin 的明确提交。" >&2
|
||
exit 1
|
||
fi
|
||
SHORT="${FULL_SHA:0:7}"
|
||
SUBJECT="$(git log -1 --format=%s "$FULL_SHA")"
|
||
|
||
case "$TAG" in
|
||
latest)
|
||
echo "拒绝:禁止构建 latest,模糊 tag 无法追溯来源提交。" >&2
|
||
exit 1
|
||
;;
|
||
rollback-*)
|
||
echo "拒绝:rollback-* 是部署时对既有镜像的人工 docker tag,不允许用来构建。" >&2
|
||
exit 1
|
||
;;
|
||
esac
|
||
if [[ "$TAG" != *-"$SHORT" ]]; then
|
||
echo "拒绝:tag「${TAG}」必须以 -${SHORT} 结尾,保证镜像 tag 与来源提交一一对应。" >&2
|
||
exit 1
|
||
fi
|
||
|
||
echo "==> 构建计划"
|
||
echo " 提交: ${FULL_SHA} ${SUBJECT}"
|
||
echo " 镜像: ${REPO_NAME}:${TAG} @ ${HOST}"
|
||
echo " 方式: git archive 流式构建(不读取服务器上任何代码目录)"
|
||
|
||
echo "==> 流式构建开始"
|
||
git archive --format=tar "$FULL_SHA" \
|
||
| ssh -o BatchMode=yes "$HOST" docker build --rm \
|
||
-t "${REPO_NAME}:${TAG}" \
|
||
--label "org.opencontainers.image.revision=${FULL_SHA}" \
|
||
--label "org.opencontainers.image.created=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
||
--label "org.opencontainers.image.source=git-archive-stream" \
|
||
-
|
||
|
||
echo "==> 回读校验镜像内记录的提交号"
|
||
GOT="$(ssh -o BatchMode=yes "$HOST" docker image inspect "${REPO_NAME}:${TAG}" \
|
||
--format '{{index .Config.Labels "org.opencontainers.image.revision"}}')"
|
||
if [ "$GOT" != "$FULL_SHA" ]; then
|
||
echo "校验失败:镜像 revision=${GOT},期望 ${FULL_SHA}。删除不可信镜像,中止。" >&2
|
||
ssh -o BatchMode=yes "$HOST" docker rmi "${REPO_NAME}:${TAG}" >/dev/null 2>&1 || true
|
||
exit 1
|
||
fi
|
||
|
||
IMAGE_ID="$(ssh -o BatchMode=yes "$HOST" docker image inspect "${REPO_NAME}:${TAG}" --format '{{.Id}}')"
|
||
SHORT_ID="${IMAGE_ID##*:}"
|
||
|
||
ssh -o BatchMode=yes "$HOST" \
|
||
'mkdir -p ~/xiaobai-build && printf "%s\t%s\t%s\t%s\tgit-archive-stream\n" "$(date "+%F %T")" "$1" "$2" "$3" >> ~/xiaobai-build/BUILD_LOG.tsv' \
|
||
"build-image" "${REPO_NAME}:${TAG}" "${FULL_SHA}" "${SHORT_ID}"
|
||
|
||
echo "==> 完成"
|
||
echo " ${REPO_NAME}:${TAG} (${SHORT_ID})"
|
||
echo " 来源提交 ${FULL_SHA} 已写入镜像 label 与 ~/xiaobai-build/BUILD_LOG.tsv"
|