feat(repos): 仓库接入预检 + codexsvc deploy key 配给脚本

本机 https 到 GitHub 常被出口策略挡、ssh 才通(2026-10-30
实测),而新项目填 https 私有仓会在 worker 里花几分钟超时
才失败。控制面不存仓库凭证的设计不动,缺的是建项目前的
快速预检与按仓库通用化的配 key 流程:

- cloud.repos.check_repo_access:只跑 git ls-remote HEAD,
  不 clone、不写盘、不改库;失败返回 (False, 原因),https
  失败时追加改走 git@ 形的提示;CLI python3 -m cloud.repos
  check <url>,退出码 0/1,只打印结果与原因。
- deploy/provision_codexsvc_key.sh:主机上以 root 跑,为
  codexsvc 生成/复用 ed25519 key(绝不覆盖已有 key),只
  打印公钥(私钥绝不打印),--check 时以 codexsvc 身份跑
  ls-remote,通了才算配好。
- 内层沙箱边界诚实说明:Docker 任务内层本就是
  danger-full-access(见 sandbox.py 注释),外层 Docker/gVisor
  才是防线;宿主级 bwrap workspace-write 的 fresh proc 在嵌套
  pidns 下是内核 EPERM、/tmp 不隔离——这些是上游 codex 二进制
  与内核的边界,本批不谎称在仓库代码里修掉,只把仓库接入
  这一档做成可重复流程。
单测:test_project_repo 20 项全过(新增 check 5 项)。
This commit is contained in:
host committed 2026-10-30 01:39:46 +08:00
1 parent b306b56517
commit 1ff8906aee
3 files changed
+173 -2

No files matched your search

+72 -2
View File
@@ -16,11 +16,20 @@
工作树脏不脏由 host.py 的 worktree 逻辑处理。
- 私有仓库 clone 失败时错误文本写明:需要仓库访问凭证,控制面
不存仓库凭证——绝不尝试用任何 key 去 clone。
- check_repo_access:建项目/建任务前的快速预检,只跑
`git ls-remote <url> HEAD`,不 clone、不写盘、不改库;
失败时返回 (False, 原因),成功时返回 (True, HEAD 哈希)。
本机 https 到 GitHub 常被出口策略挡、ssh 才通(2026-10-30
实测),预检失败的 https 私有仓应先改走 git@ 形再测,
不要等任务派发后在 worker 里花几分钟超时才发现。
CLI:`python3 -m cloud.repos check <url>`,退出码 0/1,
只打印结果与原因,绝不打印任何凭证。
"""
import os
import re
import subprocess
import sys
from urllib.parse import urlparse
__all__ = [
@@ -28,6 +37,7 @@ __all__ = [
"validate_repo_url",
"checkout_dir",
"resolve_repo",
"check_repo_access",
]
@@ -89,8 +99,9 @@ def checkout_dir(repos_dir, project_id):
return path
def _default_runner(cmd):
return subprocess.run(cmd, capture_output=True, text=True)
def _default_runner(cmd, timeout=None):
return subprocess.run(cmd, capture_output=True, text=True,
timeout=timeout)
def _run(runner, cmd):
@@ -148,3 +159,62 @@ def resolve_repo(repo_url, repos_dir, project_id, runner=None):
"git fetch origin 失败,拒绝用旧代码派发: %s"
% (err or target))
return target
def check_repo_access(repo_url, runner=None, timeout=30):
"""预检仓库是否可读:只跑 `git ls-remote <url> HEAD`。
返回 (ok, detail):ok 时 detail 为 HEAD 哈希;失败时
detail 为原因(不含凭证)。runner 缺省走真实 git,超时
由 subprocess 的 timeout 控(假 runner 不接受 timeout 时
_run 已回退);runner 抛 TimeoutExpired 时同样返回失败,
不抛异常——预检失败绝不能炸掉调用方。
https 形在主机上失败时 detail 末尾追加 ssh 形提示,
因为本机 https 到 GitHub 常被出口策略挡、ssh 才通。
"""
try:
url = validate_repo_url(repo_url)
except ValueError as exc:
return (False, str(exc))
run = runner if runner is not None else _default_runner
def _ls_remote(cmd):
try:
return run(cmd, timeout=timeout)
except TypeError:
return run(cmd)
try:
result = _ls_remote(["git", "ls-remote", url, "HEAD"])
except Exception as exc: # noqa: BLE001 - 超时、缺 git 等一律失败
detail = "git ls-remote 失败: %s" % (exc,)
if url.startswith("https://"):
detail += ";本机 https 到 GitHub 常被出口策略挡,"
detail += "请改走 git@ 形再测"
return (False, detail)
if getattr(result, "returncode", 1) != 0:
err = (getattr(result, "stderr", "") or "").strip()
detail = "git ls-remote 失败: %s" % (err or url)
if url.startswith("https://"):
detail += ";私有仓库请改走 git@ 形并给 codexsvc 配"
detail += "只读 deploy key,控制面不存仓库凭证"
return (False, detail)
head = (getattr(result, "stdout", "") or "").split()
return (True, head[0] if head else "")
def _main(argv):
if len(argv) != 3 or argv[1] != "check":
sys.stderr.write(
"用法: python3 -m cloud.repos check <repo_url>\n")
return 2
ok, detail = check_repo_access(argv[2])
if ok:
print("ok HEAD=%s" % detail)
return 0
print("fail: %s" % detail)
return 1
if __name__ == "__main__":
raise SystemExit(_main(sys.argv))