fix: withAskpass 等待非同步 callback 完成才清理 askpass 腳本
commitAndPush 傳入 async callback,但 withAskpass 是同步 try/finally, 會在第一個 await(LLM 合併指令檔)時就刪除 .git-askpass.sh,導致後續 git push 因 GIT_ASKPASS 指向已刪除腳本而失敗(cannot exec .git-askpass.sh / could not read Username)。前置驗證的 verifyRemoteAccess 用同步 callback 所以 ls-remote 通過,造成前置驗證過但 push 失敗的落差。 改為當 callback 回傳 thenable 時以 result.finally(cleanup) 延後清理, 同步 callback 維持立即清理與原樣回傳,不影響 verifyRemoteAccess / cloneRepo。 新增回歸測試斷言 git push 執行當下 askpass 腳本仍存在。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+14
-3
@@ -62,11 +62,22 @@ function withAskpass(workspace, fn) {
|
||||
const askpassScript = path.join(workspace, '.git-askpass.sh');
|
||||
fs.writeFileSync(askpassScript, '#!/bin/sh\necho "$GIT_TOKEN"\n', { mode: 0o700 });
|
||||
const credEnv = { ...process.env, GIT_ASKPASS: askpassScript, GIT_USERNAME: 'x-token', GIT_TOKEN: GITEA_TOKEN };
|
||||
const cleanup = () => { try { fs.unlinkSync(askpassScript); } catch {} };
|
||||
let result;
|
||||
try {
|
||||
return fn(credEnv);
|
||||
} finally {
|
||||
try { fs.unlinkSync(askpassScript); } catch {}
|
||||
result = fn(credEnv);
|
||||
} catch (e) {
|
||||
cleanup();
|
||||
throw e;
|
||||
}
|
||||
// Defer cleanup until an async callback settles, otherwise the askpass script
|
||||
// is deleted at the first `await` and later network ops (e.g. git push) fail
|
||||
// with "cannot exec .git-askpass.sh". Sync callbacks clean up immediately.
|
||||
if (result && typeof result.then === 'function') {
|
||||
return result.finally(cleanup);
|
||||
}
|
||||
cleanup();
|
||||
return result;
|
||||
}
|
||||
|
||||
function readGitOutput(run, args, cwd, env) {
|
||||
|
||||
@@ -93,6 +93,18 @@ describe('commitAndPush', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('keeps the askpass script present while the network push runs', async () => {
|
||||
let askpassExistsAtPush = null;
|
||||
const spawn = makeSpawn({
|
||||
push: (_args, opts) => {
|
||||
askpassExistsAtPush = !!(opts?.env?.GIT_ASKPASS && fs.existsSync(opts.env.GIT_ASKPASS));
|
||||
return { status: 0, stdout: '', stderr: '', error: null };
|
||||
},
|
||||
});
|
||||
await commitAndPush(workspace, path.join(workspace, 'repo'), spawn, sourceRoot);
|
||||
assert.equal(askpassExistsAtPush, true, 'askpass script must still exist when git push runs');
|
||||
});
|
||||
|
||||
it('cleans up askpass script after successful run', async () => {
|
||||
await commitAndPush(workspace, path.join(workspace, 'repo'), makeSpawn(), sourceRoot);
|
||||
const leftover = fs.readdirSync(workspace).filter(f => f.endsWith('.git-askpass.sh'));
|
||||
|
||||
Reference in New Issue
Block a user