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

85 lines
6.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: frontend-cicd-deploy
overview: 参考 zt-closet-backend 的 Gitea Actions CI/CD,为前端管理后台编写 deploy 工作流:构建 dist 静态资源并通过 SSH 同步到生产机 nginx 目录 /www/zt-closet-manage.lzcwy.cn。
todos:
- id: create-deploy-workflow
content: 新建 .gitea/workflows/deploy.yaml:checkout→setup-node→npm ci→npm run build→SSH+rsync 部署 dist 到 /www/zt-closet-manage.lzcwy.cn,含备份与回滚
status: completed
- id: verify-pipeline
content: 校验工作流 YAML 语法与触发配置,确认凭据引用与后端一致
status: completed
dependencies:
- 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 + 蓝绿切换」。
```mermaid
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。
```