From 971992b530a2d5a00c436440cfb3a510b710abab Mon Sep 17 00:00:00 2001 From: claude-bot Date: Mon, 20 Jul 2026 16:18:18 +0900 Subject: [PATCH] =?UTF-8?q?fix(rp):=20=EC=A2=85=EB=A3=8C=20=EC=8B=9C=20?= =?UTF-8?q?=EC=9E=84=EC=8B=9C=20=ED=8C=8C=EC=9D=BC=20=EC=A0=95=EB=A6=AC=20?= =?UTF-8?q?=EB=B3=B4=EC=9E=A5=20(=EC=8B=A4=ED=8C=A8=E2=86=92=EC=9E=AC?= =?UTF-8?q?=EC=8B=9C=EB=8F=84=20=EC=A4=91=20=EB=8B=AB=EC=95=84=EB=8F=84=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 기존 window-all-closed 는 fsp.rm 을 fire-and-forget 로 부르고 곧바로 app.quit() 하여 삭제 완료 전에 프로세스가 죽었고, '재시도 중' 에는 yt-dlp/ffmpeg 자식이 파일을 잠가 삭제가 실패했다. before-quit 에서 종료를 미루고 자식을 먼저 종료해 잠금을 푼 뒤, .temp 를 재시도 삭제(잠금 해제 대기)한 다음 실제 종료하도록 변경. 0.4.10→0.4.11. Co-Authored-By: Claude Opus 4.7 --- package.json | 2 +- src/installer-rp/main.ts | 33 +++++++++++++++++++++++++++++++-- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 8d5d905..1bda7ce 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "minecraft-music-quiz-installer", - "version": "0.4.10", + "version": "0.4.11", "description": "마인크래프트 음악퀴즈 간편설치기 + 관리 사이트", "main": "dist/installer/main.js", "scripts": { diff --git a/src/installer-rp/main.ts b/src/installer-rp/main.ts index 701871b..12a53ad 100644 --- a/src/installer-rp/main.ts +++ b/src/installer-rp/main.ts @@ -702,8 +702,37 @@ app.whenReady().then(() => { }) }) +// 종료 시 임시 파일(.temp) 정리. 예전엔 window-all-closed 에서 fsp.rm 을 fire-and-forget +// 으로 호출하고 곧바로 app.quit() 해서, 삭제가 끝나기 전에 프로세스가 죽어 미리 받아둔 +// 내용이 남곤 했다. 또 '재시도 중' 이면 yt-dlp/ffmpeg 자식이 파일을 잠가 삭제가 실패한다. +// → 자식을 먼저 죽여 잠금을 풀고, 삭제가 끝날 때까지 종료를 미룬 뒤 실제로 종료한다. +let rpExitCleanupDone = false +async function cleanupTempOnExit(): Promise { + state.cancelRequested = true + for (const child of state.activeChildren) { + try { if (!child.killed) child.kill() } catch { /* noop */ } + } + const tempDir = path.join(getMcCustomDir(), '.temp') + // 자식 종료로 파일 잠금이 풀릴 시간을 주며 몇 번 재시도. + for (let attempt = 0; attempt < 3; attempt++) { + await new Promise((r) => setTimeout(r, attempt === 0 ? 200 : 400)) + try { + await fsp.rm(tempDir, { recursive: true, force: true }) + if (!fs.existsSync(tempDir)) return + } catch { /* 잠금 등 — 다음 시도 */ } + } +} + +app.on('before-quit', (event) => { + if (rpExitCleanupDone) return + event.preventDefault() + void cleanupTempOnExit().finally(() => { + rpExitCleanupDone = true + app.quit() + }) +}) + app.on('window-all-closed', () => { - // 강제 종료 시에도 임시 파일은 정리. - fsp.rm(path.join(getMcCustomDir(), '.temp'), { recursive: true, force: true }).catch(() => {}) + // app.quit() → before-quit 에서 임시 파일을 정리한 뒤 실제 종료. if (process.platform !== 'darwin') app.quit() })