diff --git a/build_push.sh b/build_push.sh new file mode 100644 index 0000000..7ef61dc --- /dev/null +++ b/build_push.sh @@ -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 + +