From 1d87c3f94c783be0cd6c02104314666b044d6a99 Mon Sep 17 00:00:00 2001 From: lunanting Date: Sat, 12 Sep 2026 17:38:11 +0800 Subject: [PATCH] docs: define safe production deployment workflow --- AGENTS.md | 3 +- README.md | 22 +----- 仓库提交与合并规范.md | 2 +- 服务器部署规范.md | 170 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 177 insertions(+), 20 deletions(-) create mode 100644 服务器部署规范.md diff --git a/AGENTS.md b/AGENTS.md index e7ae4ec..5dddc37 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,6 +1,6 @@ # AI 开发协作入口 -开始修改、提交、推送或合并前,必须阅读并遵守 [仓库提交与合并规范](./仓库提交与合并规范.md)。该文件是本仓库多人协作的具体流程,不可仅凭本摘要跳过全文。 +开始修改、提交、推送或合并前,必须阅读并遵守 [仓库提交与合并规范](./仓库提交与合并规范.md)。准备更新生产服务器时,还必须阅读并遵守 [服务器部署规范](./服务器部署规范.md)。这些文件是本仓库多人协作的具体流程,不可仅凭本摘要跳过全文。 - 每个任务使用独立目录和唯一任务分支,默认前缀 `codex/`;不得共享目录并行编辑。 - 保留他人的未提交修改和已有提交,仅暂存本任务内容;不得清理或覆盖未知工作。 @@ -8,6 +8,7 @@ - 不改写已发布历史,不批量选择冲突的某一方;不明确的行为取舍需要负责人确认。 - 所有变更通过 PR 进入 `main`,不得直接推送主分支;独立审核与最新内容的验证通过后由维护者合并。 - 不提交凭据、私钥、环境秘密或真实业务数据;不把合并授权当成生产部署授权。 +- 开发机器使用中央系统数据库和公网 TLS Redis,无需安装本地数据库;生产部署必须先将代码通过 PR 合入 `main`,并在 `/opt/cangku/deploy.lock` 锁内从服务器 Git 工作区快进拉取和验证。 - 最终交付报告实际分支、提交 SHA、验证结果及 PR 状态,不能将“已推送”报告为“已合并”。 这些文档不代表服务端保护已启用。任何 AI 工具无法自动读取规则时,由使用它的开发者主动提供全文。 diff --git a/README.md b/README.md index 1a66e50..d09b4f5 100644 --- a/README.md +++ b/README.md @@ -8,29 +8,15 @@ ## 本地启动 -需要 Node.js 22、MySQL 系统库,以及业务实际使用的数据库和外部服务。仓库不包含生产环境配置、密钥、业务数据或原仓库历史。 - -团队成员如需免部署本地系统数据库,可使用管理员发放的部署 Key,通过加密隧道连接中央系统数据库。完整步骤及安全边界见 [远程系统数据库部署](./remote-access/README.md)。 +需要 Node.js 22。团队开发机器无需安装或初始化 MySQL、Redis 及第三方数据库;使用管理员发放的部署 Key,一次注册后自动连接中央系统数据库、公网 TLS Redis 和第三方数据库。完整步骤及安全边界见 [远程系统数据库部署](./remote-access/README.md)。 ```bash npm --prefix backend ci npm --prefix frontend ci -cp backend/.env.example backend/.env +npm run remote:dev ``` -填写 `backend/.env` 中的连接信息与密钥,分别在两个终端启动: - -```bash -cd backend -npm run dev -``` - -```bash -cd frontend -npm run dev -``` - -前端默认访问 http://localhost:5173,API 默认代理到 http://localhost:4001,可用 `VITE_API_PROXY_TARGET` 覆盖。首次启动按照安装与设置向导初始化系统。 +首次启动时填写配置请求地址和部署 Key,系统自动写入被 Git 忽略的本机配置并重启后端。保存一次性显示的管理员账号和密码后直接登录,不需要在本地创建数据库或继续执行设置向导。前端默认访问 http://localhost:5173,API 默认代理到本地后端。 ## 验证与构建 @@ -45,6 +31,6 @@ npm --prefix frontend run check ## 参与修改 -开始开发前请阅读 [仓库提交与合并规范(多用户与 AI 开发必读)](./仓库提交与合并规范.md)。AI 工具的规则入口为 [AGENTS.md](./AGENTS.md)。每个任务使用独立目录和分支,通过 PR、同步验证和独立审核合并,禁止强推或覆盖其他开发者的工作。 +开始开发前请阅读 [仓库提交与合并规范(多用户与 AI 开发必读)](./仓库提交与合并规范.md)。准备更新生产服务器时必须继续阅读 [服务器部署规范(开发者与 AI 必读)](./服务器部署规范.md)。AI 工具的规则入口为 [AGENTS.md](./AGENTS.md)。每个任务使用独立目录和分支,通过 PR、同步验证和独立审核合并,禁止强推或覆盖其他开发者的工作。 所有用户均可 Fork 仓库、创建分支并提交 Pull Request。直接推送本仓库需要仓库所有者授予协作者写权限。提交时请说明修改和验证结果,勿提交 `.env`、密码、令牌、数据库备份或真实业务数据。 diff --git a/仓库提交与合并规范.md b/仓库提交与合并规范.md index 3e55ae8..9ef69c5 100644 --- a/仓库提交与合并规范.md +++ b/仓库提交与合并规范.md @@ -1,6 +1,6 @@ # 仓库提交与合并规范(多用户与 AI 开发必读) -本规范适用于所有开发者及其使用的 AI 编程工具。目标是保留他人的提交和业务行为,让每次修改都可审核、可追溯、可回退。开始修改前必须阅读本文件与根目录 `AGENTS.md`。 +本规范适用于所有开发者及其使用的 AI 编程工具。目标是保留他人的提交和业务行为,让每次修改都可审核、可追溯、可回退。开始修改前必须阅读本文件与根目录 `AGENTS.md`;需要更新生产服务器时还必须阅读 `服务器部署规范.md`。 公开仓库允许所有人 Fork、修改并提交 Pull Request(PR);直接写入本仓库仍需要协作者权限。每名开发者使用自己的账号和凭据,禁止多人共用管理员账号或把账号、密码、令牌写入代码、文档、提交信息及 PR。 diff --git a/服务器部署规范.md b/服务器部署规范.md new file mode 100644 index 0000000..cd29c08 --- /dev/null +++ b/服务器部署规范.md @@ -0,0 +1,170 @@ +# 服务器部署规范(开发者与 AI 必读) + +本规范适用于所有开发者及其使用的 AI 编程工具。任何人准备更新生产服务器前,都必须先阅读根目录 `AGENTS.md`、`仓库提交与合并规范.md` 和本文件。目标是让仓库成为唯一代码来源,防止本地修改、并行推送和同时部署互相覆盖。 + +## 1. 固定环境与边界 + +- 生产服务器项目根目录:`/opt/cangku` +- 服务器代码目录:`/opt/cangku/app` +- 生产编排文件:`/opt/cangku/app/deploy/production/docker-compose.yml` +- 对外站点及注册接口:`https://ck.yydd.ai:8443` +- 公网 TLS Redis:`ck.yydd.ai:6380` +- 服务器 SSH 密钥目录位于开发者桌面:`~/Desktop/cangku-156.225.27.52-ssh-key` +- 当前私钥文件:`~/Desktop/cangku-156.225.27.52-ssh-key/cangku-prod-root-ed25519` +- SSH 目标:`root@156.225.27.52`,端口 `50232` + +私钥只能保存在获准访问服务器的开发机器上,权限必须为 `0600`。禁止把私钥、密码、部署 Key、`.env`、`.remote-access` 或服务器 `/opt/cangku/shared` 中的内容复制到仓库、提交、PR、聊天记录或部署日志。没有上述密钥目录的开发者无权部署,应把已经合并的提交 SHA 交给有权限的维护者。 + +## 2. 开发机器无需安装数据库或 Redis + +本项目的团队开发环境使用中央托管服务。开发者和 AI 不得为了本地开发另建或初始化 MySQL、Redis,也不要把生产连接密码写入代码: + +1. 第一次运行 `npm run remote:dev`,在弹窗中填写维护者提供的配置请求地址和部署 Key。 +2. 注册服务自动配置中央系统数据库、TLS Redis、第三方数据库连接和独立管理员。 +3. 系统数据库通过受限 SSH 隧道连接;Redis 使用 `ck.yydd.ai:6380` TLS 直连。 +4. 获取到的凭据只写入 Git 已忽略的 `backend/.env` 和 `.remote-access/`。部署 Key 不保存。 +5. 管理员账号和随机密码只显示一次,开发者必须保存在自己的密码管理器中。 +6. 注册完成后直接登录系统,不需要执行数据库初始化,也不再进入业务数据库、Redis、登录方式和对象存储配置向导。 + +常用启动命令: + +```bash +npm --prefix backend ci +npm --prefix frontend ci +npm run remote:dev +``` + +不要运行会创建本地业务库、覆盖中央配置或向生产库导入初始化 SQL 的命令。确实需要隔离数据库做迁移测试时,必须使用单独的测试环境,并在 PR 中说明,不能连接生产系统库执行测试。 + +## 3. 部署前先把代码安全合入主仓库 + +服务器只能部署已经进入远端 `origin/main` 的提交。不得把开发机器上的未提交文件直接复制到服务器,也不得让服务器拉取尚未审核的个人分支。 + +开发者或 AI 在本地依次检查: + +```bash +pwd +git remote -v +git branch --show-current +git status --short +git fetch origin --prune +git log --left-right --oneline HEAD...origin/main +git diff --check origin/main...HEAD +git diff --stat origin/main...HEAD +``` + +必须确认: + +1. 当前目录确实是本项目的独立工作目录,当前分支不是 `main`,并且没有混入其他任务或其他开发者的未提交文件。 +2. 本地已有修改先按 `仓库提交与合并规范.md` 提交到唯一任务分支,再同步最新 `origin/main`。 +3. 如果主仓库已有更新,先合并到任务分支,逐处处理冲突,重新检查最终差异并运行受影响测试。 +4. 禁止使用强制推送、`git reset --hard`、批量选择 `ours/theirs` 或复制旧目录覆盖新代码来解决冲突。 +5. 将任务分支推送到主仓库,创建 PR,经过审核和验证后合并到 `main`。这里的“先推送主仓库”指推送任务分支并通过 PR 合并,禁止直接推送 `main`。 +6. PR 合并后再次执行 `git fetch origin`,记录准备部署的 `origin/main` 完整 SHA。部署 SHA 必须与审核通过的最终提交一致。 + +有冲突时,AI 必须阅读共同基线和双方改动,保留双方仍然有效的行为。无法确定业务取舍时,停止该冲突的处理并请负责人决定。冲突解决后的代码必须重新测试、重新审核,不能沿用冲突前的结果。 + +## 4. 检查服务器是否正在更新 + +任何部署都必须使用 `/opt/cangku/deploy.lock` 文件锁。先检查当前服务和更新进程: + +```bash +SSH_KEY="$HOME/Desktop/cangku-156.225.27.52-ssh-key/cangku-prod-root-ed25519" +chmod 600 "$SSH_KEY" +ssh -i "$SSH_KEY" -p 50232 -o BatchMode=yes root@156.225.27.52 ' + cd /opt/cangku/app + docker compose -f deploy/production/docker-compose.yml ps + ps -ef | grep -E "[g]it (pull|fetch|merge)|[d]ocker compose|[n]pm .*build" || true + flock -n /opt/cangku/deploy.lock true +' +``` + +最后一条命令返回非零时,表示另一名开发者或 AI 正在部署。必须等待对方完成并确认服务恢复,不能绕过、删除或替换锁文件,也不能同时执行 Git、构建、容器重建或数据库操作。 + +取得锁之前发现容器不健康、Git 操作未结束、构建仍在运行或存在 `.git/index.lock` 时,不得继续更新。先确认是否有正在运行的部署;需要处理故障时保留现场和日志并联系当前部署者。禁止在不清楚来源时终止进程或删除 Git 锁。 + +## 5. 在同一个服务器锁内拉取和部署 + +确认目标 SHA 已进入 `origin/main` 后,通过一次 SSH 会话取得锁并保持到验证结束。将下面的 `<审核通过的完整 SHA>` 替换为实际值: + +```bash +SSH_KEY="$HOME/Desktop/cangku-156.225.27.52-ssh-key/cangku-prod-root-ed25519" +DEPLOY_SHA="<审核通过的完整 SHA>" + +ssh -i "$SSH_KEY" -p 50232 -o BatchMode=yes root@156.225.27.52 \ + "DEPLOY_SHA='$DEPLOY_SHA' flock -w 1800 /opt/cangku/deploy.lock bash -s" <<'REMOTE' +set -euo pipefail +cd /opt/cangku/app + +test -d .git || { echo '服务器代码目录不是 Git 工作区,停止部署'; exit 1; } +test -z "$(git status --porcelain)" || { echo '服务器存在未提交修改,停止部署'; exit 1; } +docker compose -f deploy/production/docker-compose.yml ps +curl -fsS http://127.0.0.1:4090/health >/dev/null || { + echo '更新前服务不健康,停止部署并先查明当前状态' + exit 1 +} + +git fetch origin --prune +test "$(git rev-parse "origin/main^{commit}")" = "$DEPLOY_SHA" || { + echo 'origin/main 与待部署 SHA 不一致,停止部署' + exit 1 +} + +git switch main +git pull --ff-only origin main +test "$(git rev-parse HEAD)" = "$DEPLOY_SHA" || { + echo '服务器 HEAD 与待部署 SHA 不一致,停止部署' + exit 1 +} + +npm --prefix frontend ci +npm --prefix frontend run build +if grep -q '^SOURCE_GIT_SHA=' /opt/cangku/shared/backend.env; then + sed -i "s/^SOURCE_GIT_SHA=.*/SOURCE_GIT_SHA=$DEPLOY_SHA/" /opt/cangku/shared/backend.env +else + printf 'SOURCE_GIT_SHA=%s\n' "$DEPLOY_SHA" >> /opt/cangku/shared/backend.env +fi +if grep -q '^SOURCE_GIT_REF=' /opt/cangku/shared/backend.env; then + sed -i 's/^SOURCE_GIT_REF=.*/SOURCE_GIT_REF=main/' /opt/cangku/shared/backend.env +else + printf 'SOURCE_GIT_REF=main\n' >> /opt/cangku/shared/backend.env +fi +docker compose -f deploy/production/docker-compose.yml config >/dev/null +docker compose -f deploy/production/docker-compose.yml up -d --build + +for attempt in $(seq 1 60); do + if curl -fsS http://127.0.0.1:4090/health >/dev/null; then + break + fi + if [ "$attempt" -eq 60 ]; then + echo '后端健康检查超时' + exit 1 + fi + sleep 2 +done + +curl -fsS https://ck.yydd.ai:8443/ >/dev/null +curl -fsS https://ck.yydd.ai:8443/health +docker compose -f deploy/production/docker-compose.yml ps +git status --short +git rev-parse HEAD +REMOTE +``` + +`flock -w 1800` 会等待最多 30 分钟。等待期间不得另开会话绕过锁部署。超时后先联系当前部署者并检查服务状态,不要直接杀进程。 + +服务器工作区必须保持干净并只做快进更新。`git pull --ff-only` 失败、服务器出现未提交文件或目标 SHA 变化时,应停止部署,在本地仓库通过新提交和 PR 解决;禁止在服务器上编辑业务代码、处理合并冲突、强制切换提交或重置历史。 + +## 6. 部署完成标准 + +部署者和 AI 只有在以下项目全部确认后才能报告完成: + +- 服务器 `HEAD` 等于预先记录且已合并的 `origin/main` SHA。 +- `git status --short` 没有输出。 +- MySQL、Redis、Redis TLS 网关、后端和注册服务容器处于正常运行状态。 +- `https://ck.yydd.ai:8443/` 返回成功,`/health` 显示系统数据库、第三方数据库和 Redis 连接成功。 +- `ck.yydd.ai:6380` 的 TLS 证书校验通过;原始 Redis 端口没有对公网开放。 +- 新版本的关键用户路径已验证,没有覆盖其他开发者已经上线的功能。 +- 最终报告包含任务分支、PR、部署 SHA、实际检查结果和未解决问题。 + +如果部署失败,不得把服务器强制重置到某个旧提交。先保留日志和当前 SHA,在本地创建修复或回退 PR;合并后再按同一流程部署。涉及不可逆数据库迁移时,部署前必须另行制定备份、兼容与回退方案,本文件不能替代迁移评审。