Files
common/build_push.sh
T

214 lines
6.4 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/sh
# 通用镜像编译和推送脚本
# 用于在 CI/CD 环境中构建和推送多架构 Docker 镜像到 SWR 注册表
set -e
# 脚本配置说明:
# - 需要设置 CI/CD 工具的 Secret 环境变量 SWR_REGISTRY, SWR_USERNAME, SWR_PASSWORD
# - 支持 CI_REPO_NAME, CI_COMMIT_TAG, CI_COMMIT_SHA, CI_COMMIT_BRANCH 等 CI/CD 变量
# - 默认构建 linux/amd64,linux/arm64 双架构镜像
# - main/master 分支推送时会额外推送 latest 标签
# - 符合 semver 规范形式的标签会生成多个标签 (vx.y.z -> vx.y, vx)
# 全局变量,记录生成的镜像及所有标签
IMAGES_TRACKED=""
# ========== 工具函数 ==========
# 删除字符串开头的 v
strip_v_prefix() {
case "$1" in
v*|V*) printf '%s\n' "${1#?}" ;;
*) printf '%s\n' "$1" ;;
esac
}
# 判断是否为 semver-like(支持 v?x 或 v?x.y 或 v?x.y.z
is_semver_like() {
local input="$1"
# 支持: 123, v1, 2.3, V4.5.6 等,最多三级,必须全为数字
echo "$input" | grep -E '^[vV]?[0-9]+(\.[0-9]+){0,2}$' >/dev/null 2>&1
}
# 清洗 Docker 标签:替换非法字符(只保留 [a-zA-Z0-9_.-]
sanitize_label() {
local label="$1"
# 将 / \ { } ( ) [ ] 空格 等替换为 _
echo "$label" | sed 's/[^a-zA-Z0-9_.-]/_/g'
}
# 从 semver 字符串生成带 v 前缀的多级标签 (vx.y.z -> vx.y, vx)
generate_version_tags() {
local ver="$1"
local repo="$2"
local clean_ver=$(echo "$ver" | sed 's/^[vV]//')
# 提取各段
local major=$(echo "$clean_ver" | cut -d. -f1)
local minor=$(echo "$clean_ver" | cut -d. -f2)
local patch=$(echo "$clean_ver" | cut -d. -f3)
local tags="$repo:v$clean_ver"
# v<major>
if [ -n "$major" ] && [ "$major" != "$clean_ver" ]; then
tags="$tags,$repo:v$major"
fi
# v<major>.<minor>
if [ -n "$minor" ]; then
local mm="$major.$minor"
if [ "$mm" != "$clean_ver" ]; then
tags="$tags,$repo:v$mm"
fi
fi
echo "$tags"
}
# ========== CI 逻辑 ==========
# 验证必需的环境变量
validate_env() {
if [ -z "${SWR_REGISTRY:-}" ]; then
echo "==> 错误: SWR_REGISTRY 环境变量未设置"
exit 1
fi
if [ -z "${SWR_USERNAME:-}" ] || [ -z "${SWR_PASSWORD:-}" ]; then
echo "==> 错误: SWR_USERNAME 或 SWR_PASSWORD 未设置"
exit 1
fi
}
# 登录到 SWR 注册表
docker_login() {
echo "==> 登录到注册表: ${SWR_REGISTRY}"
# 使用管道避免密码在命令行中暴露
echo "${SWR_PASSWORD}" | docker login -u "${SWR_USERNAME}" --password-stdin "${SWR_REGISTRY}"
if [ $? -ne 0 ]; then
echo "==> 错误: 无法登录到注册表 ${SWR_REGISTRY}"
exit 1
fi
}
# 记录镜像标签到全局变量
track_image() {
local image="$1"
if [ -z "$IMAGES_TRACKED" ]; then
IMAGES_TRACKED="$image"
else
IMAGES_TRACKED="$IMAGES_TRACKED,$image"
fi
}
# 构建并推送镜像
build_and_push() {
local SWR_REPO="${SWR_REGISTRY_REPO:-${SWR_REGISTRY}/colovu/${CI_REPO_NAME}}"
local APP_VER_ARG=""
local build_args=""
# 设置 APP_VER(用于 Dockerfile ARG),始终去除 v/V
if [ -n "${CI_COMMIT_TAG:-}" ]; then
APP_VER_ARG="--build-arg APP_VER=$(strip_v_prefix "$CI_COMMIT_TAG")"
elif [ -n "${CI_COMMIT_BRANCH:-}" ] && [ "$CI_COMMIT_BRANCH" != "main" ] && [ "$CI_COMMIT_BRANCH" != "master" ]; then
APP_VER_ARG="--build-arg APP_VER=$(strip_v_prefix "$CI_COMMIT_BRANCH")"
fi
echo "==> Debug Variables"
echo "==> CI_REPO: ${CI_REPO:-empty}"
echo "==> CI_REPO_NAME: ${CI_REPO_NAME:-empty}"
echo "==> CI_COMMIT_TAG: ${CI_COMMIT_TAG:-empty}"
echo "==> CI_COMMIT_BRANCH: ${CI_COMMIT_BRANCH:-empty}"
echo "==> CI_COMMIT_SHA: ${CI_COMMIT_SHA:-empty}"
echo "==> SWR_REPO: $SWR_REPO"
echo "==> APP_VER_ARG: ${APP_VER_ARG:-empty}"
# ===== TAG 触发 =====
if [ -n "${CI_COMMIT_TAG:-}" ]; then
if is_semver_like "$CI_COMMIT_TAG"; then
TAGS=$(generate_version_tags "$CI_COMMIT_TAG" "$SWR_REPO")
else
# 非 semver tag → 清洗后使用
local safe_tag=$(sanitize_label "$CI_COMMIT_TAG")
TAGS="$SWR_REPO:$safe_tag"
fi
IFS=','
for tag in $TAGS; do
build_args="$build_args -t $tag"
track_image "$tag"
done
unset IFS
echo ""
echo "==> 构建 Tag 镜像: $TAGS"
docker buildx build --platform linux/amd64,linux/arm64 $APP_VER_ARG $build_args --progress=plain --push --provenance=false --sbom=false .
# ===== 分支触发 =====
elif [ -n "${CI_COMMIT_BRANCH:-}" ]; then
if [ "$CI_COMMIT_BRANCH" = "main" ] || [ "$CI_COMMIT_BRANCH" = "master" ]; then
# 主干分支
build_args="-t $SWR_REPO:latest"
track_image "$SWR_REPO:latest"
elif is_semver_like "$CI_COMMIT_BRANCH" && echo "$CI_COMMIT_BRANCH" | grep -q '\.'; then
# 语义化分支 → 生成带 v 的多级标签; 仅当包含 '.' 时才生成多级标签(如 v1.2.3 → v1.2, v1
TAGS=$(generate_version_tags "$CI_COMMIT_BRANCH" "$SWR_REPO")
IFS=','
for tag in $TAGS; do
build_args="$build_args -t $tag"
track_image "$tag"
done
unset IFS
else
# 普通分支或纯数字(如 12, feature-x)→ 单标签 + sha
local safe_branch=$(sanitize_label "$CI_COMMIT_BRANCH")
build_args="-t $SWR_REPO:$safe_branch"
track_image "$SWR_REPO:$safe_branch"
fi
echo ""
echo "==> 构建 Branch 镜像: $build_args"
docker buildx build --platform linux/amd64,linux/arm64 $APP_VER_ARG $build_args --progress=plain --push --provenance=false --sbom=false .
else
echo "==> 错误: 无有效构建上下文"
exit 1
fi
}
cleanup() {
echo ""
echo "==> 清理编译镜像及缓存..."
if [ -n "$IMAGES_TRACKED" ]; then
IFS=','
for img in $IMAGES_TRACKED; do
echo " - 清理: $img"
docker rmi "$img" 2>/dev/null || true
done
unset IFS
fi
echo " - 清理缓存"
docker buildx prune -f >/dev/null 2>&1 || true
docker rmi $(docker images -q -f "dangling=true") 2>/dev/null || true
}
# 主函数
main() {
validate_env
docker_login
build_and_push
cleanup
echo ""
echo "==> 编译镜像完成"
}
# 执行主函数
main