Compare commits

...
Author SHA1 Message Date
c2ebc0ab91 docs(HEL-353): 恢复普通 README 并移除多媒体展示
放弃宣传稿与 docs/images 素材,按当前仓库真实结构重写常规说明。

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: multica-agent <github@multica.ai>
2026-09-01 21:58:35 +08:00
71a6d68ed7 docs(HEL-277): 用 25 组真实素材替换 README 占位
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: multica-agent <github@multica.ai>
2026-09-01 21:39:08 +08:00
cda13a787f docs(HEL-272): 用终版项目介绍更新仓库 README
将总工审核通过的项目介绍写入根目录 README,保留本机启动、Docker 与系统配置等必要说明,技术栈保持公开。

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: multica-agent <github@multica.ai>
2026-08-30 20:58:16 +08:00
总管andmultica-agent 6b688fd473 修复(HEL-235B): 可靠同步受限权限工作目录
Co-authored-by: multica-agent <github@multica.ai>
2026-08-29 23:29:11 +08:00
总管andmultica-agent 1e8da5fee2 部署(HEL-235B): 补严分叉与镜像标签校验
Co-authored-by: multica-agent <github@multica.ai>
2026-08-29 23:27:18 +08:00
总工andmultica-agent 51f410d942 部署(HEL-235B): 服务器本地目录纳入 Git 管理,固化 main 校验构建流程
Co-authored-by: multica-agent <github@multica.ai>
2026-08-29 23:22:59 +08:00
总工 a8732f51be 部署集成(HEL-252): 合入登录页动态小人 4a63ccd 2026-08-29 21:59:09 +08:00
6 changed files with 270 additions and 114 deletions
+36 -66
View File
@@ -165,88 +165,58 @@ docker compose restart xiaobai-review
docker compose down
```
### 镜像构建的唯一安全入口(2026-08 HEL-235 起
### 服务器本地目录更新与构建(日常推荐
生产机 `192.168.200.11` `/opt/1panel/docker/compose/xiaobaifupan` 只是历史文件树:
不是 Git 仓库、内容停在旧提交、与线上镜像不一致,且其 `compose.yaml` 会把构建结果打进
`xiaobai-review:latest`。**禁止在该目录(或任何服务器工作树)里 `docker build` /
`docker compose build`**,否则会把已上线功能悄悄打回旧版。
唯一安全构建方式是在有仓库检出、能免密 SSH 到部署机的机器上运行:
```bash
tools/build_image.sh <提交号> <镜像tag>
# 示例:tools/build_image.sh cefc86917d89 verify-hel235-cefc869
```
该脚本的行为约束:
-`git fetch`,再把提交号解析为完整 SHA,解析失败立即中止,绝不使用本地脏状态或服务器旧目录;
- 构建前读取当前线上容器镜像的 `org.opencontainers.image.revision`,用 Git 祖先关系确认候选提交包含线上全部历史;落后 `main`、旁支或错误提交会直接退出,并打印线上提交、候选提交、文件差异和将丢失的提交;
- 镜像 tag 必须以 `-<提交短号7位>` 结尾(如 `hel234-cefc869`),禁止 `latest``rollback-*`
- 通过 `git archive <提交> | ssh 部署机 docker build -` 流式构建,服务器上不存在构建用工作树;
- 构建后回读镜像 label 里的 `org.opencontainers.image.revision`,与预期提交不一致则删除镜像并中止;
- 每次构建在部署机 `~/xiaobai-build/BUILD_LOG.tsv` 留痕,可追溯每个镜像的来源提交。
构建只产出镜像,不启动、不替换任何容器;换版用新 tag 起新容器,回滚用既有镜像 tag 重跑。
### 使用 Gitea 更新程序(旧方式,生产机禁用)
代码仓库为:
生产机 `192.168.200.11``/opt/1panel/docker/compose/xiaobaifupan` 自 2026-08-29HEL-235B
起已是受 Git 管理的工作目录,只跟踪 Gitea `main`(仓库
`http://192.168.200.36:3200/leefer/xiaobai-review.git`)。由于目录顶层归 root
`.git` 存放在部署账号家目录(外部 Git 目录方案):
```text
http://192.168.200.36:3200/leefer/xiaobaifupan.git
~/xiaobai-build/repos/xiaobai-review.git Git 元数据(分支/历史/索引)
/opt/1panel/docker/compose/xiaobaifupan 工作目录(程序文件本体)
~/xiaobai-build/update-from-main.sh 一键更新+构建入口
~/xiaobai-git 便捷查看(status/log/diff
```
首次在服务器部署代码时,可以直接克隆到目标目录
日常更新只需要在服务器上执行一条命令
```bash
sudo mkdir -p /opt/xiaobai-review
sudo chown "$USER":"$USER" /opt/xiaobai-review
git clone http://192.168.200.36:3200/leefer/xiaobaifupan.git /opt/xiaobai-review
cd /opt/xiaobai-review
~/xiaobai-build/update-from-main.sh # 更新到 main 并构建 main-<短号> 镜像
~/xiaobai-build/update-from-main.sh verify-tag main-a8732f5 # 部署前复核镜像与 main 一致
```
私有仓库会提示输入 Gitea 用户名和密码或访问令牌。不要把密码写入仓库 URL、
`compose.yaml` 或脚本。然后把原 `.env``data/` 放回该目录;这两项已被 Git
忽略,后续拉取代码不会覆盖数据库与密钥。
脚本在构建前强制完成五道校验,任一不符立即停止、不产出镜像:
如需部署管理员私有问师,通过 NAS 文件管理器将本地
`data/private-mentor-skills/` 复制到服务器项目的同名 `data` 目录,并保持目录仅由
部署账号和容器运行用户读取。该内容不会通过 Gitea 同步。
1. `git fetch` 成功(连不上 Gitea 即停);
2. 必须在 `main` 分支(智能体不得用功能分支直接当正式线);
3. 工作区无未提交改动、无多余文件;
4. 只允许快进合并到 `origin/main`(分叉即停);main 新增/删除顶层文件时会给出
需管理员执行的精确清单(目录顶层归 root);
5. 构建后回读镜像 `org.opencontainers.image.revision`,与 `main` 提交不一致则删除镜像。
每次更新前先创建 SQLite 一致性备份,再拉取并重建容器(注意:`docker compose up -d --build`
从服务器本地工作树构建,仅适用于来源可信的全新环境;生产机 `192.168.200.11` 禁用,
请用 `tools/build_image.sh` 构建后换容器):
镜像 tag 固定为 `main-<提交短号7位>`(不带提交号的模糊 tag 一律禁止);每次构建在
`~/xiaobai-build/BUILD_LOG.tsv` 留痕。构建只产出镜像,不启动、不替换容器;换版与
回滚步骤见 `~/xiaobai-build/README.md`
```bash
cd /opt/xiaobai-review
docker compose exec -T xiaobai-review python -c "import sqlite3; s=sqlite3.connect('/app/data/review.db'); d=sqlite3.connect('/app/data/review-before-update.db'); s.backup(d); d.close(); s.close()"
git pull --ff-only origin main
docker compose up -d --build
docker compose ps
curl --fail http://127.0.0.1:8765/api/health
```
`compose.yaml` 的镜像名与 revision 标签同样做了强校验:直接 `docker compose up -d --build`
会因缺少 `XIAOBAI_GIT_REV` / `XIAOBAI_GIT_SHORT` 变量而拒绝执行,避免再出现构建进
`latest` 的模糊版本。需要用 compose 时先 `export` 这两个变量(值以
`~/xiaobai-build/xiaobai-git rev-parse HEAD` 为准),或直接用上面的脚本。
`docker compose up -d --build` 会原地替换应用容器,不删除宿主机的 `data` 目录。
数据库迁移会在新容器启动时自动执行。若 `git pull --ff-only` 提示本地代码有修改,
先用 `git status` 查明原因,不要用强制重置覆盖 `.env``data`
### 智能体高级入口:Git 归档流式构建
### 不使用 Git 时更新(生产机禁用)
有仓库检出、能免密 SSH 到部署机的智能体可以用 `tools/build_image.sh <提交号> <镜像tag>`
从任意明确提交流式构建(`git archive | ssh docker build`),tag 同样必须以
`-<提交短号7位>` 结尾,构建后回读 revision 校验并留痕。用于在服务器不便拉取时的
应急构建;日常正式线仍应走 `main`
`docker compose build` 会从服务器本地目录构建,来源提交不可追溯。生产机
`192.168.200.11` 上禁止使用本节方式,一律改用上一节的 `tools/build_image.sh`
### 历史方式(已废弃)
重新上传代码后执行:
```bash
docker compose down
docker compose build --pull
docker compose up -d
```
`docker compose down` 不会删除宿主机的 `data` 目录。不要使用带有手工删除
`data` 目录的清理命令。
早期文档建议在服务器重新 `git clone` 一份或手工上传代码后 `docker compose up --build`
这两条路径已废弃:服务器上**只允许存在一个受管工作目录**(上述
`/opt/1panel/docker/compose/xiaobaifupan`),任何脱离 Git 校验的本地构建都会把
来源提交变成不可追溯状态,禁止使用。
## 7. 备份与恢复
+104 -42
View File
@@ -1,76 +1,138 @@
# 小白复盘 Web
# 小白复盘
一个面向 A 股盘后复盘的本地 Web 工作台。后端使用 Python 访问 Tushare Pro,前端不依赖构建工具
面向 A 股盘后复盘的本地 Web 工作台。收盘后把涨停、炸板、连板梯队、板块轮动、集合竞价、龙虎榜等数据整理成可浏览的复盘界面;不接券商、不代为下单,也不提供个股推荐
本目录是经过保真迁移、结构治理和用户人工验收的唯一正式源码,不依赖父目录旧程序或失败版本
目录职责见[ARCHITECTURE.md](ARCHITECTURE.md),产品与维护文档见[docs/README.md](docs/README.md)。
本目录是唯一正式源码。模块边界见 [ARCHITECTURE.md](ARCHITECTURE.md),产品与维护文档见 [docs/README.md](docs/README.md)
当前包含集合竞价、涨停池、炸板池、跌停板、昨日涨停、涨停表现、市场天梯、板块轮动、题材库、人气热榜、龙虎榜和个人复盘工作区。交易日快照与同步记录保存在本地 SQLite 数据库 `data/review.db`
## 主要功能
集合竞价中心采用盘前生命周期:9:15 前显示预告,9:15–9:25 明确等待最终竞价,9:25–9:30 自动读取并重试最终竞价筛选,9:30 后停止更新并冻结为复盘归档。当前 Tushare 只提供 9:25 最终竞价快照,不将其表述为动态虚拟撮合行情
登录后左侧共 16 个页面,另有一个内嵌页「策略持续跟踪」。交易日快照保存在本地 SQLite 数据库 `data/review.db`
第三阶段加入了机构席位、席位别名、个股复权日 K、资金流、自选股、涨停原因修订、个股笔记、每日复盘和历史数据回补。
- **情绪周期**:0–100 情绪温度与阶段判定(默认首页)
- **涨停池 / 炸板池 / 跌停板 / 昨日涨停 / 涨停表现**:封板结构、炸板、跌停与昨日反馈
- **市场天梯**:按连板高度排列的市场梯队
- **板块轮动**:近若干交易日板块热力与成分下钻
- **集合竞价**:盘前生命周期;9:30 后停止更新并冻结为复盘归档。当前数据源提供 9:25 最终竞价快照,不是动态虚拟撮合行情
- **题材库 / 人气热榜 / 龙虎榜**:题材成分、双榜人气、席位与游资档案
- **智能选股**(会员):六阶段策略、精选策略库、自然语言编译为受控公式后的确定性筛选与滚动回测;候选需手动加入后才进入五交易日跟踪
- **问师**(会员):按选定的游资思维 Skill 单师对话;新增公开角色时在 `游资skills` 下增加含 `SKILL.md` 的目录,并在 `游资skills/mentor_catalog.json` 登记。管理员私有角色放在 `data/private-mentor-skills`(不进 Git / 镜像)
- **问天**(会员,冻结区,勿改代码):观势 / 观气 / 观心。卦象、干支、节气与气机由本地程序确定性计算,大模型只负责文字解释
- **我的复盘**:手工交易日志、每日复盘、提醒中心与复盘助手;不接券商、不自动下单
股票代码在桌面端悬停后会显示分时与日 K 快速预览,默认优先展示日 K;移动端点击代码后从底部打开预览面板。股票详情以及板块、题材、指数详情均可在日 K 与最新分时之间切换。日 K 复用个股详情缓存;分时优先使用 iFinD,东方财富仅作隔离的展示兜底,并使用短时内存缓存。图表数据不写入主行情不参与情绪、选股或问天计算;不可用时明确显示“分时不可用”,不会用日 K 模拟分时走势
全局能力:日间 / 夜间主题、股票代码悬停预览日 K 与分时、`Ctrl + K` 全局搜索。图表数据不写入主行情,也不参与情绪、选股或问天计算。
智能选股包含六阶段盘后候选、29 套精选策略、自定义公式 DSL、自然语言公式编译、候选排名和滚动回测。阶段与精选策略在当日行情更新后由后台确定性计算;自定义选股由用户手动执行,LLM 只负责编译自然语言条件,不参与候选筛选。竞价、估值、财务、资金、人气和席位等字段按已登记的数据可用性进入因子库,缺失时明确显示覆盖问题。
## 技术栈
候选只有经用户手动加入后才进入五交易日持续跟踪,展示 T+1 开盘/收盘、T+3、T+5、最大涨幅与最大回撤。提醒中心支持手工日期提醒,并在策略首日反馈和五日跟踪完成时生成账号私有的站内提醒。
| 层面 | 说明 |
| --- | --- |
| 运行时 | Python 3.12;标准库 `ThreadingHTTPServer`,无独立 Web 框架 |
| 依赖 | `requirements.txt` 仅含 `cryptography`;问天历法使用仓库内 `vendor/lunar_python` |
| 数据库 | SQLiteWAL),默认文件 `data/review.db` |
| 前端 | 原生 HTML / CSS / JavaScript,无打包、无构建步骤 |
| 部署 | Docker / Docker Compose,或本机直接运行 `server.py` |
| 安全 | 账号密码 scrypt 哈希;行情 Token 与模型密钥用 `APP_ENCRYPTION_KEY` 加密后存库 |
问师模块会读取当前复盘、近十日市场情绪、涨跌停、昨日反馈、板块轮动、市场阶段、龙虎榜和指定个股数据,再按选中的游资思维 Skill 进行单师对话。对话记录按账号、老师和交易日期保存在服务端;主模型不可用时自动切换辅助模型。
## 环境要求
新增公开问师角色时,在 `游资skills` 下增加一个包含 `SKILL.md` 的独立目录,并在 `游资skills/mentor_catalog.json` 中登记素材等级与结构质检。管理员私有角色放在 `data/private-mentor-skills`,该目录不进入 Git 或 Docker 镜像,且只会出现在管理员的问师列表中。系统会从 Skill 的 frontmatter、一级标题、核心模型和引用语中自动生成角色信息,无需修改注册代码。
- Python 3.12(与 `Dockerfile` 一致)
- 本机启动:能执行 `python` / `pip`
- Docker 部署:Docker Engine 24+Compose v2`docker compose`
- 行情:部署者自行申请并配置 Tushare Pro Token;部分分时优先使用同花顺 iFinD(可选)
- 问师、问天解释、复盘助手、自然语言编译公式:需配置 OpenAI 兼容接口;未配置时市场数据页仍可用
问天模块包含三个相互独立的部分:观势以市场数据生成三才六爻,用于观察“势”,行情缺失或自动取象明显偏差时可显式手动校准六爻,人工结果与自动来源严格区分;观气依据干支、精确节气、五运六气及客主加临关系观察“运”,行业五行仅作传统取象归类;观心先准备1秒,再完成5轮“吸3秒、顿2秒、呼4秒”,随后以六次三枚铜钱起卦、察念和解卦完成一次不输入问题的问心仪式。卦象、干支、节气与气机关系均由本地确定性程序计算,LLM只负责解释,不参与起卦或改动结果。
## 安装与启动
问天模块使用项目本地的 `lunar-python` 计算历法,并使用 `data/iching_zh.json` 中的固定六十四卦、卦辞和爻辞。第三方授权见 `THIRD_PARTY_NOTICES.md`
仓库根目录即为运行目录(`server.py``requirements.txt` 都在根目录)
“我的复盘”包含结构化手工交易日志,可记录方向、价格、数量、仓位、盈亏、逻辑、执行、情绪和标签,不接券商也不自动下单。顶部“复盘助手”以流式方式读取市场统计、策略跟踪、提醒、个人复盘和交易日志;对话按账号保存,只提供分析和条件化计划。
## 启动
```powershell
cd app
```bash
python -m pip install -r requirements.txt
python server.py
```
浏览器打开 `http://127.0.0.1:8765`,首次使用先注册账号。首个账号自动成为管理员,后续账号默认为普通用户。主行情不再回退演示数据:盘前、非交易日或临时取数失败时沿用最近真实收盘快照;没有任何真实快照时提示等待管理员完成首次同步。
默认监听 `127.0.0.1:8765`(仅本机可访问)。浏览器打开该地址,首次使用先注册账号;第一个账号自动成为管理员,之后注册的默认为普通用户。
需要后台启动本地验收端口时,使用`tools/start_local.ps1`。该工具把日志、进程号和Python缓存
统一写入`runtime/`,不在源码根目录产生运行文件:
主行情不再回退演示数据:盘前、非交易日或临时取数失败时沿用最近真实收盘快照;没有任何真实快照时,页面会提示等待管理员完成首次同步。
可选参数:
```bash
python server.py --host 127.0.0.1 --port 8765
```
Windows 下若需要后台启动,并把日志、进程号和 Python 缓存写入 `runtime/`(不在源码根目录产生运行文件):
```powershell
powershell -ExecutionPolicy Bypass -File tools/start_local.ps1 -Port 8797
powershell -ExecutionPolicy Bypass -File tools/start_local.ps1
```
局域网 Docker 部署使用 `Dockerfile``compose.yaml`,完整的迁移、持久化、
防火墙、备份和恢复步骤见 [DOCKER_DEPLOY.md](DOCKER_DEPLOY.md)。
该脚本默认端口为 `8797`。统一验收:
账号密码使用 scrypt 哈希;公共 Tushare Token、平台模型密钥以及原始生辰资料均使用 `APP_ENCRYPTION_KEY` 加密后保存在 SQLite。公共数据和平台模型归系统所有,生辰资料仍按账号隔离。普通用户不配置 LLM,只有管理员授权的有效会员可以使用平台模型。请将 `.env` 与数据库一起备份,丢失加密密钥后无法恢复这些资料。
```bash
python tools/verify_baseline.py
```
## 系统与账号配置
涉及运行时或前端时再加 `--e2e`Playwright)。
管理员通过页面右上角“系统管理”保存公共 Tushare Token、平台主/辅助模型、会员每日额度和后台刷新开关。所有用户读取同一份 SQLite 行情快照,不再分别配置行情 Token。已有个人凭据中的 Tushare Token 会在升级时迁移到系统配置并从个人凭据移除。
## Docker 使用
局域网或服务器部署使用仓库根目录的 `Dockerfile``compose.yaml`。容器监听 `8765`,默认以非 root 用户运行,并把宿主机 `./data` 挂到容器内 `/app/data`
1. 复制 `.env.example``.env`,填入 `APP_ENCRYPTION_KEY` 以及行情 / 模型等初始化配置。密钥不会返回到浏览器。
2. `compose.yaml` 构建时要求带上当前 Git 提交号,避免打出无版本标签的镜像:
```bash
export XIAOBAI_GIT_REV="$(git rev-parse HEAD)"
export XIAOBAI_GIT_SHORT="$(git rev-parse --short=7 HEAD)"
docker compose build
docker compose up -d
```
3. 检查健康接口:
```bash
docker compose ps
curl http://127.0.0.1:8765/api/health
```
健康响应类似 `{"ok": true, "storage": "sqlite", "account_required": true}`
完整的迁移、持久化、防火墙、备份、恢复与正式线构建入口见 [DOCKER_DEPLOY.md](DOCKER_DEPLOY.md)。`.env` 必须与数据库成对备份;丢失 `APP_ENCRYPTION_KEY` 后无法恢复加密资料。
## 目录与配置
```text
TUSHARE_TOKEN=你的Token
server.py 进程入口
backend/ 服务、路由、数据、任务、LLM
frontend/ 无构建前端(shared + pages
config/ 页面 / API / 任务等注册表
data/ SQLite 与私有数据(数据库文件不进 Git)
runtime/ 本地日志、PID、缓存(不进 Git)
tools/ 启动、验收与构建辅助脚本
游资skills/ 公开问师角色
vendor/ 本地第三方库(含 lunar-python
Dockerfile
compose.yaml
.env.example 环境变量模板(复制为 .env 后填写)
```
`.env` 中的 Tushare 和平台 LLM 配置只用于初始化系统配置,密钥不会返回到浏览器。后台刷新只在交易时段更新 SQLite 快照,不会主动刷新或重绘用户页面;用户点击页面“刷新”时读取最新快照。管理员也可点“后台刷新”立即启动一次后台同步,当前页面仍保持不变
管理员通过页面右上角「系统管理」保存公共 Tushare Token、平台主/辅助模型、会员每日额度和后台刷新开关。所有用户读取同一份 SQLite 行情快照。`.env` 中的 Tushare 和平台 LLM 配置只用于初始化系统配置。
普通用户在账号设置中维护个人资料、查看会员状态和修改密码,不配置个人 LLM。有效会员自动使用平台模型;管理员可在“系统管理”中手动开通、续期、停用会员。平台模型受管理员设置的每日调用次数限制,管理员账号始终可用。
普通用户在账号设置中维护个人资料、查看会员状态和修改密码,不配置个人 LLM。有效会员使用平台模型;管理员可开通、续期、停用会员。平台模型受每日调用次数限制,管理员账号始终可用。
Tushare 各接口有独立积分权限。程序优先使用 `limit_list_d` 获取涨跌停明细;该接口不可用时,会尝试通过日线和每日涨跌停价格推算。
相关文档:
## 隔离实时聚合验证
- [ARCHITECTURE.md](ARCHITECTURE.md) — 模块边界
- [docs/README.md](docs/README.md) — 交接手册入口
- [DOCKER_DEPLOY.md](DOCKER_DEPLOY.md) — Docker 部署、备份与恢复
- [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md) — 第三方授权(含问天历法库)
- [AGENTS.md](AGENTS.md) — 维护约束
`backend/data/realtime.py`用于验证东方财富、同花顺和选股宝网页数据源。它不写入 SQLite 主行情快照,也不参与情绪评分或智能选股;当 Tushare 实时指数权限不可用时,观势会使用东方财富三大指数和板块外显,并继续使用 Tushare 的板块成分内核与个股数据。
## 注意事项与免责声明
登录后可调用:
```text
GET /api/realtime-aggregate/health?sector=元器件
```
返回内容包括东方财富三大指数及板块快照、指数时间差、同花顺和选股宝可用性、每个来源的耗时与错误。盘中指数时间差不超过15秒,收盘后不超过120秒。`ready=true` 仅表示本次验证满足聚合层约束,不代表这些网页内部接口具有长期稳定性或商业使用授权
- 本项目是个人研究与复盘工具,全部数据、指标、候选与文字分析均不构成投资建议、证券推荐或买卖要约。
- 不接券商、不代为下单。交易日志只做手工记录与统计,不代表实际成交。
- 情绪温度、阶段判定、连板梯队、策略筛选等均为基于公开数据的统计与规则计算,不预测走势,不保证收益。
- 「问天」属于传统文化视角的观察工具,不具备预测功能,不得作为投资依据;该模块为冻结区,不要改其代码。
- 行情来自第三方接口,可能延迟、缺失或口径调整;不可用时页面会明确提示,请以交易所与券商正式披露为准。
- 不要把服务端口直接暴露到公网。不要把 Token、密码、密钥、数据库或 `.env` 提交进 Git。
- 股市有风险,入市需谨慎。投资决策及其后果由使用者本人承担
+3 -1
View File
@@ -3,7 +3,9 @@ services:
build:
context: .
dockerfile: Dockerfile
image: xiaobai-review:latest
labels:
org.opencontainers.image.revision: "${XIAOBAI_GIT_REV:?必须先设置 XIAOBAI_GIT_REV=当前 main 完整提交号(或改用 tools/update_from_main.sh}"
image: xiaobai-review:main-${XIAOBAI_GIT_SHORT:?必须先设置 XIAOBAI_GIT_SHORT=7位提交短号(或改用 tools/update_from_main.sh}
container_name: xiaobai-review
ports:
- "0.0.0.0:8765:8765/tcp"
+11 -5
View File
@@ -20,12 +20,18 @@ registry, and verification tools.
- `python tools/backfill_recent_snapshots.py --account <admin> [--lookback 60] [--dry-run]`:
auditable recent trading-day dashboard snapshot backfill. See
`docs/maintenance/行情历史补档.md`.
- `bash tools/build_image.sh <commit> <tag>`: the only sanctioned way to build the
production Docker image. Streams `git archive <commit>` to the deploy host over SSH
(default `moxiaobai@192.168.200.11`), refuses tags that do not end with the commit
- `tools/update_from_main.sh` (deployed to the server as
`~/xiaobai-build/update-from-main.sh`): the server-side update-and-build entry for
the managed local worktree at `/opt/1panel/docker/compose/xiaobaifupan`. Fetches
Gitea `main`, enforces branch/clean/fast-forward checks, builds a
`main-<shortsha>` tagged image with the revision label, and verifies the label
after the build. `tools/xiaobai-git` is the matching git wrapper for that
worktree (`status`/`log`/`diff`).
- `bash tools/build_image.sh <commit> <tag>`: agent-grade entry that streams
`git archive <commit>` to the deploy host over SSH (default
`moxiaobai@192.168.200.11`), refuses tags that do not end with the commit
short SHA, verifies the revision label after the build, and appends a record to
`~/xiaobai-build/BUILD_LOG.tsv` on the host. Building from any server-side working
tree is forbidden; see `DOCKER_DEPLOY.md`. Before building, it runs
`~/xiaobai-build/BUILD_LOG.tsv` on the host. Before building, it runs
`tools/check_deploy_baseline.sh` so the candidate commit must contain the currently
running container's Git revision as an ancestor.
- `bash tools/check_deploy_baseline.sh <commit> [--live-revision <sha>]`: deployment
+110
View File
@@ -0,0 +1,110 @@
#!/usr/bin/env bash
# 小白复盘服务器本地目录安全更新/构建入口(HEL-235B 固化)
# 作用:把 /opt/1panel/docker/compose/xiaobaifupan 的 Git 工作目录安全快进到 Gitea main,
# 校验“本地 HEAD = origin/main = 镜像 revision”后,从本地目录构建带提交号的镜像。
# 禁止:不从 main 构建;不使用不带提交短号的 tag;本地有改动/落后/分叉时一律停止。
# 说明:目录顶层归 root,本脚本用“截断写入”绕开 git 对顶层文件 unlink+重建的权限要求;
# 但 main 新增/删除顶层文件时无法自动处理,会列出需管理员执行的精确清单。
set -euo pipefail
GIT_DIR_PATH="$HOME/xiaobai-build/repos/xiaobai-review.git"
WORK_TREE="/opt/1panel/docker/compose/xiaobaifupan"
IMAGE_REPO="xiaobai-review"
LOG_FILE="$HOME/xiaobai-build/BUILD_LOG.tsv"
MODE="${1:-build}"
g() { git --git-dir="$GIT_DIR_PATH" --work-tree="$WORK_TREE" "$@"; }
refuse() { printf '拒绝:%s\n' "$*" >&2; exit 1; }
[ "$MODE" = "build" ] || [ "$MODE" = "verify-tag" ] || refuse "未知子命令「${MODE}」(可用:build / verify-tag <tag>"
[ -d "$GIT_DIR_PATH" ] || refuse "Git 目录不存在:$GIT_DIR_PATH"
echo "==> 拉取 Gitea origin/main"
g fetch --quiet origin main || refuse "无法连接 Gitea 拉取 origin/main"
echo "==> 检查分支与工作区"
BRANCH="$(g symbolic-ref --short HEAD 2>/dev/null || true)"
[ "$BRANCH" = "main" ] || refuse "当前不在 main 分支(${BRANCH:-detached}),停止"
DIRTY="$(g status --porcelain)"
[ -z "$DIRTY" ] || refuse "本地目录有未提交改动或多余文件,先处理再构建:
$DIRTY"
LOCAL_HEAD="$(g rev-parse HEAD)"
REMOTE_HEAD="$(g rev-parse origin/main)"
if [ "$LOCAL_HEAD" != "$REMOTE_HEAD" ]; then
g merge-base --is-ancestor "$LOCAL_HEAD" "$REMOTE_HEAD" \
|| refuse "本地 main 与 origin/main 历史分叉,停止(未改写工作目录)"
CHANGES="$(g diff --no-renames --name-status HEAD origin/main)"
TOP_AD="$(printf '%s\n' "$CHANGES" | grep -E "^[AD][[:space:]]+[^/]+$" || true)"
[ -z "$TOP_AD" ] || refuse "main 相比本地新增/删除了顶层文件,目录顶层归 root,需管理员执行:
$TOP_AD"
echo "==> 同步 origin/main 文件(顶层文件保留原 inode,避免目录权限限制)"
while IFS=$'\t' read -r status path; do
[ -n "$path" ] || continue
case "$status" in
D)
case "$path" in
*/*) rm -f -- "$WORK_TREE/$path" ;;
*) refuse "main 删除了顶层文件 $path,需管理员处理" ;;
esac
;;
A|M)
MODE_BITS="$(g ls-tree origin/main -- "$path" | awk '{print $1}')"
case "$MODE_BITS" in
100644|100755) ;;
*) refuse "文件 ${path} 的 Git 类型 ${MODE_BITS} 不支持自动同步,需管理员处理" ;;
esac
mkdir -p -- "$(dirname "$WORK_TREE/$path")"
g show "origin/main:$path" > "$WORK_TREE/$path"
[ "$MODE_BITS" = "100755" ] && chmod 755 "$WORK_TREE/$path" || chmod 644 "$WORK_TREE/$path"
;;
*) refuse "遇到未支持的 Git 变更类型 ${status}${path}" ;;
esac
done <<< "$CHANGES"
g read-tree origin/main
g update-ref refs/heads/main "$REMOTE_HEAD" "$LOCAL_HEAD"
DIRTY="$(g status --porcelain)"
[ -z "$DIRTY" ] || refuse "快进后工作区仍不一致,停止:
$DIRTY"
LOCAL_HEAD="$(g rev-parse HEAD)"
fi
[ "$LOCAL_HEAD" = "$REMOTE_HEAD" ] || refuse "本地 HEAD 与 origin/main 不一致,停止"
SHORT="${LOCAL_HEAD:0:7}"
echo "==> 校验通过:本地 HEAD = origin/main = ${LOCAL_HEAD}${SHORT}"
if [ "$MODE" = "verify-tag" ]; then
TAG="${2:?用法: update-from-main.sh verify-tag <tag>}"
[ "$TAG" = "main-${SHORT}" ] \
|| refuse "镜像标签必须是当前 main 对应的 main-${SHORT},收到:${TAG}"
LABEL="$(docker image inspect "${IMAGE_REPO}:${TAG}" \
--format '{{index .Config.Labels "org.opencontainers.image.revision"}}' 2>/dev/null)" \
|| refuse "镜像 ${IMAGE_REPO}:${TAG} 不存在"
[ "$LABEL" = "$LOCAL_HEAD" ] || refuse "镜像 revision${LABEL})与当前 main${LOCAL_HEAD})不一致,禁止部署"
echo "==> 通过:${IMAGE_REPO}:${TAG} 的 revision 与 main 一致,可以部署"
exit 0
fi
TAG="main-${SHORT}"
echo "==> 从本地目录构建 ${IMAGE_REPO}:${TAG}"
docker build --rm -t "${IMAGE_REPO}:${TAG}" \
--label "org.opencontainers.image.revision=${LOCAL_HEAD}" \
--label "org.opencontainers.image.created=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
"$WORK_TREE" 2>&1 | tail -5
echo "==> 回读校验镜像 revision"
GOT="$(docker image inspect "${IMAGE_REPO}:${TAG}" \
--format '{{index .Config.Labels "org.opencontainers.image.revision"}}')"
if [ "$GOT" != "$LOCAL_HEAD" ]; then
docker rmi "${IMAGE_REPO}:${TAG}" >/dev/null 2>&1 || true
refuse "镜像 revision${GOT})与 main${LOCAL_HEAD})不一致,已删除镜像"
fi
IMAGE_ID="$(docker image inspect "${IMAGE_REPO}:${TAG}" --format '{{.Id}}' | cut -c8-19)"
mkdir -p "$(dirname "$LOG_FILE")"
printf '%s\t%s\t%s\t%s\tlocal-worktree\n' \
"$(date '+%F %T')" "${IMAGE_REPO}:${TAG}" "${LOCAL_HEAD}" "${IMAGE_ID}" >> "$LOG_FILE"
cat <<EOF
==> 完成:${IMAGE_REPO}:${TAG}revision=${LOCAL_HEAD}
部署需人工确认,参考 ~/xiaobai-build/README.md 的换版与回滚步骤。
EOF
+6
View File
@@ -0,0 +1,6 @@
#!/usr/bin/env bash
# 查看服务器本地目录 Git 状态的便捷入口:xiaobai-git status / log / diff 等
exec git \
--git-dir="$HOME/xiaobai-build/repos/xiaobai-review.git" \
--work-tree="/opt/1panel/docker/compose/xiaobaifupan" \
"$@"