Skip to content

Commit 41cdeed

Browse files
adityamaruclaude
andauthored
fix(blacksmith-cache): disable auto-gc during fetch and clone to prevent unmount race (#23)
git fetch and clone are porcelain commands that trigger gc --auto internally after completing. Since gc.autoDetach defaults to true, this spawns a background daemon holding cwd and mmap'd pack files on the mirror mount, blocking the subsequent umount with EBUSY even though our explicit runMirrorGC() uses gc.autoDetach=false. Disable auto-gc during fetch/clone with gc.auto=0 to eliminate the race. This makes runMirrorGC() the single place gc runs, with proper timeout and error handling. Fixes unmount failures in post-checkout cleanup (BLA-3202). Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
1 parent fb9e5da commit 41cdeed

2 files changed

Lines changed: 20 additions & 0 deletions

File tree

dist/index.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,9 +294,12 @@ function ensureMirror(mirrorPath_1, repoUrl_1, authToken_1) {
294294
core.info(`[git-mirror] Removing partial mirror directory from failed attempt`);
295295
yield fs.promises.rm(mirrorPath, { recursive: true, force: true });
296296
}
297+
// gc.auto=0: disable auto-gc during clone (see comment in refreshMirror)
297298
const cloneArgs = [
298299
'-c',
299300
`${configKey}=${configValue}`,
301+
'-c',
302+
'gc.auto=0',
300303
'clone',
301304
'--mirror',
302305
'--progress',
@@ -333,9 +336,16 @@ function refreshMirror(mirrorPath_1, repoUrl_1, authToken_1) {
333336
const { configKey, configValue } = getAuthConfigArgs(repoUrl, authToken);
334337
const gitEnv = buildGitEnv(verbose);
335338
yield retryHelper.execute(() => __awaiter(this, void 0, void 0, function* () {
339+
// gc.auto=0: disable git's internal auto-gc that porcelain commands like
340+
// fetch run after completing. Without this, fetch can spawn a background
341+
// gc daemon (gc.autoDetach defaults to true) that holds cwd + mmap'd pack
342+
// files on the mirror mount, causing the subsequent umount to fail with
343+
// EBUSY. We run gc explicitly in runMirrorGC() with gc.autoDetach=false.
336344
const fetchArgs = [
337345
'-c',
338346
`${configKey}=${configValue}`,
347+
'-c',
348+
'gc.auto=0',
339349
'-C',
340350
mirrorPath,
341351
'fetch',

src/blacksmith-cache.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,9 +322,12 @@ export async function ensureMirror(
322322
)
323323
await fs.promises.rm(mirrorPath, {recursive: true, force: true})
324324
}
325+
// gc.auto=0: disable auto-gc during clone (see comment in refreshMirror)
325326
const cloneArgs = [
326327
'-c',
327328
`${configKey}=${configValue}`,
329+
'-c',
330+
'gc.auto=0',
328331
'clone',
329332
'--mirror',
330333
'--progress',
@@ -372,9 +375,16 @@ export async function refreshMirror(
372375
const {configKey, configValue} = getAuthConfigArgs(repoUrl, authToken)
373376
const gitEnv = buildGitEnv(verbose)
374377
await retryHelper.execute(async () => {
378+
// gc.auto=0: disable git's internal auto-gc that porcelain commands like
379+
// fetch run after completing. Without this, fetch can spawn a background
380+
// gc daemon (gc.autoDetach defaults to true) that holds cwd + mmap'd pack
381+
// files on the mirror mount, causing the subsequent umount to fail with
382+
// EBUSY. We run gc explicitly in runMirrorGC() with gc.autoDetach=false.
375383
const fetchArgs = [
376384
'-c',
377385
`${configKey}=${configValue}`,
386+
'-c',
387+
'gc.auto=0',
378388
'-C',
379389
mirrorPath,
380390
'fetch',

0 commit comments

Comments
 (0)