* LICENSE —— MIT
* THIRD-PARTY-NOTICES —— 依赖清单、再分发合规说明(含随仓库分发的
Apache ECharts 5.6.0 / Apache-2.0)与自查清单
* CONTRIBUTING.md —— 开发环境、五层验证、必须遵守的不变量、提交规范
* SECURITY.md —— 漏洞私有报告渠道、已有措施、已知非目标
* CODE_OF_CONDUCT.md —— 改编自 Contributor Covenant 2.1
* .github/ —— Bug 报告 / 功能建议表单 + PR 模板
* .editorconfig —— 与 .gitattributes 保持一致
* 全部 Python / Shell 源文件加 SPDX-License-Identifier: MIT 头
* README 增加「开源与许可」章节与许可标识
113 行
5.9 KiB
Markdown
113 行
5.9 KiB
Markdown
# 贡献指南(Contributing)
|
||
|
||
感谢你有兴趣改进 WorkBuddy Portal。这是一个**单进程 Flask + SQLite** 的轻量项目,
|
||
刻意保持了很小的依赖面与很平的目录结构——请先花两分钟读完本文,你的改动会更顺利被接受。
|
||
|
||
---
|
||
|
||
## 一、开发环境
|
||
|
||
| 项 | 要求 |
|
||
|---|---|
|
||
| Python | **3.13**(Docker 镜像用的就是 3.13-slim;3.11+ 一般也可) |
|
||
| 操作系统 | Linux / macOS / Windows 均可,但**注意**下面「Windows 特有坑」一节 |
|
||
| 依赖 | `pip install -r requirements.txt` |
|
||
| 可选 | Playwright + Chromium(只有跑界面截图才需要) |
|
||
|
||
起步:
|
||
|
||
```bash
|
||
git clone <你的仓库地址> workbuddy-portal
|
||
cd workbuddy-portal
|
||
python -m venv .venv && . .venv/bin/activate # Windows: .venv\Scripts\activate
|
||
pip install -r requirements.txt
|
||
|
||
python manage.py init # 建表 + 建管理员(默认 admin/admin123)
|
||
python manage.py serve --port 8848 # 或 docker compose up -d --build
|
||
```
|
||
|
||
## 二、用示例数据开发,别用真实数据
|
||
|
||
`tools/demo_data.py` 会生成一份**完全合成**的数据集(假模型名、假 Prompt、偏斜的积分分布),
|
||
放在 `data/demo/` 下(该目录已在 `.gitignore` 内):
|
||
|
||
```bash
|
||
python tools/demo_data.py # 默认 data/demo,管理员 admin/admin123
|
||
WB_DATA_DIR=$PWD/data/demo python manage.py serve --port 8849 --no-scheduler
|
||
```
|
||
|
||
这样你既能有一个「看起来像真的」的界面来调试,也不会把任何真实用量带进仓库或截图。
|
||
|
||
## 三、改动前请先跑一遍验证
|
||
|
||
项目有一层层递进的验证,**代价从低到高**,改完至少跑到第 2 层:
|
||
|
||
| # | 命令 | 覆盖什么 | 需要什么 |
|
||
|---|---|---|---|
|
||
| 1 | `python -m compileall -q workbuddy_portal manage.py tools` | 语法 | — |
|
||
| 2 | `python tools/smoke.py` | **99 项**离线断言:全页面只读渲染、模板残留检测、历史缺陷防回归、静态资源、CSS 类名对账 | 无(用 Flask test_client,不启服务) |
|
||
| 3 | `python tools/check_live.py --base http://127.0.0.1:8848` | **56 项**真实 HTTP 断言,含登录/CSRF/开放重定向 | 一个运行中的服务 |
|
||
| 4 | `python tools/shots.py --base http://127.0.0.1:8849` | 登录后逐页截图并收集 `console`/`pageerror` | Playwright + Chromium |
|
||
| 5 | `docker compose up -d --build && docker compose ps` | 容器化路径 | Docker |
|
||
|
||
> `tools/shots.py` 是**最有价值的一层**:项目曾经出过「大屏整页全白」的 bug,
|
||
> 只有它抓到了(`smoke` 与 `check_live` 都放过了)。改前端务必跑。
|
||
|
||
这三支脚本都会打印 `RESULT: ok=N fail=0`,`fail` 不为 0 时退出码是 1,可直接接进 CI。
|
||
|
||
## 四、必须遵守的几条不变量
|
||
|
||
这些是踩过坑之后定下来的,破坏它们会在生产上以很隐蔽的方式出问题:
|
||
|
||
1. **SQLite 只允许一个写者。** 任何采集动作都要走 `collect._Lock()`(`data/collect.lock`)。
|
||
不要起多个带调度的进程;多实例部署时其余实例设 `WB_DISABLE_SCHEDULER=1`。
|
||
2. **列表类接口默认不返回 `prompt` 全文。** 它占原始体积约 80%。只有 `/api/top` 与
|
||
`/api/records` 带,且都做截断。新增接口请沿用这个约定。
|
||
3. **两种字段命名契约不要互相「统一」**:`/api/bundle` 用短键(`d/c/k/m/cl/t/px`,大屏页依赖),
|
||
`/api/records` 用可读全名。改错会让大屏静默渲染成空白。
|
||
4. **流式响应里不要复用 `db.get_db()`。** Flask 在响应迭代开始前就会关掉请求上下文里的连接,
|
||
生成器一读库就 `Cannot operate on a closed database`。要在生成器内部自建连接并 `finally` 关闭。
|
||
5. **Docker 部署用命名卷,不要退回绑定挂载。** Windows + Docker Desktop 走 9p,
|
||
宿主进程碰过 WAL 库之后容器会永久打不开数据库(详见 [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md))。
|
||
6. **`.gitignore` 不支持行尾注释**——规则后跟 `# 注释` 会让整行失效。注释必须单独占一行,
|
||
改完用 `git check-ignore -v <file>` 逐条确认命中。
|
||
|
||
## 五、代码风格
|
||
|
||
- 遵循 PEP 8;行宽 100。
|
||
- **注释与文档字符串用中文**,说明「为什么这么做」而不是「这行在做什么」。
|
||
- 提交前用 `python -m compileall` 与 `python tools/smoke.py` 自查。
|
||
- 不要引入新的第三方依赖,除非有充分理由并在 PR 里说明——这个项目的卖点之一就是依赖少。
|
||
如果确实新增了,请同步登记到 [THIRD-PARTY-NOTICES.md](THIRD-PARTY-NOTICES.md)。
|
||
|
||
## 六、提交与 PR
|
||
|
||
提交信息用 **Conventional Commits**,一句话说清「改了什么」即可,正文可写动机:
|
||
|
||
```
|
||
fix(docker): 数据改用 Docker 命名卷,修容器打不开数据库的问题
|
||
feat(api): 新增 /api/export 支持按筛选条件导出 CSV
|
||
docs: 补充反向代理部署示例
|
||
```
|
||
|
||
PR 请包含:
|
||
|
||
- **动机**:解决什么问题,或复现步骤
|
||
- **改动**:涉及哪些文件、有没有破坏性变更
|
||
- **验证**:贴出 `smoke` / `check_live` 的 `RESULT` 行;改前端再贴截图脚本的输出
|
||
- 若改动影响数据口径(聚合、去重、时区),请**额外写一份独立聚合与之比对**,
|
||
不要只靠肉眼看页面
|
||
|
||
## 七、Windows 特有坑(若你在 Windows 上开发)
|
||
|
||
- 宿主 Windows 进程访问过 `data/usage.sqlite` 后,**容器内**会打不开同一个库。
|
||
用命名卷部署时,宿主侧跑 CLI 请一律走 `docker compose exec portal python manage.py …`。
|
||
- Git 的 `/tmp` 会被解析成 `C:\tmp`,`git commit -F /tmp/msg.txt` 会失败——用仓库内路径。
|
||
- 本机若开着 HTTP 代理,`curl http://127.0.0.1:…` 会被代理拦成 502,探测本地服务要加 `--noproxy '*'`。
|
||
- 行尾:仓库用 `.gitattributes` 锁死 `eol=lf`,`docker/*.sh` 若带 CRLF 会在容器里报
|
||
`exec format error`。
|
||
|
||
---
|
||
|
||
有任何不确定的地方,先在 Issue 里问,比写完再返工更省事。
|