Files
fangke/docs/superpowers/specs/2026-09-29-git-and-deploy-automation-design.md
T

6.7 KiB
Raw Blame History

Git 托管接入 + 一键部署方案 — 设计文档

日期:2026-09-29 分支:m1 状态:设计已获用户批准 远程仓:http://219.157.255.213:25311/xinyouhe/fangke.git(用户自建)

背景与目标

当前部署是"约定驱动的手工链":流程存在于对话上下文与一条记忆中,每次由 AI 手工执行 6-8 步(构建→scp 经跳板→解压→cp 到正确挂载点→docker 重建→探活→哈希核对)。已两次踩中"解压到错误目录"同类坑(/opt/cms-cj/dist 而非 admin/dist)。仓库无 git remote,无任何部署脚本。

目标:

  1. 项目托管到用户提供的自建 git 远程仓(版本化/备份)。
  2. 部署固化为仓库内一键脚本 deploy/deploy.sh——用户说"部署到线上"时 AI 执行该命令;日常任务完成后的标准部署走同一条命令。
  3. 消灭手工多步流程的错误类(错误目录/静默半部署/漏验证)。

Part 1:Git 远程仓初始化

  1. git remote add origin http://219.157.255.213:25311/xinyouhe/fangke.git
  2. 推送前卫生处理(实现时以当时 git status 实测为准,按以下原则逐文件决策):
    • 被代码引用的资产(如 LoginLeftView 引用的图片)→ 提交入库
    • 设计文档/设计稿(assets/design 等)→ 提交入库(团队知识)
    • 临时验证产物(根目录截图、测试页 html、.playwright-mcp/、.tmp-*)→ .gitignore
    • .gitignore 必须在本期初始提交中即包含 deploy/logs/——否则首次部署产生的日志污染工作区,下一次部署被自身的"工作区干净"前置门禁拦住
    • 敏感文件扫描:确认无 .env/私钥/明文密码入库(.env.example 除外);deploy/.env 不存在于本机(仅服务器有)
    • 首次 push 到 http 远程会弹凭证(Windows Git Credential Manager 交互一次后缓存)——一次性用户侧操作,AI 执行 push 前需用户确认凭证已配好
  3. 推送 m1 与 main 两分支(m1 为当前功能分支)。
  4. 后续提交按既有节奏正常 push(不做 push 即部署——见"明确不做")。

Part 2:deploy/deploy.sh 一键部署脚本

用法

bash deploy/deploy.sh <server|admin|sdk|all> [--skip-tests] [--allow-dirty]
  • 单目标只走对应子集(如仅改 admin 时 30 秒级完成)
  • all 按依赖序执行:server → admin → sdk(SDK 新事件依赖 server 白名单先上线)

脚本顶部配置(环境变量可覆盖)

JUMP_HOST=root@8.152.213.143      # 跳板
TARGET=root@47.254.176.10         # 生产服务器
SSH_KEY=~/.ssh/id_ed25519
REMOTE_DIR=/opt/cms-cj
SITE_URL=https://tracker.12418113.xyz

all 完整流程(固化现手工链 + 全部已知陷阱防御)

步骤 内容 防的坑
1 前置 git 工作区干净(--allow-dirty 逃生舱);跳板 SSH 可达 部署未提交状态
2 测试门禁 server: pytest tracker/tests(188 基线);sdk: vitest run + tsc;admin: vue-tsc(build 内含) 带病上线
3 构建 admin: NODE_OPTIONS=--max-old-space-size=6144 pnpm build;sdk: build + gzip 字节数记录 本机 OOM
4 打包与传输 tar 按真实路径打包(非暂存 dist/ 方案):admin/sdk 目标打 admin/dist sdk/dist;server 目标打 server/(排除 pycache/db.sqlite3/.pytest_cache;绝不打包 node_modules/.git/docs)→ scp 经跳板 → /tmp 根目录 tar 连 node_modules 一起传
5 服务器侧 在 $REMOTE_DIR 解压——tar 真实路径直接落位挂载点(admin/dist、sdk/dist 原地合并覆盖,无 cp、无暂存、无清理步骤——彻底消灭"解压到错误目录"路径);server: docker compose up -d --build web(Dockerfile 内自动 migrate) 错误挂载点目录(×2 历史)
6 验证 tracker-web 容器 Up;curl -f $SITE_URL → 200;admin:sha256(curl $SITE_URL 的 index.html) === sha256(本机 admin/dist/index.html);sdk:curl $SITE_URL/sdk/v1.js 含新 build_id 部署无效/缓存误判
7 记录 deploy/logs/deploy-<时间戳>.log:git 哈希/部署目标/版本标识/验证结果/耗时(logs 目录 gitignore,已在 Part 1 落地) 无部署历史可追溯

单目标语义(每个目标都完整走:前置检查 + 自己的测试 + 自己的验证)

目标 子集
server pytest → tar server/ → 解压 $REMOTE_DIR → docker compose up -d --build web → 容器 Up + curl -f $SITE_URL 200(无 dist 传输)
admin vue-tsc/build → tar admin/dist → 解压落位 → index.html sha256 线上比对(无 docker、无 pytest)
sdk vitest + tsc → build → tar sdk/dist → 解压落位 → 线上 v1.js 含 build_id(无 docker)

失败语义

set -euo pipefail;任一步失败立即中止并输出失败步骤与原因——杜绝静默半部署。单目标失败时 all 序列停止(不做部分继续)。

平台约束

  • 脚本为 bash(开发机 Git Bash 执行;服务器侧命令经 ssh 执行,无 Windows 依赖)
  • 服务器现有 1Panel/OpenResty 等其他项目严格不触碰(既有红线)

Part 3:使用约定(新建 CLAUDE.md + 更新会话背景与记忆)

现状核实:仓库与用户级均无 CLAUDE.md——部署约定目前活在用户每会话粘贴的"项目背景"块与 AI 自动记忆中。落地:

  1. 新建仓库 CLAUDE.md(部署约定段):用户说"部署到线上" → AI 执行 bash deploy/deploy.sh all(或按改动范围选单目标)并汇报验证结果与版本标识;任务完成后的标准部署 = 同一条命令;部署日志位于 deploy/logs/。
  2. 用户侧:其后会话粘贴的"项目背景"块中"部署策略"段改为指向本脚本(由用户自行更新其模板,spec 不代写)。
  3. AI 自动记忆:更新既有"前端部署目标目录"记忆条目——手工链描述作废,指向 deploy/deploy.sh(历史陷阱教训保留)。

明确不做(本期)

  • push 触发自动部署(webhook/CI):git 服务器无暴露接收端口必要;未来升级时 CI 可直接调用本脚本(接口即命令行)
  • 服务器侧构建:构建留在开发机,服务器保持纯 Docker 运行态
  • 自动回滚机制:出问题走"再改再发"(单管理员低频部署,YAGNI;部署日志保留上一版哈希可供人工对照)

验证计划

  1. 脚本干跑:单目标 admin 部署一次(近期 admin 已是最新,重部署无损)验证全流程
  2. all 全量部署一次(当前三端均为最新代码,等价幂等重部署)验证顺序与哈希核对
  3. 故意注入失败(如断开跳板模拟)验证"失败即停"语义
  4. git push 后远程仓确认两分支与提交历史完整