From 4b0551e2723fa236cd9dde5335543fd84784df94 Mon Sep 17 00:00:00 2001 From: zydi Date: Fri, 19 Dec 2025 06:25:49 +0000 Subject: [PATCH] update --- actions/build-images/action.yml | 155 +++++++++++++++++++++++++++++++ actions/deploy-docker/action.yml | 128 +++++++++++++++++++++++++ 2 files changed, 283 insertions(+) create mode 100644 actions/build-images/action.yml create mode 100644 actions/deploy-docker/action.yml diff --git a/actions/build-images/action.yml b/actions/build-images/action.yml new file mode 100644 index 0000000..7fcfcd5 --- /dev/null +++ b/actions/build-images/action.yml @@ -0,0 +1,155 @@ +name: 'Reusable Docker Image Build' +description: '通用的 Docker 镜像构建 / 验证 / 清理逻辑,用于在各仓库中复用' + +inputs: + dockerfile: + description: 'Dockerfile 路径(相对于仓库根目录)' + required: true + context: + description: '构建上下文路径(相对于仓库根目录)' + required: false + default: '.' + image_name: + description: 'Docker 镜像名称(不含标签)' + required: true + image_tag: + description: 'Docker 镜像标签/版本' + required: true + enable_cache: + description: '是否启用 Docker 构建缓存' + required: false + default: 'true' + build_args: + description: 'Docker 构建参数(每行一个,格式: KEY=VALUE)' + required: false + default: '' + +runs: + using: 'composite' + steps: + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Cache Docker layers + if: ${{ inputs.enable_cache == 'true' }} + uses: actions/cache@v3 + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-${{ inputs.image_name }}-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx-${{ inputs.image_name }}- + ${{ runner.os }}-buildx- + + - name: Prepare build arguments + id: prepare + shell: bash + run: | + echo "====== 准备构建参数 ======" + + DOCKERFILE="${{ inputs.dockerfile }}" + CONTEXT="${{ inputs.context }}" + IMAGE_NAME="${{ inputs.image_name }}" + IMAGE_TAG="${{ inputs.image_tag }}" + FULL_IMAGE="${IMAGE_NAME}:${IMAGE_TAG}" + + echo "Dockerfile: $DOCKERFILE" + echo "Context: $CONTEXT" + echo "Image: $FULL_IMAGE" + + # 验证 Dockerfile 是否存在 + if [ ! -f "$DOCKERFILE" ]; then + echo "✗ Dockerfile 不存在: $DOCKERFILE" + exit 1 + fi + + # 验证构建上下文目录是否存在 + if [ ! -d "$CONTEXT" ]; then + echo "✗ 构建上下文目录不存在: $CONTEXT" + exit 1 + fi + + echo "✓ 构建参数验证通过" + echo "" + + # 设置输出变量 + echo "full_image=$FULL_IMAGE" >> "$GITHUB_OUTPUT" + + # 处理构建参数 + BUILD_ARGS="" + if [ -n "${{ inputs.build_args }}" ]; then + echo "====== 构建参数 ======" + while IFS= read -r line; do + if [ -n "$line" ]; then + echo " $line" + BUILD_ARGS="$BUILD_ARGS --build-arg $line" + fi + done <<< "${{ inputs.build_args }}" + echo "" + fi + + echo "build_args=$BUILD_ARGS" >> "$GITHUB_OUTPUT" + + - name: Build Docker image + shell: bash + run: | + echo "====== 开始构建 Docker 镜像 ======" + echo "构建时间: $(date '+%Y-%m-%d %H:%M:%S')" + echo "分支: ${{ github.ref_name }}" + echo "" + + echo "====== 构建配置 ======" + echo "镜像: ${{ steps.prepare.outputs.full_image }}" + echo "Dockerfile: ${{ inputs.dockerfile }}" + echo "Context: ${{ inputs.context }}" + echo "" + + # 构建命令 + BUILD_CMD="docker build \ + -t ${{ steps.prepare.outputs.full_image }} \ + -f ${{ inputs.dockerfile }} \ + ${{ steps.prepare.outputs.build_args }} \ + ${{ inputs.context }}" + + echo "执行命令:" + echo "$BUILD_CMD" + echo "" + + # 执行构建 + eval $BUILD_CMD + + echo "" + echo "====== 镜像构建完成 ======" + + - name: Verify image + shell: bash + run: | + echo "====== 验证构建的镜像 ======" + + IMAGE="${{ steps.prepare.outputs.full_image }}" + + if docker image inspect "${IMAGE}" >/dev/null 2>&1; then + SIZE=$(docker image inspect "${IMAGE}" --format='{{.Size}}' | awk '{printf "%.2f", $1/1024/1024}') + CREATED=$(docker image inspect "${IMAGE}" --format='{{.Created}}') + ID=$(docker image inspect "${IMAGE}" --format='{{.Id}}' | cut -d':' -f2 | cut -c1-12) + + echo "✓ ${IMAGE}" + echo " 镜像 ID: ${ID}" + echo " 大小: ${SIZE} MB" + echo " 创建时间: ${CREATED}" + else + echo "✗ ${IMAGE} 验证失败" + exit 1 + fi + + echo "" + echo "✓ 镜像验证成功" + + - name: Cleanup + if: always() + shell: bash + run: | + echo "====== 清理悬空镜像 ======" + docker image prune -f + echo "✓ 清理完成" + + diff --git a/actions/deploy-docker/action.yml b/actions/deploy-docker/action.yml new file mode 100644 index 0000000..a5283b5 --- /dev/null +++ b/actions/deploy-docker/action.yml @@ -0,0 +1,128 @@ +name: 'Reusable Docker Compose Deploy' +description: '通用的 Docker Compose 部署逻辑,用于在各仓库中复用' + +inputs: + compose_file: + description: 'Docker Compose 文件路径(相对于仓库根目录)' + required: true + compose_dir: + description: 'Docker Compose 工作目录(相对于仓库根目录,默认使用 compose_file 所在目录)' + required: false + default: '' + checkout_ref: + description: '要检出的分支或标签' + required: false + default: '' + enable_sparse_checkout: + description: '是否启用稀疏检出' + required: false + default: 'false' + sparse_checkout: + description: '稀疏检出路径列表(每行一个路径)' + required: false + default: '' + startup_wait: + description: '服务启动后等待时间(秒)' + required: false + default: '0' + +runs: + using: 'composite' + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: ${{ inputs.checkout_ref || github.ref }} + sparse-checkout: ${{ inputs.enable_sparse_checkout == 'true' && inputs.sparse_checkout || '' }} + sparse-checkout-cone-mode: false + fetch-depth: ${{ inputs.enable_sparse_checkout == 'true' && 1 || 0 }} + + - name: Prepare compose paths + id: prepare + shell: bash + run: | + set -e + + COMPOSE_FILE="${{ inputs.compose_file }}" + COMPOSE_DIR_INPUT="${{ inputs.compose_dir }}" + + if [ -z "$COMPOSE_DIR_INPUT" ]; then + DIR="$(dirname "$COMPOSE_FILE")" + if [ "$DIR" = "." ]; then + DIR="." + fi + else + DIR="$COMPOSE_DIR_INPUT" + fi + + echo "compose_file=$COMPOSE_FILE" >> "$GITHUB_OUTPUT" + echo "compose_dir=$DIR" >> "$GITHUB_OUTPUT" + + echo "Compose file: $COMPOSE_FILE" + echo "Compose dir : $DIR" + + - name: Stop old services + shell: bash + run: | + echo "====== 停止旧服务 ======" + + cd "${{ steps.prepare.outputs.compose_dir }}" + + # 停止并移除容器 + docker compose -f "${{ steps.prepare.outputs.compose_file }}" down || true + + echo "✓ 旧服务已停止" + echo "" + + - name: Deploy services + shell: bash + run: | + echo "====== 部署服务 ======" + + cd "${{ steps.prepare.outputs.compose_dir }}" + + echo "启动服务..." + docker compose -f "${{ steps.prepare.outputs.compose_file }}" up -d + + echo "✓ 服务已启动" + echo "" + + - name: Wait for services + if: ${{ inputs.startup_wait != '0' }} + shell: bash + run: | + echo "====== 等待服务初始化 ======" + echo "等待时间: ${{ inputs.startup_wait }} 秒" + sleep ${{ inputs.startup_wait }} + echo "✓ 等待完成" + echo "" + + - name: Verify services + shell: bash + run: | + echo "====== 验证服务状态 ======" + + cd "${{ steps.prepare.outputs.compose_dir }}" + + docker compose -f "${{ steps.prepare.outputs.compose_file }}" ps + echo "" + + # 检查是否有失败的服务 + FAILED_SERVICES=$(docker compose -f "${{ steps.prepare.outputs.compose_file }}" ps --status exited --format json | jq -r '.Service' 2>/dev/null || true) + + if [ -n "$FAILED_SERVICES" ]; then + echo "⚠ 以下服务启动失败:" + echo "$FAILED_SERVICES" + echo "" + echo "查看失败服务的日志:" + for service in $FAILED_SERVICES; do + echo "" + echo "=== $service 日志 ===" + docker compose -f "${{ steps.prepare.outputs.compose_file }}" logs --tail=50 "$service" + done + exit 1 + else + echo "✓ 所有服务运行正常" + fi + +