Files
zt-closet-manage-frontend/.codebuddy/plans/frontend-cicd-deploy_4b05aca9.md
T
2026-09-12 10:59:48 +08:00

6.2 KiB
Raw Blame History

name, overview, todos
name overview todos
frontend-cicd-deploy 参考 zt-closet-backend 的 Gitea Actions CI/CD,为前端管理后台编写 deploy 工作流:构建 dist 静态资源并通过 SSH 同步到生产机 nginx 目录 /www/zt-closet-manage.lzcwy.cn。
id content status
create-deploy-workflow 新建 .gitea/workflows/deploy.yaml:checkout→setup-node→npm ci→npm run build→SSH+rsync 部署 dist 到 /www/zt-closet-manage.lzcwy.cn,含备份与回滚 completed
id content status dependencies
verify-pipeline 校验工作流 YAML 语法与触发配置,确认凭据引用与后端一致 completed
create-deploy-workflow

产品概述

为前端项目 zt-closet-manage-frontend 建立与后端一致的 CI/CD 流水线(Gitea Actions),实现「打标签自动构建 → 部署到生产机 nginx 静态目录」的全流程自动化。

核心功能

  • 推送 v* 标签或手动触发(workflow_dispatch)时,自动安装依赖、类型检查并构建出 dist 静态产物。
  • 通过 SSH 将构建产物安全传输到生产机,部署到 nginx root 目录 /www/zt-closet-manage.lzcwy.cn。
  • 采用蓝绿目录原子切换部署,线上目录任何时刻都完整,失败秒级回滚,保证发布零中断。
  • 复用后端已有的 Gitea runner 与 SSH 部署凭据,无需重复配置密钥。

约束说明

  • 前端为纯静态资源,不使用 Docker,构建产物直接放到 nginx 静态目录。
  • 前端 API baseURL 为 /api/v1,由生产 nginx 反代到 127.0.0.1:8081,构建无需改动 baseURL 与 vite 配置。
  • 产物结构(index.html + /assets/ 带 hash 资源)与现有 nginx try_files、/assets/ 长缓存配置匹配。

技术栈

  • CI/CD:Gitea Actions(与后端 zt-closet-backend 一致),runner 环境 ubuntu-latest。
  • 构建:Node.js(匹配 package.json engines ^22.18.0 || >=24.12.0,取 Node 22)+ npm。
  • 部署:SSH + rsync 传输到临时目录 + 蓝绿目录原子切换(mv)上线。

实现方案

参考后端 .gitea/workflows/deploy.yaml 的整体风格(name / on push tags v* + workflow_dispatch / concurrency / env / SSH heredoc 远端执行 / 健康检查与回滚),针对前端「纯静态资源」特点做如下调整:

  • 去掉 Docker:前端无需镜像构建,改为 actions/checkout → actions/setup-node(Node 22,启用 npm 缓存)→ npm ci → npm run build(内含 type-check + vite build)。
  • 产物传输用 rsync:相比 scp,rsync 支持增量传输与 --delete,可清理临时目录中上一版本遗留的旧 hash 资源,传输更快、更干净。
  • 蓝绿目录原子切换(核心):新产物先 rsync 到临时目录 /www/.zt-closet-deploy-tmp(绝不动线上目录);校验临时目录 index.html 存在后,用 mv 原子切换:将当前线上目录改名为备份目录,再把临时目录改名为线上目录。任何一步失败,线上目录保持原样不受影响;切换后校验失败则把备份目录 mv 回去,秒级回滚。
  • 凭据复用:直接沿用后端的 secret ZT_CLOSET_PROD_SSH_KEY 与 vars ZT_CLOSET_PROD_SSH_HOST、ZT_CLOSET_PROD_SSH_USER,生产机同一台,避免重复配置。

关键决策与权衡

  • 蓝绿目录原子切换 而非 直接 rsync --delete 覆盖:直接对线上目录 --delete 若传输中断,会出现「旧文件已删、新文件未传完」的半残状态导致站点 404;蓝绿方式线上目录始终完整,切换是一次 mv,零中断、回滚快。权衡是需 nginx 进程对 /www 目录有 rename 权限(root 运行 nginx 通常满足),且临时目录与线上目录需在同一分区才能 mv。
  • rsync 而非 scp:增量 + --delete(作用于临时目录)解决 Vite 带 hash 资源累积问题,传输量小、目录干净;需生产机预装 rsync(Linux 默认自带)。
  • 不改动 vite 配置:默认 base='/' 与 nginx root 部署完全契合,/assets/ 长缓存与 SPA try_files 均已就绪,零改动最安全。
  • 健康检查用文件校验:静态站点无 /ping 接口,改用「线上目录 index.html 是否存在」作为成功判据,简单可靠。

实现要点

  • 性能:setup-node 开启 npm 缓存(cache: true),源码未变动时 npm ci 复用缓存,加快重建;rsync 增量避免全量传输。
  • 可靠性:远端脚本 set -e,rsync 到临时目录 → 校验 index.html → mv 原子切换 → 切换后校验;失败则把备份目录 mv 还原,保证发布原子性与零中断;concurrency.group: deploy + cancel-in-progress: false 防止并发部署互相打断。
  • 安全:SSH 私钥仅从 secret 注入,写入 ~/.ssh/deploy_key(600 权限),StrictHostKeyChecking=no 仅用于自动化;不引入明文密钥;不改动业务代码与请求层。
  • 爆炸半径控制:仅新增 .gitea/workflows/deploy.yaml 一个文件,不动现有源码、vite 配置与 nginx 配置;标签触发不影响日常 push。

架构设计

沿用后端现有分层部署链路,仅将「Docker 镜像」环节替换为「静态产物 rsync + 蓝绿切换」。

flowchart LR
  P[push tag v* / workflow_dispatch] --> CI[Gitea Actions Runner]
  CI --> B[setup-node + npm ci + npm run build]
  B --> D[dist 静态产物]
  D --> R[rsync over SSH 到临时目录]
  R --> V{校验 index.html}
  V -->|通过| M[mv 原子切换上线]
  V -->|失败| X1[保留线上旧版本]
  M --> N[生产机 /www/zt-closet-manage.lzcwy.cn]
  N --> X[nginx 对外提供 SPA + 反代 /api 到 127.0.0.1:8081]

目录结构

/root/zt-closet-manage-frontend/
└── .gitea/
    └── workflows/
        └── deploy.yaml   # [NEW] 前端构建部署流水线。触发:push tag v* 与 workflow_dispatch。步骤:checkout → setup-node(Node22, 开 npm 缓存) → npm ci → npm run build(type-check + vite build) → SSH 密钥 + rsync 将 dist/ 同步到临时目录 /www/.zt-closet-deploy-tmp → 校验 index.html 存在 → mv 蓝绿原子切换上线(线上目录改名为备份、临时目录改名为线上),切换后校验失败则 mv 备份还原。复用后端 secret ZT_CLOSET_PROD_SSH_KEY 与 vars ZT_CLOSET_PROD_SSH_HOST/ZT_CLOSET_PROD_SSH_USER。