Enhance app update process with improved restart handling and Windows launcher resolution
All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good

- Added a delay in the app update process to ensure the UI reflects completion before quitting.
- Updated the Windows launcher resolution logic to prefer the installed launcher, improving post-update behavior.
- Refactored the restart script to use `.cmd` extension and optimized process handling for better reliability.
This commit is contained in:
Tom Butcher 2026-08-04 00:24:38 +01:00
parent 4fdf3a233d
commit 6f2f7ed5c2
2 changed files with 35 additions and 24 deletions

View File

@ -207,6 +207,8 @@ const launchInstallerAndRestart = async (
}
scheduleAppRestart();
// Give the detached restart watcher a moment to start, and let the UI show 100%.
await new Promise((resolve) => setTimeout(resolve, 1000));
Utils.quit();
};

View File

@ -77,6 +77,20 @@ export const resolveAppLaunchPath = () => {
}
if (process.platform === 'win32') {
// Prefer the installed launcher so post-update restart targets the new binary
// (in-app updates may leave the running process under bin.old).
const installDir = readWindowsInstallDirFromRegistry()
if (installDir) {
const launcherFromRegistry = path.join(installDir, 'bin', 'launcher.exe')
if (existsSync(launcherFromRegistry)) {
return launcherFromRegistry
}
}
if (existsSync(WINDOWS_DEFAULT_LAUNCHER)) {
return WINDOWS_DEFAULT_LAUNCHER
}
const execBase = path.basename(process.execPath).toLowerCase()
if (execBase === 'launcher.exe') {
return process.execPath
@ -96,18 +110,6 @@ export const resolveAppLaunchPath = () => {
return launcherInBin
}
const installDir = readWindowsInstallDirFromRegistry()
if (installDir) {
const launcherFromRegistry = path.join(installDir, 'bin', 'launcher.exe')
if (existsSync(launcherFromRegistry)) {
return launcherFromRegistry
}
}
if (existsSync(WINDOWS_DEFAULT_LAUNCHER)) {
return WINDOWS_DEFAULT_LAUNCHER
}
return process.execPath
}
@ -137,39 +139,46 @@ const spawnDetachedWindowsRestart = ({ appLaunchPath, parentPid }) => {
'FarmControl',
'Updates'
)
const scriptPath = path.join(updateDir, `restart-${parentPid}.bat`)
const scriptPath = path.join(updateDir, `restart-${parentPid}.cmd`)
const appLaunchWin = appLaunchPath.replaceAll('/', '\\')
const appDirWin = path.dirname(appLaunchWin)
// Separate process: wait until Farm Control's PID exits, then relaunch launcher.exe.
const script = `@echo off
setlocal
setlocal EnableExtensions
set "PARENT_PID=${parentPid}"
set "APP_LAUNCHER=${appLaunchWin}"
set "APP_DIR=${appDirWin}"
:waitparent
tasklist /FI "PID eq %PARENT_PID%" 2>NUL | find "%PARENT_PID%" >NUL && (
timeout /t 1 /nobreak >nul
tasklist /FI "PID eq %PARENT_PID%" 2>NUL | findstr /I /C:"%PARENT_PID%" >NUL
if not errorlevel 1 (
timeout /t 1 /nobreak >NUL
goto waitparent
)
timeout /t 2 /nobreak >nul
timeout /t 1 /nobreak >NUL
cd /d "%APP_DIR%"
start "" "%APP_LAUNCHER%"
ping -n 2 127.0.0.1 >nul
del "%~f0"
timeout /t 1 /nobreak >NUL
del "%~f0" >NUL 2>&1
`
mkdirSync(updateDir, { recursive: true })
writeFileSync(scriptPath, script, 'utf8')
const child = spawn('cmd.exe', ['/c', scriptPath], {
const child = spawn(
process.env.ComSpec || 'cmd.exe',
['/d', '/c', scriptPath],
{
detached: true,
stdio: 'ignore',
windowsHide: true
})
windowsHide: true,
cwd: updateDir
}
)
child.unref()
}