feat: 命令台云端环境——选、建、管 + 任务绑定环境

环境是项目仓库的命名基准检出(项目 + 仓库快照 + 分支 + 执行器),
不是官方 Codex 云端环境(无公开调用口,不宣称能建它)。
- DB:environments 表(creating/ready/failed/archived,部分唯一索引
  保证同项目未归档名唯一、归档名可重用)+ tasks.environment_id,
  server/worker/db 三处迁移同步(含 #67 的 goal_mode,不丢新语义)。
- API:POST/GET /environments、GET /environments/{id}、
  POST /environments/{id}/archive;任务创建选环境须本项目已 ready、
  执行器一致或继承、cloud 执行器拒绝。
- Worker:run_all 每轮开头准备 creating 环境(真实 git clone +
  checkout 分支,幂等、无 preparing 卡死态,准备期归档 CAS 不覆盖);
  环境任务派发时 task[repo] 指到环境检出(租户 Docker 容器内路径 +
  envs 整目录挂载),任务仍一任务一 worktree。
- Console:环境卡(列表/新建/归档,未 ready 置灰)+ 新建任务环境下拉
  + 任务列表 [环境] 标记;公网 nginx 须同批代理 /environments。
- 单测:新增 test_task_environments.py 14 个;本地实弹(真实 git
  clone octocat/Hello-World,不跑模型)项目→环境→任务→归档全通。
This commit is contained in:
host committed 2026-10-27 04:07:54 +08:00
1 parent c86b450851
commit 3d45edc8b4
6 files changed
+1224 -11

No files matched your search

+48
View File
@@ -782,6 +782,54 @@ interrupted 且终态任务 cancel 返回 400、心跳后 sweep 不扫刚心跳
任务仍需 worker 认领执行;`CloudExecutor` 仍是占位符;公网 https
入口仍需部署侧反向代理 + TLS。
## 云端环境(选、建、管)
环境是项目仓库的一个命名基准检出,不是官方 Codex 的云端环境
(那是官方产品自己的后端,本控制面没有它的公开调用口,不宣称
能建它)。本控制面的环境管的是主机/Docker 这边的基准:项目 +
仓库快照 + 分支 + 执行器,任务选了环境后仍是一任务一 worktree
(host.py 从环境检出 HEAD 为任务建分支),任务隔离、合并、提 PR
全都不变。
- API:`POST /environments`(project_id、name 必填,branch 可选
空=默认分支,executor 可选 local/docker;建完 status=creating)、
`GET /environments?project_id=`(默认排除已归档,
`include_archived=1` 才含归档)、`GET /environments/{id}`、
`POST /environments/{id}/archive`。写操作(建/归档)要求项目
create_task 权限,读要求 read 权限。
- 创建规则:项目没有 repo_url 时 400(没有仓库就没有环境);
同项目未归档环境名唯一(409,归档过的名字可重用,部分唯一
索引保证);分支名只允许字母数字与 `_ . - /`,不以 `-` 开头、
不含 `..`、不以 `.lock` 结尾;executor 只接受 local/docker。
- 准备:worker 在 `run_all` 每轮开头准备所有 creating 的环境:
目录不存在时 `git clone`,存在时校验 git 仓库且 origin 与环境
repo_url 一致再 `fetch`,分支非空时 `checkout` 该分支(不存在
则失败,绝不自动建分支),成功变 ready、失败变 failed 并写
error_text。准备本身幂等,没有 preparing 中间态——worker 中途
死了、环境仍是 creating 时下一轮会重新准备;准备期间用户归档
了环境时 CAS 写回改不到行,状态保持 archived。
- 任务绑定:`POST /tasks` 接受可选 `environment_id`,必须是本项目
已 ready 的环境(creating/failed/archived 一律 400 并写明状态);
环境 executor 非空且任务 executor 已指定时必须一致,任务
executor 未指定时继承环境 executor(认领过滤靠它);cloud
执行器没有环境检出路径,一律拒绝。worker 派发时环境任务不走
项目仓库路径,直接把 `task["repo"]` 指到环境检出(租户 Docker
为容器内路径 `/var/lib/codex-app-server/envs/<id>`,租户挂载里
整目录挂载该项目 envs 目录;非租户为 `repos_dir/envs/<id>`)。
- 管理:归档幂等(已归档再归档返回 200);有 queued/running
任务正在用环境时归档 409;归档只改状态,不删检出目录(已完成
任务的 worktree 独立,检出留着可查,磁盘清理另行手动做)。
重试(retry)只改任务状态,environment_id 原样保留;会话分支
(fork)不继承 environment_id——分支是 thread/worktree 分叉,
不是在环境上重开任务。
- 命令台:项目卡下方新增“环境”卡(列表/新建/归档,creating→
ready 的过程靠 10 秒自动刷新可见,失败原因直接显示在列表里);
新建任务栏新增环境下拉(未 ready 的置灰、不可选),任务列表里
带环境任务会标 `[环境 <短 id>]`。公网 nginx 若要让页面调通环境
接口,须把 `/environments` 同批代理到双机上游(与 `/console`、
`/tasks`、`/projects`、`/workers`、`/health` 同款,否则页面能打开
但环境区永远加载失败)。
## 运维批(本轮新增:部署、恢复演练、监控、心跳、多用户验证)
细则在 `deploy/README.md`,这里只记代码侧的接口与默认值: