From efd0b23cd33a300c2ea13b60221855c0c93eb838 Mon Sep 17 00:00:00 2001 From: claude_dev Date: Thu, 30 Jul 2026 20:58:51 +0800 Subject: [PATCH] =?UTF-8?q?feat(ci):=20=E5=BC=80=E5=8F=91session=E7=8E=AF?= =?UTF-8?q?=E5=A2=83=E6=89=8B=E5=86=8C=20+=20Gitea=20Actions=20CI/CD=20wor?= =?UTF-8?q?kflow=20+=20parquet=E5=90=8C=E6=AD=A5=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - docs/session-environment-guide.md: 面向各开发session的部署→验证→冒烟手册(P1) - .gitea/workflows/ci-cd.yml: 复用Mac runner编排 test→nas-smoke→vps(prod gate) - scripts/nas_sync/sync_parquet.sh: parquet全量scp(static+valuation), setsid脱离会话 --- .gitea/workflows/ci-cd.yml | 45 +++++++++ docs/session-environment-guide.md | 150 ++++++++++++++++++++++++++++++ scripts/nas_sync/sync_parquet.sh | 21 +++++ 3 files changed, 216 insertions(+) create mode 100644 .gitea/workflows/ci-cd.yml create mode 100644 docs/session-environment-guide.md create mode 100644 scripts/nas_sync/sync_parquet.sh diff --git a/.gitea/workflows/ci-cd.yml b/.gitea/workflows/ci-cd.yml new file mode 100644 index 0000000..c98c7a9 --- /dev/null +++ b/.gitea/workflows/ci-cd.yml @@ -0,0 +1,45 @@ +name: CI/CD +# 三机流水线:push master → Mac runner 单测 → NAS 冒烟(ssh) → [审批] → VPS 部署+冒烟(ssh) +# Runner: 复用现有 online 的 mac-mini-arm64 (label: macos-arm64),不另起 NAS runner。 +# Mac runner 作为编排节点,ssh 到 NAS/VPS 执行(~/.ssh/config 已配 sanguo-nas + 49.232.102.198)。 +on: + push: + branches: [master] + workflow_dispatch: {} # 允许 Gitea UI 手动触发(实测/重跑用) + +jobs: + # ---------- P2: 验证 ---------- + test: + runs-on: macos-arm64 + steps: + - uses: actions/checkout@v4 + - name: pytest (Mac venv310, fixture) + run: | + source ~/.openclaw/sanguo_projects/sanguo_vnpy_v2/venv310/bin/activate + pytest tests/ -q + # TODO 实测: actions/checkout 能否在 Mac 拉取(Mac 外网);venv310 依赖是否覆盖 checkout 代码的 import + + nas-smoke: + needs: test + runs-on: macos-arm64 + steps: + - name: NAS 冒烟 (ssh NAS 跑容器读 quant_trading.db) + run: | + ssh sanguo-nas 'cd /volume1/stock/sanguo_vnpy_v2 && \ + /var/packages/Docker/target/usr/bin/docker run --rm \ + -v /volume1/stock:/volume1/stock -v $(pwd):/app \ + sanguo_vnpy_v2:with-sqlite python /app/scripts/smoke_e2e.py' + # TODO 实测: Mac runner job 用户能否 ssh sanguo-nas(ssh key/known_hosts) + + # ---------- P3: VPS CD (带人工 gate) ---------- + vps: + needs: nas-smoke + runs-on: macos-arm64 + environment: prod # 人工审批 gate;删除此行 = 验证通过全自动推 VPS + steps: + - uses: actions/checkout@v4 + - name: promote 代码到 NAS+VPS + run: bash scripts/nas_sync/promote.sh + - name: VPS 冒烟 (LocalUnifiedProvider 真数据) + run: ssh 49.232.102.198 'cd C:\sanguo_vnpy_v2 && C:\Python310\python.exe -X utf8 scripts/data_platform/verify_unified_e2e.py' + # TODO 实测: prod environment 审批在 Gitea UI(仓库 Settings → Environments → prod 加 reviewer) diff --git a/docs/session-environment-guide.md b/docs/session-environment-guide.md new file mode 100644 index 0000000..0bc6baf --- /dev/null +++ b/docs/session-environment-guide.md @@ -0,0 +1,150 @@ +# 开发 Session 环境使用指南 + +> 面向**所有开发 session**(前端 / 后端 / 策略 / 数据):开发完成后如何 **部署 → 验证 → 同步 → 冒烟**。 +> 环境/部署/数据同步由**专属运维 session**负责;本手册让你能**自助**完成部署验证,不用等运维。 +> +> 晋升/rollback/数据铁律的完整细节见 [`three-env-code-promote.md`](./three-env-code-promote.md),本文不重复(DRY),只给「开发 session 照着做」的总入口。 + +## 0. 一句话流程 + +``` +Mac 改代码 → pytest(本地 fixture) → promote.sh → NAS 冒烟 → VPS 冒烟 +``` + +--- + +## 1. 职责边界 + +| 角色 | 负责 | 工作机器 | +|------|------|----------| +| **各开发 session** | 你自己的模块代码 + 本地单测 | Mac | +| **运维 session** | 环境、部署脚本、CI/CD、数据同步、定时任务 | Mac + NAS + VPS | + +**边界**:你改代码 + 本地测;部署用 `promote.sh`(或 CI/CD 自动);**环境/数据层问题**(库连不上、数据缺失/重复、路径错)报运维 session 根治,**不在 provider/业务代码里兜底**(兜底会掩盖数据层真 bug)。 + +--- + +## 2. 三机总览 + +| 机器 | 角色 | 代码路径 | 关键特征 | +|------|------|----------|----------| +| **Mac** | dev 开发 | `~/.openclaw/sanguo_projects/sanguo_vnpy_v2` | arm64 / `venv310` / **用 fixture,不拉真实数据**(42GB 放不进) | +| **NAS** | test 验证 | `/volume1/stock/sanguo_vnpy_v2` | amd64 / Docker / 数据**只读副本** | +| **VPS** | prod 生产 | `C:\sanguo_vnpy_v2` | Windows / **无 git·docker·rsync**(只有 scp)/ 权威 DB **唯一写源** | + +**数据流单向**:VPS 采集(权威)→ NAS 只读副本 → Mac 按需。**NAS/Mac 永不写回 VPS**。 + +--- + +## 3. 标准流程(开发完照着走) + +### Step 1 — Mac 本地单测(fixture) + +```bash +cd ~/.openclaw/sanguo_projects/sanguo_vnpy_v2 +source venv310/bin/activate +pytest tests/<你的模块>/ -q +``` +**预期**:全绿。Mac 用 fixture,不需要真实数据。 + +### Step 2 — 部署 NAS test + VPS prod(一条命令同时推) + +```bash +bash scripts/nas_sync/promote.sh --module sanguo_<你的模块> +``` +- 单模块补推用 `--module`;**改了多个模块或根文件**(pyproject/pytest.ini/requirements/run_web.py)用全量 `bash scripts/nas_sync/promote.sh`。 +- promote.sh 内部:Mac→NAS(rsync,LAN 快) + Mac→VPS(scp)。**数据铁律双防护**,绝不会推 `data/`。 + +### Step 3 — NAS 冒烟(数据层/读取层改动必跑) + +```bash +ssh sanguo-nas 'cd /volume1/stock/sanguo_vnpy_v2 && \ + /var/packages/Docker/target/usr/bin/docker run --rm \ + -v /volume1/stock:/volume1/stock -v $(pwd):/app \ + sanguo_vnpy_v2:with-sqlite python /app/scripts/smoke_e2e.py' +``` +**预期**:`RESULT: 读取 N 条 BarData`(N>0)。验证 `read_db_daily` 能读 NAS `quant_trading.db`。 + +### Step 4 — VPS 落盘验证(确认新代码到 prod) + +```bash +ssh 49.232.102.198 'powershell -NoProfile -Command "Get-ChildItem C:\sanguo_vnpy_v2\sanguo_<模块> -Filter *.py | Select Name,LastWriteTime | Format-Table -Auto"' +``` +**预期**:文件 `LastWriteTime` 是刚推送的时间。 +> 教训:**commit ≠ 部署 VPS**。git commit 后必须 promote,否则 VPS 跑旧代码。 + +### Step 5 — VPS 冒烟(provider/数据层改动必跑) + +```bash +ssh 49.232.102.198 'cd C:\sanguo_vnpy_v2 && C:\Python310\python.exe -X utf8 scripts/data_platform/verify_unified_e2e.py' +``` +**预期**:末尾 `E2E DONE — LocalUnifiedProvider VPS 真数据验证通过`。验证 get_price / get_index_stocks / get_fundamentals 三大接口在 VPS 真数据上正确。 + +--- + +## 4. 数据访问铁律 + +- **provider 读 VPS 本地数据**(`C:\sanguo_vnpy_v2\data\`),**不调 online**(baostock/akshare 在线)。 +- **NAS 副本只读**,永不写回 VPS(VPS 是唯一写源)。 +- **Mac 用 fixture**,不拉真实数据(放不进,且 Phase1 已决策 Mac 只做 fixture 开发)。 +- **数据层瑕疵**(重复行/格式不一/缺失)→ 报运维/数据 session 根治,**不在 provider 适配兜底**。 + +--- + +## 5. 角色速查表 + +| 角色 | 模块 | 改完怎么推 | 必跑验证 | +|------|------|-----------|----------| +| **前端** | `sanguo_web` | `--module sanguo_web` | Mac `pytest tests/`(无 web 子目录则全量)+ 浏览器看渲染 | +| **后端** | `sanguo_api` | `--module sanguo_api` | Mac `pytest tests/api/` | +| **策略** | `sanguo_portfolio` `sanguo_backtest` | **全量**(多模块) | Mac `pytest tests/portfolio/ tests/backtest/` + NAS 冒烟(Step 3) | +| **数据** | `sanguo_data` `sanguo_common` | `--module sanguo_data` | NAS 冒烟(Step 3)+ **VPS 冒烟(Step 5)** | + +> 策略/数据层改动影响 provider 数据读取,**NAS + VPS 双冒烟必跑**。 + +--- + +## 6. Reload 机制(VPS 无常驻 web) + +VPS 是**一次性任务**模型(回测 runner + 采集 schtask),**没有常驻 web 服务**。 + +- 代码部署后 **下次启动自动加载**,不用 hot reload、不用重启服务。 +- **立即让采集生效**: + ```bash + ssh 49.232.102.198 'schtasks /end sanguo-bs-eod && schtasks /run sanguo-bs-eod' + ``` + +--- + +## 7. 常见坑(完整版见 [`three-env-code-promote.md` §8](./three-env-code-promote.md)) + +| 坑 | 解法 | +|----|------| +| `ssh vps` 连不上(fake-ip 198.18.1.254) | 用 `ssh 49.232.102.198`(本机 config 无 vps 别名) | +| scp 路径报错 | VPS 目标用**正斜杠**:`49.232.102.198:"C:/sanguo_vnpy_v2/"` | +| PowerShell 中文乱码 | `-NoProfile` + `Get-ChildItem`/`Select-String`,避中文路径 | +| 长任务挂 ssh 被杀 | NAS 用 `setsid`(脱离会话进程组),VPS 用 `schtasks` | +| 光猫挡 VPS 入站 | VPS 不能 git pull / rsync push,只能 Mac/NAS **主动 scp 推** | +| 改完 VPS 行为没变 | commit≠部署;代码下次启动才加载,采集要 `/end && /run` 立即生效 | + +--- + +## 8. CI/CD(自动化,建设中) + +目标形态(Gitea Actions,NAS 1.26.2 已支持): +``` +push master → 自动 NAS 单测+冒烟(job_nas_test) → 通过 → 审批后推 VPS+冒烟(job_vps) +``` +- **P2(NAS CI)**:push 自动跑 pytest + smoke_e2e.py。 +- **P3(VPS CD)**:NAS 通过 → 人工审批 gate(prod 安全)→ 自动 scp 推 VPS + verify_unified_e2e.py。 +- **建成前**:按本手册 §3 手动走(5 步)。 + +建成状态见运维 session 更新。 + +--- + +## 参考 + +- [三机代码晋升 Runbook](./three-env-code-promote.md) — 晋升命令 / rollback / 数据铁律 / 常见坑(完整版) +- [三机环境设计 spec](./design/dev-test-prod-env-design.md) — dev/test/prod 权威设计 +- [NAS 部署目录约定](../CLAUDE.md) — `/app` 与 `/volume1/stock` 挂载、rsync 部署流程 diff --git a/scripts/nas_sync/sync_parquet.sh b/scripts/nas_sync/sync_parquet.sh new file mode 100644 index 0000000..0cd69fc --- /dev/null +++ b/scripts/nas_sync/sync_parquet.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# NAS 端:VPS parquet(static+valuation) 全量 scp pull(一次性备份,非每日)。 +# 脱离 ssh 会话运行(避免 sshd 关会话杀进程组): +# nohup setsid bash sync_parquet.sh /dev/null 2>&1 & +set -u +KEY=/var/services/homes/admin/.ssh/id_ed25519_nas +VPS=Administrator@49.232.102.198 +DATA=/volume1/stock/sanguo_vnpy_v2/data +LOG=/volume1/stock/sanguo_vnpy_v2/data_backup/parquet.log + +echo "=== parquet scp start $(date) ===" >> "$LOG" +cd "$DATA" || { echo "cd fail" >> "$LOG"; exit 1; } + +# scp -r 覆盖式拉取(scp 无断点续传,已传文件会重传;6.24GB 全量约 70min) +scp -r -i "$KEY" -o StrictHostKeyChecking=no \ + "$VPS:C:/sanguo_vnpy_v2/data/static" \ + "$VPS:C:/sanguo_vnpy_v2/data/valuation_baostock" ./ >> "$LOG" 2>&1 +echo "SCP_RC=$? $(date)" >> "$LOG" + +du -sh "$DATA/static" "$DATA/valuation_baostock" >> "$LOG" 2>&1 +echo "PARQUET_DONE $(date)" >> "$LOG"