feat: 增加通用编译/推送脚本

This commit is contained in:
2026-01-09 17:59:49 +08:00
parent c38a32bc78
commit 741749d849
+198
View File
@@ -0,0 +1,198 @@
#!/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 分支推送时会额外推送 latest 标签
# 全局变量,记录生成的镜像及所有标签
IMAGES_TRACKED=""
# 验证必需的环境变量
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() {
if [ -n "${SWR_USERNAME:-}" ] && [ -n "${SWR_PASSWORD:-}" ]; then
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
else
echo "错误: SWR_USERNAME 或 SWR_PASSWORD 未设置"
exit 1
fi
}
# 从 TAG 生成版本标签 (vx.y.z -> vx.y, vx)
generate_version_tags() {
local tag="$1"
local repo="$2"
local tags="$repo:$tag"
# 检查是否为语义化版本格式 (vx.y.z 或 x.y.z)
if echo "$tag" | grep -E '^[vV]?[0-9]+\.[0-9]+\.[0-9]+' >/dev/null 2>&1; then
# 提取主版本号 (vx -> x)
local major=$(echo "$tag" | sed -E 's/^([vV])?([0-9]+)\..*/\2/')
if [ -n "$major" ] && [ "$major" != "$tag" ]; then
tags="$tags,$repo:$major"
fi
# 提取次版本号 (vx.y -> x.y)
local minor=$(echo "$tag" | sed -E 's/^([vV])?([0-9]+\.[0-9]+)\..*/\2/')
if [ -n "$minor" ] && [ "$minor" != "$tag" ] && [ "$minor" != "$major" ]; then
tags="$tags,$repo:$minor"
fi
fi
echo "$tags"
}
# 记录镜像标签到全局变量
track_image() {
local image="$1"
if [ -z "$IMAGES_TRACKED" ]; then
IMAGES_TRACKED="$image"
else
IMAGES_TRACKED="$IMAGES_TRACKED,$image"
fi
}
# 构建并推送镜像
build_and_push() {
# 确定镜像标签
local IMAGE_TAG=""
if [ -n "${CI_COMMIT_TAG:-}" ]; then
IMAGE_TAG="${CI_COMMIT_TAG:-}"
else
# 使用 cut 截取前8位字符
IMAGE_TAG=$(echo "${CI_COMMIT_SHA:-}" | cut -c1-8)
fi
# 确定传入 Dockerfile 的 APP_VER 参数
local APP_VER_ARG=""
if [ "${CI_COMMIT_BRANCH:-}" = "main" ] || [ "${CI_COMMIT_BRANCH:-}" = "master" ]; then
# 主分支编译时不传版本信息(使用 Dockerfile 中的默认值)
APP_VER_ARG=""
elif [ -n "${CI_COMMIT_TAG:-}" ]; then
# 基于 tag 编译时,传入不含 v 前缀的版本号
local CLEAN_TAG=$(echo "${CI_COMMIT_TAG:-}" | sed 's/^vV*//' )
APP_VER_ARG="--build-arg APP_VER=${CLEAN_TAG}"
else
# 基于其他分支编译时,传入不含 v 前缀的分支名称
local CLEAN_BRANCH=$(echo "${CI_COMMIT_BRANCH:-}" | sed 's/^vV*//' )
APP_VER_ARG="--build-arg APP_VER=${CLEAN_BRANCH}"
fi
# 支持自定义注册表路径
if [ -z "${SWR_REGISTRY_REPO:-}" ]; then
SWR_REGISTRY_REPO="${SWR_REGISTRY:-}/colovu/${CI_REPO_NAME:-}"
fi
# 初始化TAGS变量以避免未定义错误
local TAGS=""
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_SHA: ${CI_COMMIT_SHA:-empty}"
echo "CI_COMMIT_BRANCH: ${CI_COMMIT_BRANCH:-empty}"
echo "SWR_REGISTRY: ${SWR_REGISTRY_REPO:-empty}"
echo "IMAGE_TAG: ${IMAGE_TAG:-empty}"
echo "APP_VER_ARG: ${APP_VER_ARG:-empty}"
echo "========================"
# 根据是否为标签版本构建镜像
if [ -n "${CI_COMMIT_TAG:-}" ]; then
# 生成版本标签 (包括主版本、次版本等)
TAGS=$(generate_version_tags "$IMAGE_TAG" "$SWR_REGISTRY_REPO")
# 如果是 main 分支,添加 latest 标签
if [ "${CI_COMMIT_BRANCH:-}" = "main" ]; then
TAGS="$TAGS,$SWR_REGISTRY_REPO:latest"
fi
# 构建并推送镜像
echo ""
echo "Build and push image: $TAGS"
# 将逗号分隔的标签转换为 docker buildx 参数
local build_args=""
IFS=','
for tag in $TAGS; do
build_args="$build_args -t $tag"
track_image "$tag"
done
unset IFS
docker buildx build --platform linux/amd64,linux/arm64 $APP_VER_ARG $build_args --push --provenance=false --sbom=false .
if [ $? -ne 0 ]; then
echo "错误: 镜像构建或推送失败"
exit 1
fi
else
# 非标签版本,只构建单个标签
echo ""
echo "Build and push image: $SWR_REGISTRY_REPO:$IMAGE_TAG"
docker buildx build --platform linux/amd64,linux/arm64 -t "$SWR_REGISTRY_REPO:$IMAGE_TAG" $APP_VER_ARG --push --provenance=false --sbom=false .
if [ $? -ne 0 ]; then
echo "错误: 镜像构建或推送失败"
exit 1
fi
track_image "$SWR_REGISTRY_REPO:$IMAGE_TAG"
fi
echo "Image pushed: $SWR_REGISTRY_REPO:$IMAGE_TAG"
}
# 清理构建缓存
cleanup() {
echo ""
echo "Cleaning up images and build cache..."
# 删除记录的所有镜像标签
if [ -n "$IMAGES_TRACKED" ]; then
echo "Removing tracked images: $IMAGES_TRACKED"
IFS=','
for image in $IMAGES_TRACKED; do
echo "Removing image: $image"
docker rmi "$image" 2>/dev/null || true
done
unset IFS
fi
docker buildx prune -f
docker rmi "$(docker images -f "dangling=true" -q)" 2>/dev/null || true
}
# 主函数
main() {
validate_env
docker_login
build_and_push
cleanup
}
# 执行主函数
main