From 32959c733061b0c52cc5615867fe08dd9943ced1 Mon Sep 17 00:00:00 2001 From: HenryDu8133 <813367384@qq.com> Date: Sun, 13 Sep 2026 11:55:23 +0800 Subject: [PATCH] =?UTF-8?q?ci(workflow):=20=E4=BF=AE=E5=A4=8D=20deploy=20?= =?UTF-8?q?=E6=AD=A5=E9=AA=A4=20bash=20=E8=AF=AD=E6=B3=95=EF=BC=8C?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=E6=95=B0=E7=BB=84=E5=8F=82=E6=95=B0=E5=B9=B6?= =?UTF-8?q?=E9=80=82=E9=85=8D=20docker=20run=20=E7=9B=B4=E6=8E=A5=E9=83=A8?= =?UTF-8?q?=E7=BD=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/build-deploy.yml | 122 +++++++++++++++++++++++++++--- 1 file changed, 111 insertions(+), 11 deletions(-) diff --git a/.gitea/workflows/build-deploy.yml b/.gitea/workflows/build-deploy.yml index a74586a..0a109aa 100644 --- a/.gitea/workflows/build-deploy.yml +++ b/.gitea/workflows/build-deploy.yml @@ -10,7 +10,7 @@ env: IMAGE_NAME: henry_du/bywcloudreve SERVER_HOST: 192.140.163.199 SERVER_USER: ubuntu - COMPOSE_PATH: /home/ubuntu/cloudreve + CONTAINER_NAME: cloudreve jobs: build-and-deploy: @@ -66,19 +66,119 @@ jobs: username: ${{ env.SERVER_USER }} key: ${{ secrets.SSH_PRIVATE_KEY }} port: 22 + script_stop: true script: | - set -e - cd ${{ env.COMPOSE_PATH }} + set -euo pipefail + sudo -n true 2>/dev/null || SUDO="" + [ -z "${SUDO+x}" ] && SUDO="sudo" - echo "${{ secrets.GITEA_TOKEN }}" | docker login ${{ env.REGISTRY }} -u "${{ gitea.actor }}" --password-stdin + CONTAINER="${{ env.CONTAINER_NAME }}" + NEW_IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest" - echo "Pulling latest image..." - docker compose pull cloudreve + echo "[1/6] 登录 Gitea Container Registry" + echo "${{ secrets.GITEA_TOKEN }}" | $SUDO docker login "${{ env.REGISTRY }}" -u "${{ gitea.actor }}" --password-stdin - echo "Recreating cloudreve container (preserving volumes)..." - docker compose up -d --remove-orphans cloudreve + echo "[2/6] 拉取最新镜像 ${NEW_IMAGE}" + $SUDO docker pull "${NEW_IMAGE}" - echo "Cleaning up old images..." - docker image prune -f + if ! $SUDO docker inspect "$CONTAINER" > /tmp/old.json 2>/dev/null; then + echo "[WARN] 现有容器 $CONTAINER 不存在,按默认参数首次启动(请确认 postgres/redis 已在 172.17.0.1)。" + $SUDO docker run -d \ + --name "$CONTAINER" \ + --restart unless-stopped \ + -p 5212:5212 -p 6888:6888 -p 6888:6888/udp \ + -v cloudreve_backend_data:/cloudreve/data \ + --add-host=postgresql:172.17.0.1 \ + --add-host=redis:172.17.0.1 \ + -e CR_CONF_Database.Type=postgres \ + -e CR_CONF_Database.Host=postgresql \ + -e CR_CONF_Database.User=cloudreve \ + -e CR_CONF_Database.Name=cloudreve \ + -e CR_CONF_Database.Port=5432 \ + -e CR_CONF_Redis.Server=redis:6379 \ + "$NEW_IMAGE" || true + echo "首次启动命令已执行。" + exit 0 + fi - echo "Deployment completed at $(date)" + echo "[3/6] 从现有容器提取运行参数 (安装 jq)" + which jq >/dev/null 2>&1 || ($SUDO apt-get update -qq && $SUDO apt-get install -y -qq jq >/dev/null) + + OLD_IMAGE="$(jq -r '.Config.Image' /tmp/old.json)" + RESTART_POLICY="$(jq -r '.HostConfig.RestartPolicy.Name' /tmp/old.json)" + NETWORK_MODE="$(jq -r '.HostConfig.NetworkMode' /tmp/old.json)" + echo "旧镜像: ${OLD_IMAGE} -> 新镜像: ${NEW_IMAGE}" + echo "重启策略: ${RESTART_POLICY}, 网络: ${NETWORK_MODE}" + + # 以数组方式构建参数,避免 eval 转义问题 + RUN_ARGS=( -d ) + RUN_ARGS+=( --name "${CONTAINER}_new" ) + [ "${RESTART_POLICY}" != "null" ] && [ -n "${RESTART_POLICY}" ] && RUN_ARGS+=( --restart "${RESTART_POLICY}" ) + [ "${NETWORK_MODE}" != "null" ] && [ -n "${NETWORK_MODE}" ] && RUN_ARGS+=( --network "${NETWORK_MODE}" ) + + # 环境变量 + while IFS= read -r env_line; do + [ -z "${env_line}" ] && continue + RUN_ARGS+=( -e "${env_line}" ) + done < <(jq -r '.Config.Env[]?' /tmp/old.json) + + # 端口映射 + while IFS=$'\t' read -r cport hostip hostport; do + [ -z "${cport}" ] && continue + proto="" + if [[ "${cport}" == */udp ]]; then + proto="/udp" + cport="${cport%/udp}" + elif [[ "${cport}" == */tcp ]]; then + cport="${cport%/tcp}" + fi + if [ -n "${hostport}" ]; then + ip_part="" + [ -n "${hostip}" ] && ip_part="${hostip}:" + RUN_ARGS+=( -p "${ip_part}${hostport}:${cport}${proto}" ) + else + RUN_ARGS+=( -p "${cport}${proto}" ) + fi + done < <(jq -r '.HostConfig.PortBindings | to_entries[]? | .key as $k | .value[]? | [$k, .HostIp, .HostPort] | @tsv' /tmp/old.json) + + # 卷挂载 + while IFS= read -r mline; do + [ -z "${mline}" ] && continue + RUN_ARGS+=( ${mline} ) + done < <(jq -r '.Mounts[]? | select(.Type=="bind" or .Type=="volume") as $m + | if $m.Type == "bind" then + "-v " + $m.Source + ":" + $m.Destination + (if $m.ReadOnly then ":ro" else "" end) + else + "-v " + $m.Name + ":" + $m.Destination + (if $m.ReadOnly then ":ro" else "" end) + end' /tmp/old.json 2>/dev/null || true) + + # Extra hosts + while IFS= read -r h; do + [ -z "${h}" ] && continue + RUN_ARGS+=( --add-host "${h}" ) + done < <(jq -r '.HostConfig.ExtraHosts[]?' /tmp/old.json 2>/dev/null || true) + + echo "[4/6] 以相同参数启动新容器 (${#RUN_ARGS[@]} args)" + NEW_CID=$($SUDO docker run "${RUN_ARGS[@]}" "${NEW_IMAGE}") + echo "新容器ID: ${NEW_CID}" + sleep 5 + + NEW_STATUS=$($SUDO docker inspect -f '{{.State.Status}}' "${NEW_CID}" 2>/dev/null || echo "unknown") + echo "新容器状态: ${NEW_STATUS}" + if [ "${NEW_STATUS}" != "running" ] && [ "${NEW_STATUS}" != "created" ]; then + echo "[ERROR] 新容器未正常运行,输出最近日志后回滚。" + $SUDO docker logs --tail 60 "${NEW_CID}" || true + $SUDO docker rm -f "${NEW_CID}" 2>/dev/null || true + exit 1 + fi + + echo "[5/6] 停用旧容器并切换新容器为目标名称" + $SUDO docker stop -t 30 "$CONTAINER" 2>/dev/null || true + $SUDO docker rm -f "$CONTAINER" 2>/dev/null || true + $SUDO docker rename "${CONTAINER}_new" "$CONTAINER" + + echo "[6/6] 清理旧镜像 / 悬空镜像" + $SUDO docker image prune -f >/dev/null 2>&1 || true + + FINAL_IMG=$($SUDO docker inspect -f '{{.Config.Image}}' "$CONTAINER") + echo "✅ 部署完成 @ $(date) 。容器 $CONTAINER 当前镜像: ${FINAL_IMG}"