diff --git a/frontend/index.js b/frontend/index.js index d858179b..1cb797ba 100644 --- a/frontend/index.js +++ b/frontend/index.js @@ -1579,6 +1579,36 @@ function stripProgressRedraws(text) { return text.replace(/[^\n]*\r(?!\n)/g, ''); } +// Git for Windows only puts Git\cmd on PATH, so a GUI launch can't find bash. +// Never System32 / WindowsApps bash.exe (WSL): it can't run the Windows paths we pass. +function findBash() { + if (process.platform !== 'win32') return { cmd: 'bash', env: {} }; + const gitBash = (root) => root && path.join(root, 'Git', 'bin', 'bash.exe'); + const candidates = []; + for (const dir of (process.env.PATH || '').split(path.delimiter)) { + if (!dir) continue; + candidates.push(path.join(dir, 'bash.exe')); + // ...\Git\cmd on PATH -> ...\Git\bin\bash.exe + if (path.basename(dir).toLowerCase() === 'cmd') candidates.push(path.join(dir, '..', 'bin', 'bash.exe')); + } + candidates.push( + gitBash(process.env.ProgramW6432), + gitBash(process.env.ProgramFiles), + gitBash(process.env['ProgramFiles(x86)']), + process.env.LOCALAPPDATA && path.join(process.env.LOCALAPPDATA, 'Programs', 'Git', 'bin', 'bash.exe'), + ); + const system32 = path.join(process.env.SystemRoot || 'C:\\Windows', 'System32').toLowerCase(); + const isWsl = (c) => { const d = path.dirname(c).toLowerCase(); return d.startsWith(system32) || d.endsWith('\\windowsapps'); }; + const found = candidates.find((c) => c && !isWsl(c) && fs.existsSync(c)); + if (found) return { cmd: found, env: {} }; + // MSYS2 bash needs a login shell for /usr/bin on PATH; CHERE_INVOKING keeps the cwd + for (const root of [process.env.MSYS2_ROOT, 'C:\\msys64', 'C:\\msys2']) { + const msys = root && path.join(root, 'usr', 'bin', 'bash.exe'); + if (msys && fs.existsSync(msys)) return { cmd: msys, args: ['-l'], env: { CHERE_INVOKING: '1', MSYSTEM: 'MINGW64' } }; + } + return null; +} + ipcMain.on('downloadCoreLibs', (event, { ofPath, source, tag, platform }) => { const send = (msg) => { if (!event.sender.isDestroyed()) event.sender.send('consoleMessage', msg); }; const done = (ok) => { if (!event.sender.isDestroyed()) event.sender.send('downloadCoreLibsDone', { ok }); }; @@ -1603,10 +1633,17 @@ ipcMain.on('downloadCoreLibs', (event, { ofPath, source, tag, platform }) => { } send(`
${t('downloads.coreStarting', { tag: `${source || 'apothecary'} ${tag}`, platform })}
`); + const bash = findBash(); + if (!bash) { + send(`${t('msg.error')} ${t('downloads.bashMissing')}
`); + done(false); + return; + } + args = [...(bash.args || []), ...args]; let child; try { - const env = platform === 'emscripten64' ? { ...process.env, BITS: '64' } : process.env; - child = spawn('bash', args, { cwd: path.dirname(script), env, windowsHide: true }); + const env = { ...process.env, ...bash.env, ...(platform === 'emscripten64' ? { BITS: '64' } : {}) }; + child = spawn(bash.cmd, args, { cwd: path.dirname(script), env, windowsHide: true }); } catch (error) { send(`${t('msg.error')} ${error.message}
`); done(false); @@ -1619,7 +1656,7 @@ ipcMain.on('downloadCoreLibs', (event, { ofPath, source, tag, platform }) => { done(false); }); child.on('close', (code) => { - send(`
(command used: bash ${args.join(' ')})
`); + send(`
(command used: ${bash.cmd} ${args.join(' ')})
`); if (code !== 0) { event.sender.send('sendUIMessage', `\n${t('msg.error')}
${t('downloads.coreFailed', { code })}`);