310 lines
22 KiB
Markdown
310 lines
22 KiB
Markdown
# 三机代码晋升 Runbook (Phase4)
|
||
|
||
> Mac(dev 源头) → NAS(test 镜像) → VPS(prod 生产) 单向代码晋升。
|
||
> 本文档只管 **代码**,不管数据(数据铁律见末节)。
|
||
|
||
## 1. 角色与流向
|
||
|
||
| 机器 | 角色 | 代码路径 | 工具 |
|
||
|------|------|----------|------|
|
||
| **Mac** | dev 源头(改代码) | `~/.openclaw/sanguo_projects/sanguo_vnpy_v2` | rsync/scp |
|
||
| **NAS** | test 镜像(只读副本) | `/volume1/homes/admin/.sanguo_projects/sanguo_vnpy_v2`(容器挂载源;⚠️`/volume1/stock/sanguo_vnpy_v2`=数据备份区非代码位) | rsync(LAN 快) |
|
||
| **VPS** | prod 生产(实跑) | `C:\sanguo_vnpy_v2` | scp(无 rsync) |
|
||
|
||
```
|
||
改代码 rsync (Step1)
|
||
Mac dev ──────────────────► NAS test (镜像)
|
||
│
|
||
└─────── scp (Step2) ─────► VPS prod (生产)
|
||
```
|
||
|
||
> 两路都从 Mac 出发,NAS 是只读镜像(供回归),VPS 是生产实跑。**NAS 不是中转**,VPS 代码不经 NAS。
|
||
|
||
晋升是 **单向**(Mac → 外),NAS/VPS 永不回推 Mac。NAS 是镜像备份,VPS 是生产实跑。
|
||
|
||
## 2. 前置条件
|
||
|
||
- **Mac SSH key 连 VPS**: `~/.ssh/config` 已配 `Host 49.232.102.198` + key `~/.ssh/id_ed25519`。
|
||
- 验证: `ssh 49.232.102.198 'echo VPS_OK'` 应直接通(免密)。
|
||
- **严禁 `ssh vps`**: 本机 config 无此别名,会被代理 fake-ip 劫持到 198.18.1.254 报 Connection reset。
|
||
- **NAS SSH**: `ssh sanguo-nas`(LAN 免密)。
|
||
- **工作目录**: 在 Mac 代码根 `~/.openclaw/sanguo_projects/sanguo_vnpy_v2` 下执行。
|
||
|
||
## 3. 使用
|
||
|
||
### 3.1 全量晋升(所有模块 + 根文件)
|
||
|
||
改完一批代码后:
|
||
|
||
```bash
|
||
cd ~/.openclaw/sanguo_projects/sanguo_vnpy_v2
|
||
bash scripts/nas_sync/promote.sh
|
||
```
|
||
|
||
推送内容:
|
||
- 模块目录(14 个): `sanguo_api sanguo_backtest sanguo_common sanguo_data sanguo_factor sanguo_live sanguo_orchestrator sanguo_portfolio sanguo_qmt_bridge sanguo_research sanguo_trader scripts config tests`(2026-10-01 起 `sanguo_web` 移出——legacy 平行后端归档至 `_retired/sanguo_web/`,退出 promote 白名单与 docker COPY 面)
|
||
- 根文件(4 个): `pyproject.toml pytest.ini requirements-lock.txt run_web.py`
|
||
|
||
### 3.2 单模块快速补推
|
||
|
||
只想推刚改的一个模块(如 `sanguo_portfolio`):
|
||
|
||
```bash
|
||
bash scripts/nas_sync/promote.sh --module sanguo_portfolio
|
||
```
|
||
|
||
`--module` 模式: NAS+VPS 只推该模块,**不推根文件**(根文件改动需走全量)。
|
||
|
||
可用模块名见上方列表。脚本会在本地缺失时报错退出。
|
||
|
||
## 4. Reload 机制(重要)
|
||
|
||
VPS 进程分两类:**常驻**(sanguo-api 生产控制台 / live-supervisor / shadow-desk,开机自启)+ **一次性任务**(回测 runner_backtest + 采集 schtask)。⚠️ 2026-10-01 勘误:旧文「无常驻 web 服务」过时(见 §12 勘误注);旧任务名 `sanguo-bs-eod` 已于 2026-08-20 并入 `sanguo-bs-daily`。
|
||
|
||
| 场景 | reload 动作 |
|
||
|------|-------------|
|
||
| 采集 schtask(定时) | **无需操作**。下次 schtask 触发时自动加载新代码 |
|
||
| 回测任务 | **无需操作**。下次启动回测时自动加载新代码 |
|
||
| sanguo-api(常驻控制台) | `schtasks /end sanguo-api && schtasks /run sanguo-api`(改 sanguo_api/orchestrator/前端后必须) |
|
||
| 立即让采集生效 | `schtasks /end sanguo-bs-daily && schtasks /run sanguo-bs-daily` |
|
||
|
||
> 一句话: 代码部署后,**下次启动自动生效**;常驻进程(api/supervisor)须显式重启。
|
||
|
||
立即生效命令(在 VPS 上执行,通过 ssh):
|
||
|
||
```bash
|
||
ssh 49.232.102.198 'schtasks /end sanguo-bs-daily && schtasks /run sanguo-bs-daily'
|
||
```
|
||
|
||
## 5. 落盘验证
|
||
|
||
脚本 Step3 自动验证 VPS 第一个推送模块的 `.py` 文件 mtime。手动深度核查:
|
||
|
||
```bash
|
||
# VPS: 查某模块文件列表+mtime
|
||
ssh 49.232.102.198 'powershell -NoProfile -Command "Get-ChildItem C:\sanguo_vnpy_v2\sanguo_portfolio -Filter *.py | Select Name,Length,LastWriteTime | Format-Table -Auto"'
|
||
|
||
# VPS: 查某文件是否含新代码标记
|
||
ssh 49.232.102.198 'powershell -NoProfile -Command "Select-String -Path C:\sanguo_vnpy_v2\sanguo_common\__init__.py -Pattern SOME_TOKEN"'
|
||
|
||
# NAS: 查某文件
|
||
ssh sanguo-nas "ls -la /volume1/homes/admin/.sanguo_projects/sanguo_vnpy_v2/sanguo_portfolio/"
|
||
```
|
||
|
||
> 教训(memory: commit≠部署VPS): 改完代码必须晋升,否则 VPS 跑旧代码。用 `Select-String`/`grep` 验证新代码落盘。
|
||
|
||
## 6. Rollback
|
||
|
||
代码部署出问题需要回退:
|
||
|
||
```bash
|
||
# 1. Mac 本地回退到上一个好版本
|
||
cd ~/.openclaw/sanguo_projects/sanguo_vnpy_v2
|
||
git log --oneline -5 # 找到好版本
|
||
git checkout <good_commit> -- sanguo_portfolio/ # 或整个目录
|
||
|
||
# 2. 重新晋升回退后的代码
|
||
bash scripts/nas_sync/promote.sh --module sanguo_portfolio
|
||
```
|
||
|
||
> NAS/VPS 没有 git,rollback = Mac git 回退 + 重新 promote。所以 **Mac git 历史是唯一真相源**。
|
||
|
||
## 7. 数据铁律(边界)
|
||
|
||
**只推代码,严禁推 `data/`。**
|
||
|
||
| 允许推 | 严禁推 |
|
||
|--------|--------|
|
||
| `sanguo_*/` 代码模块 | `data/`(42GB 数据库 + parquet) |
|
||
| `scripts/`、`config/`、`tests/` | `logs/`、`vnpy_v4.4.0/`(VPS 已有) |
|
||
| 根配置文件 | `vnpy_qmt_v0.3.3/`、`docker/`、`.git/` |
|
||
| | `__pycache__/`、`*.pyc`、`htmlcov/`、`*.log` |
|
||
|
||
脚本已通过两层防护:
|
||
1. **rsync(NAS)**: `--exclude='/data' --exclude='__pycache__' ...` 白名单式排除。
|
||
2. **scp(VPS)**: 按模块逐个推,天然隔离 `data/`(不在模块列表里)。
|
||
|
||
## 8. 常见坑
|
||
|
||
| 坑 | 解法 |
|
||
|----|------|
|
||
| `ssh vps` 连不上(fake-ip) | 用 `ssh 49.232.102.198`,config 无 vps 别名 |
|
||
| scp 路径反斜杠报错 | VPS 目标用**正斜杠**: `49.232.102.198:"C:/sanguo_vnpy_v2/"` |
|
||
| PowerShell 中文乱码 | 用 `-NoProfile`,查文件用 `Get-ChildItem`/`Select-String`,避免中文路径 |
|
||
| scp 某模块卡住 >60s | Ctrl-C 记录,先验证已通的模块,单独重推失败的 |
|
||
| 部署后 VPS 行为没变 | 代码是下次启动才加载;采集 schtask 需 `/end && /run` 立即生效 |
|
||
| macOS bash 3.2 空数组报错 | 脚本已用 `${#arr[@]} -gt 0` 守卫;如改脚本注意 `set -u` + 空数组 |
|
||
|
||
## 9. NAS 容器 --init 铁律(防后台回测被清)
|
||
|
||
NAS test 容器 `sanguo_vnpy_v2` **必须 `docker run --init` 创建**(tini 作 PID1)。
|
||
|
||
**根因**:无 `--init` 时 PID1=uvicorn(非 init/tini)。`docker exec ... nohup python &` 启动的后台回测进程,在 docker exec 会话(bash)结束后成孤儿,docker 清理该后台进程组 → **长任务读盘中途被清**(02 small_cap day0 选股 5128 只 fundamentals 需 ~4min 超会话存活被清,log 停 day0 无 error;03 纯量价 day0 秒级没被清)。一度误判 OOM,被 `dmesg` 空 + 前台跑过 day0 推翻。
|
||
|
||
**何时会丢 --init(复发风险)**:
|
||
- ci-cd `nas-deploy` 只 `docker restart`(**不重建,--init 保留** ✅)。
|
||
- ⚠️ **任何手动 `docker stop && rm && run` 必须带 `--init`** —— 这是唯一丢 --init 的路径。
|
||
|
||
**手动重建命令要点**:
|
||
```bash
|
||
D=/var/packages/Docker/target/usr/bin/docker # Synology docker 不在 PATH
|
||
# 1. 先 commit 当前容器(备份 + 含运行时手动装的 pip 包如 jwt)
|
||
$D commit sanguo_vnpy_v2 sanguo_vnpy_v2:pre-init-rebuild
|
||
# 2. stop && rm
|
||
$D stop sanguo_vnpy_v2 && $D rm sanguo_vnpy_v2
|
||
# 3. run --init 原参数(挂载/端口/env/network 原样保留)
|
||
$D run -d --init --name sanguo_vnpy_v2 \
|
||
-v /volume1/homes/admin/.sanguo_projects/sanguo_vnpy_v2:/app \
|
||
-v /volume1/stock:/volume1/stock -p 8000:8000 \
|
||
--restart unless-stopped sanguo_vnpy_v2:pre-init-rebuild
|
||
```
|
||
- ⚠️ **启动 image 用 `pre-init-rebuild`**(含 jwt 等运行时包)。**勿用 base `with-sqlite`** —— 缺 jwt,web 起 `ModuleNotFoundError`。(Dockerfile.nas 零 pip install,依赖全靠 base,故 jwt 只在 commit 层,重建从 base 会丢。)
|
||
- 验证:`$D inspect sanguo_vnpy_v2 --format '{{.HostConfig.Init}}'` = `true`;`ps -o comm -p $(...PID)` = `docker-init`(tini)。
|
||
|
||
**正式 nas-verify(重建后必跑,同 ci-cd 口径)**:login(token 字段) + `/api/v1/strategy/list` 200 非空 + dbbardata 600519 SSE >0。
|
||
|
||
## 10. NAS 镜像可复现构建(避免 commit 链依赖丢失)
|
||
|
||
**现状**:NAS 运行的 `sanguo_vnpy_v2:pre-init-rebuild` 是 **docker commit 快照链**(with-sqlite → 手动 pip 装 bullet-trade/jwt/… → commit),非从 Dockerfile 构建。手动装的包只活在容器层,**任何从 base 重建都会丢**。实测 `pre-init-rebuild` 比 `with-sqlite` 多 64 个包(含 jwt/bullet-trade/baostock/jqdatasdk/vnpy_ctastrategy/pytest/整个 jupyter 栈)。
|
||
|
||
**构建走 `docker/Dockerfile`(单阶段自包含,2026-08-01 已验证+换装)**:FROM python:3.10-slim + 编译 TA-Lib + `pip install -r requirements-docker.txt` + COPY 代码。⚠️**不是** `Dockerfile.nas`(它 `FROM sanguo_vnpy:base` 是从未建成的 2-stage 残留,别用)。NAS 容器 bind-mount `/app` 提供运行时代码,镜像只需 Python+依赖层。
|
||
|
||
**已修(2026-08-01,均在 git)**:
|
||
- `requirements-docker.txt` 补缺失顶层运行时依赖:`bullet-trade==0.9.2`/`jqdatasdk==1.9.8`(bullet_trade import 链)/`vnpy_ctastrategy==1.4.1`/`vnpy_sqlite==1.1.3`。
|
||
- `polars[rtcompat]==1.42.1`:NAS CPU(Synology Celeron)无 AVX2,plain polars `import` 即 SIGILL(exit132);[rtcompat] 拉 polars-runtime-compat 兜底。**勿放回 `>=` 松约束**(会拉到要 AVX2 的新版崩溃)。
|
||
- `Dockerfile` pip+apt 都补清华镜像(原 pypi.org/deb.debian.org 国内不稳);加 `.dockerignore`(排 data_cache/frontend/venv 等,上下文 1.9G→~50MB)。
|
||
|
||
**可复现重建步骤(已实证)**:
|
||
```bash
|
||
D=/var/packages/Docker/target/usr/bin/docker
|
||
CTX=/volume1/homes/admin/.sanguo_projects/sanguo_vnpy_v2
|
||
# 1. 单阶段构建(setsid/nohup 防 ssh 杀;log 落盘)
|
||
ssh sanguo-nas "cd $CTX && setsid $D build -f docker/Dockerfile -t sanguo_vnpy_v2:reproducible . > /tmp/repro_build.log 2>&1 &"
|
||
# 2. 起测试容器带 --init(见 §9),新名+8001 端口不占 sanguo_vnpy_v2
|
||
$D run -d --init --name sanguo_repro_test -v $CTX:/app -v /volume1/stock:/volume1/stock -p 8001:8000 --network sanguo_vnpy_network -e DATA_DIR=$CTX sanguo_vnpy_v2:reproducible
|
||
# 3. 验证:nas-verify(8001 login+strategy/list+dbbardata) + 03 短测(bullet_trade 链路)
|
||
# 4. 全过才换主容器(见下);pre-init-rebuild image 留回滚
|
||
# 5. 进程检测用 /proc/<pid>/cmdline,别用 ps(此容器 ps 看不到 python,会误判)
|
||
```
|
||
**换主容器**:`$D stop sanguo_vnpy_v2 && $D rm sanguo_vnpy_v2` → `$D run -d --init --name sanguo_vnpy_v2`(同 §9 挂载/端口/network/env) `sanguo_vnpy_v2:reproducible` → 8000 nas-verify。**回滚**:同命令换 image 为 `sanguo_vnpy_v2:pre-init-rebuild`。
|
||
|
||
**验证结果(2026-08-01)**:03 momentum_timing 短测 total_return 15.62%/sharpe 1.59/6 持仓;web/login/dbbardata 全过;reproducible 镜像已换上主容器(Init=true)。⚠️02/01 未单独实测(用同栈,理论同),策略 session 首跑若异常即回滚。
|
||
|
||
## 11. 发布关联性检查清单(每次推 VPS 必过,2026-09-14 起)
|
||
|
||
> 背景:09-08 切大QMT 桥后,每次发布都伴随数据/策略 session 的连锁问题(09-08 断链/09-11 停摆/09-14 shadow 树死)。
|
||
> 本节是事后复盘的沉淀:**发布动作本身只管代码落盘,而生产系统的其余半边(schtask/引擎长进程/桥契约/非代码资产)全在代码流之外**。
|
||
> 生产栈架构与运维详见 [`deployment/vps-production-runbook.md`](./deployment/vps-production-runbook.md)。
|
||
|
||
### 11.0 铁律:VPS 引擎是长进程,promote ≠ 生效
|
||
|
||
12 引擎(live×6+shadow×6)常驻运行,promote 只落盘**不重启进程**——存活引擎继续跑旧代码,
|
||
下次启动的进程(schtask/收割重启)用新代码 = **混版运行**。涉引擎模块(sanguo_portfolio/sanguo_live/sanguo_trader)
|
||
发布必须与策略 session 约定收割重启窗口(工序见 production-runbook §5,先 /end desk 再收割)。
|
||
引擎重启会重置 run_monthly force 计数与锚语义 → **月度开火日(value 等)前禁重启**,窗口由策略域报。
|
||
|
||
### 11.1 发布时机窗
|
||
|
||
| 窗口 | 禁止动作 | 原因 |
|
||
|------|----------|------|
|
||
| 09:15–15:10 | 推涉引擎/桥/schtask 的变更 | 交易窗:探针开火/引擎下单/哨兵巡检全在忙 |
|
||
| 21:00–22:00 | 推涉数据腿的变更 | xt_eod 夜班占桥 |
|
||
| 盘前 07:50–09:25 | 重启桥/引擎 | relogin→晨查→探针→闸门链会被打断 |
|
||
| 月度开火日盘前 | 重启引擎 | 锚/force 计数重置=开火缺席(09-09 value 教训) |
|
||
|
||
### 11.2 影响矩阵(改了什么 → 谁中招 → 必做动作)
|
||
|
||
| 改动面 | 波及 | 发布时必做 |
|
||
|--------|------|-----------|
|
||
| `sanguo_portfolio/` `sanguo_live/` `sanguo_trader/` | 策略域 12 引擎 | **提前通知策略 session** 安排收割重启;重启后核台账锚点+基线 |
|
||
| `sanguo_data/` `scripts/data_platform/` | 数据域采集 schtask(xt_eod/bs/ak) | 通知数据 session;21:00 前完成让其当晚验锚 |
|
||
| `scripts/qmt_relogin/`(哨兵/探针/relogin/gate) | 三层防御本体 | **手动 scp 到 `C:\sanguo_bigqmt\`**(promote 不推此目录);观察哨兵下一跳 |
|
||
| `xtquant_bridge` shim / 桥 server(bigqmt_signal_trader) | 桥契约双侧(引擎+xt_eod) | shim 与 server 补丁**同步部署**;relogin 生效;日历探针断言 int 毫秒 |
|
||
| provider/取数层 | 引擎+回测+xt_eod | `verify_unified_e2e.py` 必跑 |
|
||
| `sanguo_backtest/` | 仅回测 | 无窗口约束,常规流程 |
|
||
|
||
### 11.3 非代码资产(本流程推不到,清单见 production-runbook §4)
|
||
|
||
promote.sh 只推 git 模块。**桥工具箱(`C:\sanguo_bigqmt`)、glue shim、QMT 安装树内 server 补丁、
|
||
wrapper ps1、redis、DPAPI 凭证、schtask 注册**全部在代码流之外——repo 改了 ≠ VPS 生效,
|
||
须手动 scp;QMT 客户端升级会重装安装树=server 补丁丢失须重打。
|
||
|
||
### 11.4 发布后验证序列(涉 🔴 面必跑)
|
||
|
||
1. 落盘:`Select-String` 验新代码标记(commit≠部署)
|
||
2. `verify_unified_e2e.py`(provider/数据面改动)
|
||
3. `bridge_switch_ops.py trade-probe` → GREEN cash 基线零漂(涉桥/引擎)
|
||
4. 哨兵下一跳(:10/:40)GREEN(涉防御/桥)
|
||
5. 交易日 09:15 探针 status=50(涉交易链)
|
||
6. 策略域恒等式(涉引擎,由其执行)
|
||
|
||
### 11.5 已知部署陷阱(事故实锤,复发先查)
|
||
|
||
| 陷阱 | 实锤 |
|
||
|------|------|
|
||
| pip 装 xtquant 相关包覆盖官方 xtquant | 09-08 事故:wheel src/ 同名直覆盖 site-packages |
|
||
| 桥 server 补丁打错部署位 | 09-11:安装树同名包多拷贝,首打错到源码副本 |
|
||
| wrapper ps1 带中文注释 | PS5.1 GBK 解析吃掉语句行,PYTHONPATH 静默失效(两次踩中) |
|
||
| 杀 auto-respawn 子进程不先 /end desk | 09-14:重拉赛跑,新树 8 秒全灭 |
|
||
| 裸跑 `python xt_eod.py` 无 wrapper | PYTHONPATH 回落旧腿,09-18 后必死 |
|
||
| 长断桥后以为引擎会自动重连 | 36h+ 重试环会死,桥活也不重连,须收割 |
|
||
|
||
## 12. Commit 环境标签与 VPS 触及面判定(2026-09-14 自 `vps-impact-map.md` 收编)
|
||
|
||
CI `enforce-label` 强制:本次 push 的 commit 必须带 `[vps]`(VPS 运行时也触发,待推 prod)或 `[nas]`(NAS/docker 专属),无标签 **CI 直接 fail**(merge commit 豁免)。
|
||
`[vps]` 触发链:nas-verify 绿后 CI 自动开/更新 `[待推VPS]` issue(open=待推);推 `vps-deploy.yml` 成功 → 打 `vps-deployed` tag + 自动关 issue。
|
||
查 VPS 未部署:`git log vps-deployed..HEAD --grep='\[vps\]'`(版本真相=移动 tag `vps-deployed`)。
|
||
|
||
> **CI pytest 门禁范围(2026-10-01 扩至 8 目录)**:`tests/data_platform+portfolio+factor+api`(原四)+ `tests/trader+backtest+data+orchestrator`(10-01 架构审计 P0-5 补入——撮合/涨跌停/软限/持仓账本/shadow 等钱相关测试进强制门禁,本地实测 339 绿 7.6s)。门禁口径唯一维护处=`ci-cd.yml` 注释。
|
||
> **CI 凭据纪律(2026-10-01 起)**:nas-verify 登录口令走 Gitea secret `NAS_API_PASSWORD`(${{ secrets.* }} 引用),**明文口令禁入 workflow/文档/git**;auth 种子=`config/backtest.yaml`(gitignore 本地文件,bcrypt hash+jwt_secret)。**⚠️轮换流程(10-01 实战定型)**:①Gitea 建/改 secret → ②Mac 手动 `promote --target nas --module config`(**CI 的 nas-deploy 从 git checkout 跑 promote,checkout 里没有 gitignore 种子——yaml 只能从 Mac 工作树手动推**)→ ③重启 NAS 容器(auth 启动时读入)→ ④push CI 改动/触发重跑。VPS 侧=config 模块手动 `--target vps` + 重启 sanguo-api。反序或漏②③=nas-verify login 红(run638 实证)。
|
||
|
||
**判定钥匙**:维度是「VPS 运行时是否触发」,**不是**「文件改了没」。
|
||
|
||
| 归属 | 模块/路径 | VPS 运行时场景 |
|
||
|------|-----------|----------------|
|
||
| 🔴 **VPS 触及**(=[vps],要考虑推) | `sanguo_data/` | schtask 每日采集(bs/xt/ak) |
|
||
| 🔴 | `sanguo_backtest/` | CTA 回测入口(load_data/metrics/引擎) |
|
||
| 🔴 | `sanguo_portfolio/runner_backtest.py` | VPS 直接调 run_backtest(不走 pool) |
|
||
| 🔴 | `sanguo_portfolio/runner_live.py` `sanguo_live/` `sanguo_trader/`(shadow) `sanguo_qmt_bridge/` | 实盘引擎 / 大QMT 桥栈 |
|
||
| 🔴 | `sanguo_common/` | 被上述引用 |
|
||
| 🔴 | `scripts/data_platform/*.py` | VPS schtask 跑的采集/merge/verify |
|
||
| 🔴 | `scripts/qmt_relogin/` | 三层防御本体;⚠️**promote 不部署它——须手动 scp 到 `C:\sanguo_bigqmt\`**(§11.3) |
|
||
| 🔴 | `sanguo_api/`(routes/app/uvicorn) | **VPS 常驻 sanguo-api**(run_api.ps1→run_web.py→:8000=生产控制台 vnpy.mysanguo.top,runbook §7.1);改后须 `schtasks /end /tn sanguo-api && schtasks /run /tn sanguo-api` |
|
||
| 🔴 | `frontend/`(Vue 源码) | VPS 挂 `frontend/dist`(公网前端);vps-deploy.yml 每次自动构建+推送+重启 sanguo-api(§13) |
|
||
| 🟡 弱触及 | `sanguo_orchestrator/` | 随 sanguo_api 常驻加载(run_web.py 单进程内存态,app.py 直接 import);VPS 控制台动作可触发,改后同样须重启 sanguo-api |
|
||
| 🟡 弱触及 | `requirements-lock.txt` | VPS 手动 pip 装,lock 仅参考 |
|
||
| 🟢 | `sanguo_web/`(**legacy 平行后端,不是前端**) | 已退役归档 `_retired/sanguo_web/`(2026-10-01 用户拍板:代码保留、退出 promote 白名单+docker COPY,不可再启动为服务);前端=`frontend/` |
|
||
| 🟢 | `docker/` `Dockerfile` `entrypoint.sh` `.gitea/` | VPS 无 docker |
|
||
| 🟢 | `config/`(vnpy_db 等容器 config) | VPS 用 `SANGUO_DB_PATH` env 覆盖(backtest.yaml 仍作种子直推) |
|
||
|
||
> **⚠️ 2026-10-01 勘误(audit/20261001_docs_audit/ P0-2)**:旧矩阵曾把 `sanguo_api/`+前端整行标 🟢「VPS 无常驻 web」——**错**。VPS 一直常驻 sanguo-api(runbook §7.6 09-25 勘误原文:「本就常驻跑纯 API」),vps-deploy.yml 每次部署都构建前端 dist 推 VPS 并重启 sanguo-api。api/前端改动按上表现判 **🔴 [vps]**。同病原文当时还复制在 `.claude/CLAUDE.md`、`session-environment-guide.md`、`vps-deploy-pending.md` 三处,本轮一并修正。
|
||
|
||
- **混合模块**(`sanguo_portfolio/` 若只改 orchestrator 调度、`sanguo_api/` 若只改 routes)看 diff 具体文件对照上表,不整模块一刀切。
|
||
- **判定 3 步**:①看 commit 标签 → ②对照触及面表 → ③问「VPS 跑这段代码吗」。
|
||
- 物理层天然隔离:promote.sh 白名单推送,`docker/`、`.gitea/` 等进不了 VPS。
|
||
|
||
### 12.1 设计文档同步闸门(`enforce design-doc sync`,2026-09-14 起)
|
||
|
||
三条线各有一份唯一活文档,CI 强制「**档随码走**」:push 触及某线代码域而未同 push 更新其设计文档(多档任一)→ **CI 直接 fail**;确属纯实现/bug 修复不改设计 → commit 标题**结尾**打 **`[no-doc]`** 声明豁免(显式可审计,与 `[vps]`/`[nas]` 同款肌肉记忆)。
|
||
|
||
| 线 | 代码域 | 唯一设计文档(任一更新即可) |
|
||
|----|--------|------------------------------|
|
||
| 因子 | `sanguo_factor/`、`frontend/src/views/factor/`、`sanguo_portfolio/strategies/factor_topn.py` | `docs/factor_research/factor-system-design.md`(**禁新增独立 md**,收编/日期节追加) |
|
||
| 数据 | `sanguo_data/` | `docs/superpowers/specs/2026-07-21-data-source-fusion-design.md` |
|
||
| 基建 | `scripts/nas_sync/`、`scripts/qmt_relogin/`、`scripts/bridge_switch_ops.py`、`.gitea/workflows/`、`docker/`、`Dockerfile` | `docs/three-env-code-promote.md` 或 `docs/deployment/vps-production-runbook.md` |
|
||
|
||
设计哲学(双层):指令层(CLAUDE.md 三线立牌)管「会做」,机械层(本闸门)管「不忘」;豁免是显式声明而非静默跳过,只有遗忘才会被拦。fail-open:闸门脚本自身异常一律放行,绝不因闸门 bug 卡全团队 push。
|
||
|
||
09-14 上线当日加固四处(评审实锤):①`[no-doc]` 匹配**锚定标题行尾**——原子串匹配被闸门自身 commit(描述里提及 `[no-doc]` 字样)误豁免;②闸门**自我 fetch BEFORE/AFTER**——原先依赖上一步 enforce-label 的 fetch 副作用,步骤重排即静默 no-op;③基建域补 `scripts/qmt_relogin/` + `scripts/bridge_switch_ops.py`(runbook §4 钦定的桥工具箱资产,当日桥工具修复曾零义务过闸);④**步骤名含冒号必须加引号**——首版 `name:` 里裸冒号使整个 workflow YAML 解析失败,Gitea **静默不创建任何 run**(CI 全瘫而非单步红,0141f4e 起三笔 push 零 run 才暴露);改 workflow 必须本地 `yaml.safe_load` 验过再推。
|
||
|
||
## 13. 前端构建与发布
|
||
|
||
- **NAS(自动)**:ci-cd.yml 的 nas-deploy 自动 `npm ci + vue-tsc + build + rsync dist`(dist 不进 git、不在 promote.sh)。
|
||
- **VPS(手动,mysanguo.online 北京直 serve)**:
|
||
```bash
|
||
cd frontend && npm run build
|
||
scp -r frontend/dist/* Administrator@49.232.102.198:C:/sanguo_vnpy_v2/frontend/dist/
|
||
ssh 49.232.102.198 "schtasks /end /tn sanguo-api && schtasks /run /tn sanguo-api"
|
||
```
|
||
(sanguo_api 的 ProcessPool worker 缓存旧代码,改后端必须重启 sanguo-api 才生效。)
|