CI / backend (push) Canceled after 0s
- 0.1–0.9 全部标为完成,但不是"代码写完"就算完成: 33 条测试全绿、ruff/format 通过、真机 uvicorn 冒烟(/health /version 登录 建工作区 列表 /auth/me)通过 之后才敢打勾;没跑过的(downgrade、mypy、CI 在 Gitea 实跑、Web/Android 联调)保持未勾并写明原因。 - 记录 5 处"计划与实际不符"的偏差及理由: JWT 30min/14d、refresh 无状态不可撤销、权限用 ROLE_RANK 而非 PERMISSIONS 字典、 app_user 故意不加 RLS(登录发生在租户绑定之前)、minio 与 pgvector 推迟到 M3。 - 新增「环境现状与阻塞」一节:Docker 起不来用便携 PG、外网 TLS 被拦走 HTTP 镜像、 pip 必须 --no-cache-dir(否则写缓存卡死)、pg_ctl restricted token 报错、fnsvr-01 只有 8418/3306。 - 验收用例表加"现状"列,区分已验证 / 部分 / 未做。 顺带修: - alembic downgrade 之前直接失败:workspace 的 RLS 策略引用了 member, 不先 DROP POLICY 就 drop table,报 "cannot drop table member because other objects depend on it"。 现已按策略先删后删表,downgrade base → upgrade head 全通。 - npm 依赖必须 --ignore-scripts 才能装(沙箱禁止 lifecycle 脚本派生进程), 已写进 DEVELOPMENT.md;web 首次构建成功(tsc -b + vite,177KB/gzip 58KB)。 验收:pytest 33 passed / ruff 干净 / alembic 升降级往返通过 / web build 通过。
117 lines
4.8 KiB
Markdown
117 lines
4.8 KiB
Markdown
# 开发环境(M0)
|
||
|
||
> 目标:新人在 20 分钟内把后端跑起来并让 `pytest` 全绿。
|
||
|
||
## 1. 依赖
|
||
|
||
| 组件 | 版本 | 说明 |
|
||
|---|---|---|
|
||
| Python | 3.10+(CI 用 3.11) | 后端 |
|
||
| Node | 20+ | 前端 |
|
||
| Docker Desktop | 任意近期版本 | 只用来跑 Postgres / Redis |
|
||
|
||
## 2. 端口约定(别改用默认端口)
|
||
|
||
| 服务 | 端口 | 为什么 |
|
||
|---|---|---|
|
||
| PostgreSQL | **5433** | 避开机器上可能已有的 5432 |
|
||
| Redis | **6380** | 同上 |
|
||
| 后端 API | **8100** | 前端 dev 代理指向它 |
|
||
| 前端 dev | **5180** | 与 CORS 默认值一致 |
|
||
|
||
## 3. 起基础设施
|
||
|
||
```bash
|
||
cd deploy
|
||
docker-compose -f docker-compose.dev.yml up -d # 老版 CLI 是 docker-compose,新版是 docker compose
|
||
```
|
||
|
||
首次初始化会自动执行 `deploy/postgres/init/01-roles.sql`,创建两个角色:
|
||
|
||
- `dadao_owner` —— 拥有库、可以建表,**只有 alembic 用**。
|
||
- `dadao_app` —— 应用连接用,无 DDL、无 BYPASSRLS,行级安全对它生效。
|
||
|
||
> 为什么必须两个角色:PostgreSQL 的 RLS 会对表 owner 和 superuser **直接跳过**。
|
||
> 如果应用用 owner 连库,多租户隔离就是摆设,而且测试会"假通过"。
|
||
|
||
## 4. 后端
|
||
|
||
```bash
|
||
cd backend
|
||
python -m venv .venv
|
||
.venv\Scripts\activate # Linux/macOS: source .venv/bin/activate
|
||
pip install -r requirements.txt
|
||
cp .env.example .env # Windows: copy .env.example .env
|
||
|
||
alembic upgrade head # 建表 + RLS 策略(人工复核过再上)
|
||
uvicorn app.main:app --reload --port 8100
|
||
```
|
||
|
||
自检:
|
||
|
||
```bash
|
||
curl http://127.0.0.1:8100/health
|
||
curl http://127.0.0.1:8100/version # commit 必须等于 git rev-parse --short HEAD
|
||
```
|
||
|
||
开发环境验证码固定为 `SMS_DEV_CODE`(默认 `123456`),且 `/auth/sms/send` 会把 `dev_code` 回显在响应里——
|
||
**只有 `ENV=dev` 才允许**,`ENV=prod` 时启动即失败。
|
||
|
||
## 5. 前端
|
||
|
||
```bash
|
||
cd web
|
||
npm install
|
||
npm run dev # http://127.0.0.1:5180,/api 代理到 8100
|
||
npm run build # 产物必须在仓库 HEAD 上构建,禁止本地构建后上传
|
||
```
|
||
|
||
## 6. 测试
|
||
|
||
```bash
|
||
cd backend
|
||
pytest -q # 必须全绿才能提交
|
||
ruff check . && ruff format --check .
|
||
```
|
||
|
||
测试会真连数据库、真跑迁移、真走 HTTP 栈。跑之前确保 Postgres 在 5433。
|
||
|
||
隔离测试(`tests/test_tenant_isolation.py`)是本仓库最重要的一组用例,改动任何租户表都必须重跑它。
|
||
|
||
## 7. 常见坑
|
||
|
||
| 现象 | 原因 | 处理 |
|
||
|---|---|---|
|
||
| `pytest` 报 `attached to a different loop` | 连接池跨事件循环复用 | 测试环境已用 NullPool,别改回去 |
|
||
| 迁移报 `permission denied for schema public` | 用的是应用角色 | `MIGRATION_DATABASE_URL` 必须指向 owner |
|
||
| 接口返回空列表而不是报错 | 忘了 `bind_tenant`,RLS 过滤掉了一切 | 走 `MemberDep` / `TenantDep` 依赖 |
|
||
| `pip` 报 `UnicodeDecodeError: 'gbk' codec` | requirements.txt 里有非 ASCII | 该文件保持纯 ASCII |
|
||
| `docker compose` 报 `unknown command` | 只装了 compose v1 | 用 `docker-compose` |
|
||
| Docker 起不来 | Docker Desktop 未启动 / 需要管理员 | 先启动 Desktop,`docker info` 有输出再继续 |
|
||
| 迁移报 `cannot insert multiple commands into a prepared statement` | asyncpg 走预备语句,一次只能一条 SQL | 多语句 DDL 拆成每语句一次 `op.execute()` |
|
||
|
||
### 本机特例(2026-10 实测,换机器可跳过)
|
||
|
||
这台开发机的 Docker Desktop 起不来,且外网 TLS 被拦,实际用的是:
|
||
|
||
- **便携 PostgreSQL 16.4** 放在 `.local/pg`(已在 `.gitignore`),`initdb` 后用**后台作业**前台跑:
|
||
```powershell
|
||
.\.local\pg\pgsql\bin\postgres.exe -D .\.local\pg\pgdata -p 5433 `
|
||
-c listen_addresses=127.0.0.1 -c fsync=off -c synchronous_commit=off
|
||
```
|
||
不要用 `Start-Process`:父进程一退出,子进程就被带走;`pg_ctl` 在本机会报
|
||
`could not create restricted token: error code 87`。
|
||
- **装包只能走 HTTP 镜像**:PyPI `http://mirrors.aliyun.com/pypi/simple/`,
|
||
npm `http://mirrors.huaweicloud.com/repository/npm/`。
|
||
- **pip 必须加 `--no-cache-dir`**:不加时写缓存会卡死(无输出、CPU 空转,实测 25 分钟无进展)。
|
||
- **安装类命令一律写日志文件再 tail**,不要用管道截断输出,否则看不到进度也不知道卡在哪。
|
||
- `npm install` 要加 `--ignore-scripts`:沙箱不允许 npm 的 lifecycle 脚本派生进程(`EPERM spawn`)。
|
||
|
||
## 8. 提交前自检清单
|
||
|
||
1. `pytest` 全绿,`ruff` 无告警。
|
||
2. 新增租户表时:`workspace_id NOT NULL` + RLS 策略 + 跨租户测试,三者齐全。
|
||
3. 迁移文件提交前人工复核(`alembic revision --autogenerate` 只做草稿)。
|
||
4. 改了接口就更新 `2.Design/API.md`(M1 建立该文档)。
|
||
5. 推送到远端,生产部署后 `curl /version` 校验 commit。
|