pr: commit worktree changes before push; fix merge/pr order docs

POST /tasks/{id}/pr used to push the branch as-is, so uncommitted
worktree changes never reached the PR (empty diff). Now, after all
skip checks pass, the endpoint commits the worktree on host/<task>
(guarded: refuses when the worktree is on another branch), cleans
__pycache__/*.pyc like host.py merge does, and falls back to the
cloud identity when no git identity is configured. Nothing-to-commit
proceeds. Docs corrected: PR comes after status-merge and before
any local git merge (which deletes branch+worktree).
This commit is contained in:
host committed 2026-10-26 15:20:02 +08:00
1 parent b984171f27
commit 0b96b2929c
5 files changed
+263 -10

No files matched your search

+83 -3
View File
@@ -13,9 +13,23 @@ host/<host 任务名>,worktree 在 <仓库根>/work/host-worktrees/
4. 本机没有 gh 二进制 -> pr_skipped, no_gh
5. gh 未登录(gh auth status 非零) -> pr_skipped,
gh_not_authenticated
只有以上全过才 push 分支 + gh pr create;push 或 pr create
失败时抛 PrStepError(调用方据此返回 502),任务状态保持
merged 不变,绝不把任务标 failed。
只有以上全过才提交 worktree 改动、push 分支 + gh pr create;
push 或 pr create 失败时抛 PrStepError(调用方据此返回 502),
任务状态保持 merged 不变,绝不把任务标 failed。
提交规则(create_task_pr 在全部跳过检查通过后、push 之前做):
worktree 必须正好在分支 host/<任务名> 上,否则直接失败,
绝不在别的分支上提交;先清掉 worktree 内的 __pycache__ /
*.pyc(与 host.py merge 同规则,只动 worktree,不动主仓),
再 git add -A + commit;nothing to commit 时直接继续。
身份用环境里的 git 身份(与 host.py merge 一致),缺身份时
回退到 cloud/cloud@local 再试一次。
顺序约束(与云端 merge 接口配套):任务 completed -> 调
POST /tasks/{id}/merge 改状态 -> 调 POST /tasks/{id}/pr 推分支
建 PR -> 再在 GitHub 上合 PR 或本地 host.py merge。本地
host.py merge 成功后会删分支和 worktree,此后调 PR 会跳过
no_worktree,所以 PR 必须在本地 git merge 之前做。
worker 不自动推:自动 PR 只由显式调 POST /tasks/{id}/pr
触发。默认自动推会把测试任务也推到远端,本模块不做。
@@ -118,6 +132,68 @@ def check_pr_ready(worktree_dir, git_runner=None, gh_which=None,
return True
def _commit_worktree_changes(worktree_dir, branch, task_id,
git_runner=None):
"""push 之前把 worktree 的改动提交到分支上。
顺序:先确认 worktree 正好在目标分支上(不在就直接失败,
绝不在别的分支上提交,commit 到 main 里就污染主分支了);
再清掉 worktree 内的 __pycache__ / *.pyc(可再生的缓存,
与 host.py merge 同规则);再 git add -A + commit。
nothing to commit 时直接返回;缺 git 身份时回退到
cloud/cloud@local 再试一次;其它失败抛 PrStepError(commit)。
"""
cur = _run(["git", "-C", worktree_dir, "branch", "--show-current"],
cwd=worktree_dir, runner=git_runner)
if (getattr(cur, "stdout", "") or "").strip() != branch:
raise PrStepError(
"commit", "worktree 不在分支 %s 上,拒绝提交" % branch)
for root, dirs, files in os.walk(worktree_dir):
if ".git" in dirs:
dirs.remove(".git")
if "__pycache__" in dirs:
shutil.rmtree(os.path.join(root, "__pycache__"),
ignore_errors=True)
dirs.remove("__pycache__")
for fn in files:
if fn.endswith(".pyc"):
try:
os.remove(os.path.join(root, fn))
except OSError:
pass
add = _run(["git", "-C", worktree_dir, "add", "-A"],
cwd=worktree_dir, runner=git_runner)
if getattr(add, "returncode", 1) != 0:
raise PrStepError(
"commit", "git add 失败:%s" % (
(getattr(add, "stderr", "") or "")
or (getattr(add, "stdout", "") or "")))
msg = "cloud: %s" % task_id
c = _run(["git", "-C", worktree_dir, "commit", "-m", msg],
cwd=worktree_dir, runner=git_runner)
if getattr(c, "returncode", 1) == 0:
return
out = ((getattr(c, "stdout", "") or "")
+ (getattr(c, "stderr", "") or ""))
if "nothing to commit" in out:
return
if ("tell me who you are" in out or "user.name" in out
or "user.email" in out):
c2 = _run(["git", "-C", worktree_dir,
"-c", "user.name=cloud", "-c", "user.email=cloud@local",
"commit", "-m", msg],
cwd=worktree_dir, runner=git_runner)
if getattr(c2, "returncode", 1) == 0:
return
out2 = ((getattr(c2, "stdout", "") or "")
+ (getattr(c2, "stderr", "") or ""))
if "nothing to commit" in out2:
return
raise PrStepError(
"commit", "git commit 失败(已试 cloud 身份):%s" % out2[:300])
raise PrStepError("commit", "git commit 失败:%s" % out[:300])
def create_task_pr(task_id, auto_pr, worktrees_root=None,
git_runner=None, gh_runner=None, gh_which=None):
"""给一个已 merged 的云端任务建 PR。
@@ -125,6 +201,8 @@ def create_task_pr(task_id, auto_pr, worktrees_root=None,
auto_pr 不为 1 时直接抛 PrSkipped(auto_pr_disabled),调用方
在查 worktree 之前就应先查项目 auto_pr——本函数也再查一次,
防止直接调用者漏查。成功返回 {pr_url, pr_number, branch}。
全部跳过检查通过后,先把 worktree 改动提交到分支再 push,
否则未提交的改动进不了 PR。
"""
if auto_pr != 1:
raise PrSkipped(_SKIP_AUTO_PR)
@@ -134,6 +212,8 @@ def create_task_pr(task_id, auto_pr, worktrees_root=None,
branch = "host/" + host_name
check_pr_ready(worktree_dir, git_runner=git_runner,
gh_which=gh_which, gh_runner=gh_runner)
_commit_worktree_changes(worktree_dir, branch, task_id,
git_runner=git_runner)
push = _run(["git", "-C", worktree_dir, "push", "origin", branch],
cwd=worktree_dir, runner=git_runner)
if getattr(push, "returncode", 1) != 0: