fix(pr-watch): 試跑的預告與實跑的守門對齊,並說出路徑被佔住這件事
review 抓到一處落差:試跑判「終止且工作樹乾淨」就預告 git worktree remove, 但實跑多判一件事——那條路徑上的東西是不是真的一棵工作樹。別的 clone 在同一條路徑上 留下目錄時(路徑由 owner/repo/分支名 推導,不含本機 clone 的位置),試跑會預告一行 實跑必然拒絕的指令,而回報裡完全看不出原因。 判斷收進 lib 的 inspectWorktree,由它直接給出 reason(missing/foreign/dirty/ removable):試跑與實跑、自動與手動四條路徑從此擋在同一個判斷上,worktree-remove 裡 那份重算的副本也跟著刪掉。回報多一個「是工作樹」欄位,手動出口的 NOT_A_WORKTREE 不再是使用者第一次聽到這件事。 順帶兩件同源的修正: - 「是不是工作樹」改認 .git 為**檔案**。獨立 clone 的 .git 是目錄,先前會被當成工作樹, 然後在 git worktree remove 那一步炸出一句原始錯誤。 - 兩支腳本的 --dry-run 請求預告收進 pr-threads 的 plannedRequests,並補上 pr-watch 先前漏掉的那句說明(逐則 reaction 與逐個 review 的行內留言事前列不完)。預告與實際 發出的請求分開寫,加一個端點就會有一邊忘了改。 - PR 讀不到 head 分支時給出 PULL_HEAD_MISSING,而不是拿空字串去推導一條路徑。 議題 #41 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+34
-1
@@ -223,6 +223,33 @@ test('工作樹早就不在時不當成失敗,也不說自己清了', async (t
|
||||
assert.equal(json.data.suggestedAction, 'nothing-to-do');
|
||||
});
|
||||
|
||||
test('路徑上是別的 clone 留下的東西時,說出來而不是靜靜跳過', async (t) => {
|
||||
const { repo, run, worktree } = await withScene(t, { state: 'closed', merged: true });
|
||||
repo.git('worktree', 'remove', worktree);
|
||||
mkdirSync(join(worktree, '.git'), { recursive: true });
|
||||
|
||||
const { code, json } = await run();
|
||||
|
||||
assert.equal(code, 0);
|
||||
assert.equal(json.data.工作樹.是工作樹, false, '.git 是目錄的是獨立 clone,不是工作樹');
|
||||
assert.equal(json.data.cleaned, false);
|
||||
assert.equal(json.data.suggestedAction, 'cleanup', '要人動手,而手動出口會說出確切的原因');
|
||||
assert.equal(existsSync(join(worktree, '.git')), true, '不是我們建的東西就不碰');
|
||||
});
|
||||
|
||||
test('清不掉的路徑,--dry-run 不會預告一行實跑會拒絕的指令', async (t) => {
|
||||
// 試跑印得出漂亮的計畫、實跑卻被擋下來,是最難查的那種落差
|
||||
const { repo, run, worktree } = await withScene(t, { state: 'closed', merged: true });
|
||||
repo.git('worktree', 'remove', worktree);
|
||||
mkdirSync(worktree, { recursive: true });
|
||||
writeFileSync(join(worktree, '別人的東西.txt'), 'x\n');
|
||||
|
||||
const { json } = await run(['--dry-run']);
|
||||
|
||||
assert.deepEqual(json.data.commands, []);
|
||||
assert.equal(json.data.工作樹.是工作樹, false);
|
||||
});
|
||||
|
||||
// ── 回報內容 ───────────────────────────────────────────────────────
|
||||
|
||||
test('回報內容含 PR 狀態、未處理留言數與工作樹現況', async (t) => {
|
||||
@@ -234,7 +261,13 @@ test('回報內容含 PR 狀態、未處理留言數與工作樹現況', async (
|
||||
assert.equal(json.data.url, `https://gitea.jsc.idv.tw/${REPO}/pulls/${INDEX}`);
|
||||
assert.equal(json.data.branch, BRANCH, '工作樹是從 PR 的 head 分支推導的,要說出用的是哪一支');
|
||||
assert.equal(json.data.未處理留言數, 1);
|
||||
assert.deepEqual(json.data.工作樹, { 路徑: worktree, 存在: true, 有未提交變更: false, 檔案: [] });
|
||||
assert.deepEqual(json.data.工作樹, {
|
||||
路徑: worktree,
|
||||
存在: true,
|
||||
是工作樹: true,
|
||||
有未提交變更: false,
|
||||
檔案: [],
|
||||
});
|
||||
});
|
||||
|
||||
test('建議動作是固定的列舉值,呼叫端才能程式化判斷', async (t) => {
|
||||
|
||||
Reference in New Issue
Block a user