# ═══════════════════════════════════════════════════════════════ # Demo 部署工作流 — CI 成功后自动部署 / 可手动触发 # # 触发条件: # 1. "CI — Docker Build & Push" 在 main/master 上成功完成后自动触发 # 2. workflow_dispatch 手动触发(可指定镜像标签) # # 运行环境: # - self-hosted runner(与 Gitea 同一服务器) # - Docker 部署,外置 MySQL & Caddy # # 前置配置(Gitea → 仓库 → 设置 → Secrets): # REGISTRY_TOKEN — 具有 package:read 权限的 Gitea Access Token # DEMO_SECRET_KEY — Flask SECRET_KEY(随机字符串,≥32位) # DEMO_ADMIN_PASS — Demo 管理员密码(首次部署时设置) # # 服务器前置条件: # - 已安装 Docker + docker compose(或 docker-compose) # - 已安装并运行 Caddy,配置了 sites 目录 include # - runner 用户在 docker 组 或 使用 sudo(见下方 SUDO_DOCKER 变量) # ═══════════════════════════════════════════════════════════════ name: Deploy — Demo Environment on: # CI 工作流在 main/master 完成后触发 workflow_run: workflows: - "CI — Docker Build & Push" branches: - main - master types: - completed # 手动触发,支持指定镜像标签 workflow_dispatch: inputs: image_tag: description: '镜像标签(默认: latest)' required: false default: 'latest' env: REGISTRY: git.hty1024.com # Demo 部署目录(需与服务器实际路径一致) DEPLOY_DIR: /opt/resource-library-demo # Caddy 站点配置目录(需在 Caddyfile 中通过 import 引用) CADDY_SITES_DIR: /etc/caddy/sites # 应用对外端口(Caddy 反向代理到此端口) APP_PORT: 5000 # Demo 域名 DOMAIN: prl.hty1024.com # 外置 MySQL 连接串 DB_URL: mysql+pymysql://resource_library:BWRVzzCwzuuP_1Hj@pma.hty1024.com:31000/resource_library jobs: deploy-demo: name: Deploy to Demo runs-on: self-hosted # workflow_run 触发时,仅在 CI 成功时继续 if: > github.event_name == 'workflow_dispatch' || (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success') steps: # ── 1. 检出代码(获取 compose 文件等) ────────────────────── - name: 检出代码 uses: actions/checkout@v4 # ── 2. 确定目标镜像标签 ────────────────────────────────────── - name: 确定镜像标签 id: image run: | REPO=$(echo "${{ github.repository }}" | tr '[:upper:]' '[:lower:]') IMAGE_NAME="${{ env.REGISTRY }}/${REPO}" if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then TAG="${{ github.event.inputs.image_tag }}" else TAG="latest" fi echo "name=${IMAGE_NAME}" >> $GITHUB_OUTPUT echo "tag=${TAG}" >> $GITHUB_OUTPUT echo "full=${IMAGE_NAME}:${TAG}" >> $GITHUB_OUTPUT echo "镜像: ${IMAGE_NAME}:${TAG}" # ── 3. 登录 Gitea 镜像仓库 ────────────────────────────────── - name: 登录 Gitea 镜像仓库 uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ gitea.actor }} password: ${{ secrets.REGISTRY_TOKEN }} # ── 4. 检测 Docker Compose 命令 ───────────────────────────── - name: 检测 Compose 命令 run: | if docker compose version &>/dev/null 2>&1; then echo "COMPOSE_CMD=docker compose" >> $GITHUB_ENV echo "使用: docker compose" elif command -v docker-compose &>/dev/null; then echo "COMPOSE_CMD=docker-compose" >> $GITHUB_ENV echo "使用: docker-compose" else echo "错误:未找到 docker compose 或 docker-compose,请先安装" exit 1 fi # ── 5. 拉取最新镜像 ────────────────────────────────────────── - name: 拉取镜像 run: | docker pull ${{ steps.image.outputs.full }} echo "镜像摘要: $(docker inspect ${{ steps.image.outputs.full }} --format='{{.Id}}')" # ── 6. 创建部署目录 ────────────────────────────────────────── - name: 准备部署目录 run: | mkdir -p ${{ env.DEPLOY_DIR }} # ── 7. 写入环境配置文件 ────────────────────────────────────── - name: 写入 .env 文件 run: | cat > ${{ env.DEPLOY_DIR }}/.env.demo << 'ENVEOF' # 自动生成 — 请勿手动编辑,由 CI/CD 工作流维护 DATABASE_URL=${{ env.DB_URL }} SECRET_KEY=${{ secrets.DEMO_SECRET_KEY }} APP_PORT=${{ env.APP_PORT }} FLASK_ENV=production ENVEOF chmod 600 ${{ env.DEPLOY_DIR }}/.env.demo echo "env 文件已写入" # ── 8. 复制 compose 文件到部署目录 ─────────────────────────── - name: 复制 Compose 文件 run: | cp docker-compose.external-db.yml ${{ env.DEPLOY_DIR }}/docker-compose.yml # ── 9. 更新 compose 文件中的镜像引用 ───────────────────────── - name: 更新镜像标签 run: | cd ${{ env.DEPLOY_DIR }} # 将 compose 文件中的镜像替换为当前目标镜像 sed -i "s|image:.*resource-library.*|image: ${{ steps.image.outputs.full }}|g" docker-compose.yml echo "已更新镜像引用为: ${{ steps.image.outputs.full }}" # ── 10. 部署 / 更新容器 ────────────────────────────────────── - name: 部署容器 run: | cd ${{ env.DEPLOY_DIR }} $COMPOSE_CMD \ --env-file .env.demo \ --project-name resource-library-demo \ up -d --remove-orphans --pull never echo "容器已启动" $COMPOSE_CMD --project-name resource-library-demo ps # ── 11. 等待服务就绪 ───────────────────────────────────────── - name: 健康检查 run: | max_wait=90 elapsed=0 url="http://127.0.0.1:${{ env.APP_PORT }}/auth/login" echo "等待服务就绪(最长 ${max_wait}s)…" while [ "$elapsed" -lt "$max_wait" ]; do if curl -fsSL --max-time 5 "$url" > /dev/null 2>&1; then echo "服务健康!(${elapsed}s)" exit 0 fi sleep 5 elapsed=$((elapsed + 5)) echo " 已等待 ${elapsed}s…" done echo "健康检查超时,查看日志:" $COMPOSE_CMD \ --project-name resource-library-demo \ logs --tail 50 2>/dev/null || true exit 1 # ── 12. 配置 Caddy 站点 ────────────────────────────────────── - name: 配置 Caddy run: | # 确保 Caddy sites 目录存在 mkdir -p ${{ env.CADDY_SITES_DIR }} # 写入站点配置(仅在文件内容变更时才重载) CADDY_CONF="${{ env.CADDY_SITES_DIR }}/${{ env.DOMAIN }}.caddy" NEW_CONF=$(cat docker/caddy/${{ env.DOMAIN }}.caddy) OLD_CONF=$(cat "$CADDY_CONF" 2>/dev/null || echo "") if [ "$NEW_CONF" != "$OLD_CONF" ]; then cp docker/caddy/${{ env.DOMAIN }}.caddy "$CADDY_CONF" echo "Caddy 配置已更新,重载…" # 优先使用 systemctl,退而使用 caddy reload if systemctl is-active --quiet caddy; then systemctl reload caddy || caddy reload --config /etc/caddy/Caddyfile elif caddy validate --config /etc/caddy/Caddyfile &>/dev/null; then caddy reload --config /etc/caddy/Caddyfile else echo "警告:无法重载 Caddy,请手动执行 systemctl reload caddy" fi else echo "Caddy 配置未变更,跳过重载" fi # ── 13. 输出部署摘要 ───────────────────────────────────────── - name: 输出部署摘要 run: | echo "## 🚀 Demo 环境部署成功" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "| 项目 | 值 |" >> $GITHUB_STEP_SUMMARY echo "|------|-----|" >> $GITHUB_STEP_SUMMARY echo "| 访问地址 | https://${{ env.DOMAIN }} |" >> $GITHUB_STEP_SUMMARY echo "| 镜像 | \`${{ steps.image.outputs.full }}\` |" >> $GITHUB_STEP_SUMMARY echo "| 触发方式 | ${{ github.event_name }} |" >> $GITHUB_STEP_SUMMARY echo "| 部署时间 | $(date -u '+%Y-%m-%d %H:%M:%S UTC') |" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "**Demo 地址:** https://${{ env.DOMAIN }}" >> $GITHUB_STEP_SUMMARY