All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good
- Reduced the wait time for graceful shutdown of the Farm Control application from two minutes to two seconds, enhancing responsiveness during updates. - Implemented additional checks to confirm the termination of the Farm Control process, providing clearer error messages if the process fails to stop. - Adjusted the loop for verifying process termination to improve efficiency and reduce unnecessary delays.
403 lines
14 KiB
NSIS
403 lines
14 KiB
NSIS
!include "FileFunc.nsh"
|
|
!insertmacro GetParameters
|
|
!insertmacro GetOptions
|
|
|
|
Var ProgressLogFile
|
|
Var IsInAppUpdate
|
|
Var RestartAfterInstall
|
|
Var FinalInstDir
|
|
Var UpdateParentPid
|
|
|
|
; Generate an include at compile time that defines APP_COPY_TOTAL_BYTES and
|
|
; copyApplicationFilesWithProgress. The generated macro contains one File
|
|
; command and one byte-progress log entry for every file in the source tree.
|
|
!macro compileProgressFileCopy sourceDir generator
|
|
!tempfile GENERATED_FILE_COPY_INCLUDE
|
|
!system 'powershell.exe -NoLogo -NoProfile -NonInteractive -ExecutionPolicy Bypass -File "${generator}" -SourceDir "${sourceDir}" -OutputPath "${GENERATED_FILE_COPY_INCLUDE}"' = 0
|
|
!include "${GENERATED_FILE_COPY_INCLUDE}"
|
|
!delfile "${GENERATED_FILE_COPY_INCLUDE}"
|
|
!undef GENERATED_FILE_COPY_INCLUDE
|
|
!macroend
|
|
|
|
!macro initProgressLog
|
|
StrCpy $ProgressLogFile ""
|
|
StrCpy $IsInAppUpdate "0"
|
|
StrCpy $RestartAfterInstall "0"
|
|
StrCpy $FinalInstDir ""
|
|
StrCpy $UpdateParentPid "0"
|
|
|
|
${GetParameters} $R9
|
|
|
|
ClearErrors
|
|
${GetOptions} $R9 "/UPDATE" $R8
|
|
${IfNot} ${Errors}
|
|
StrCpy $IsInAppUpdate "1"
|
|
${EndIf}
|
|
|
|
ClearErrors
|
|
${GetOptions} $R9 "/RESTARTFC" $R8
|
|
${IfNot} ${Errors}
|
|
StrCpy $RestartAfterInstall "1"
|
|
${EndIf}
|
|
|
|
ClearErrors
|
|
${GetOptions} $R9 "/PARENTPID=" $R8
|
|
${IfNot} ${Errors}
|
|
; Convert to an integer before it is used in Win32 calls or taskkill.
|
|
IntOp $UpdateParentPid $R8 + 0
|
|
${EndIf}
|
|
|
|
; Prefer env var so paths with spaces are reliable; /LOG= remains supported.
|
|
ReadEnvStr $ProgressLogFile "FARMCONTROL_INSTALL_LOG"
|
|
${If} $ProgressLogFile == ""
|
|
ClearErrors
|
|
${GetOptions} $R9 "/LOG=" $ProgressLogFile
|
|
${If} ${Errors}
|
|
StrCpy $ProgressLogFile ""
|
|
${EndIf}
|
|
${EndIf}
|
|
|
|
${If} $ProgressLogFile != ""
|
|
; Strip surrounding quotes from /LOG="C:\path with spaces\log.log"
|
|
StrCpy $R8 $ProgressLogFile 1
|
|
${If} $R8 == '"'
|
|
StrCpy $ProgressLogFile $ProgressLogFile "" 1
|
|
StrLen $R8 $ProgressLogFile
|
|
IntOp $R8 $R8 - 1
|
|
${If} $R8 > 0
|
|
StrCpy $ProgressLogFile $ProgressLogFile $R8
|
|
${EndIf}
|
|
${EndIf}
|
|
|
|
; Truncate any previous log so progress starts clean.
|
|
Push $0
|
|
FileOpen $0 "$ProgressLogFile" w
|
|
${If} $0 != ""
|
|
FileClose $0
|
|
${EndIf}
|
|
Pop $0
|
|
${EndIf}
|
|
!macroend
|
|
|
|
; Open/append/close each line so the updater can read the log concurrently
|
|
; (avoids an exclusive lock for the whole install; LogEx SHARE_READ is an alternative).
|
|
!macro progressLog line
|
|
DetailPrint "${line}"
|
|
${If} $ProgressLogFile != ""
|
|
Push $0
|
|
FileOpen $0 "$ProgressLogFile" a
|
|
${If} $0 != ""
|
|
FileSeek $0 0 END
|
|
FileWrite $0 "${line}$\r$\n"
|
|
FileClose $0
|
|
${EndIf}
|
|
Pop $0
|
|
${EndIf}
|
|
!macroend
|
|
|
|
!macro progressPercent percent
|
|
!insertmacro progressLog "installer:%${percent}"
|
|
!macroend
|
|
|
|
!macro progressPhase phase
|
|
!insertmacro progressLog "installer:PHASE:${phase}"
|
|
!macroend
|
|
|
|
!macro progressStatus status
|
|
!insertmacro progressLog "installer:STATUS:${status}"
|
|
!macroend
|
|
|
|
!macro progressCopyTotal bytes
|
|
!insertmacro progressLog "installer:COPY_TOTAL:${bytes}"
|
|
!macroend
|
|
|
|
!macro progressCopyFile bytes relativePath
|
|
!insertmacro progressLog "installer:COPY_FILE:${bytes}:${relativePath}"
|
|
!macroend
|
|
|
|
!macro progressSuccess
|
|
!insertmacro progressPercent 100
|
|
!insertmacro progressStatus "Installation complete. Restarting Farm Control..."
|
|
!insertmacro progressLog "installer: The install was successful."
|
|
!macroend
|
|
|
|
!macro progressFailure message
|
|
!insertmacro progressLog "installer:STATUS:${message}"
|
|
!insertmacro progressLog "installer: The install failed."
|
|
!macroend
|
|
|
|
!macro quitFarmControl
|
|
!insertmacro progressPhase "Stopping Farm Control"
|
|
!insertmacro progressStatus "Stopping running Farm Control processes..."
|
|
DetailPrint "Stopping running Farm Control processes..."
|
|
ExecWait 'taskkill /F /IM FarmControl.exe /T' $R0
|
|
ExecWait 'taskkill /F /IM launcher.exe /T' $R0
|
|
!macroend
|
|
|
|
; Side-by-side update: install into Farm Control.new while the running app
|
|
; keeps using Farm Control, then swap folders after the process exits.
|
|
!macro prepareSideBySideUpdate
|
|
!insertmacro progressPhase "Preparing update"
|
|
!insertmacro progressStatus "Preparing staging folder for update..."
|
|
|
|
StrCpy $FinalInstDir $INSTDIR
|
|
StrCpy $INSTDIR "$FinalInstDir.new"
|
|
|
|
${If} ${FileExists} "$INSTDIR"
|
|
DetailPrint "Removing leftover staging folder..."
|
|
RMDir /r "$INSTDIR"
|
|
${EndIf}
|
|
!macroend
|
|
|
|
; Swap order: Farm Control -> .old, Farm Control.new -> Farm Control, delete .old.
|
|
!macro swapUpdateStaging
|
|
!insertmacro progressPhase "Applying update"
|
|
!insertmacro progressStatus "Replacing Farm Control with the new version..."
|
|
DetailPrint "Replacing Farm Control with staged update..."
|
|
|
|
; The install section ran SetOutPath into the install tree; a directory that
|
|
; is any process's working directory cannot be renamed, so move out first.
|
|
SetOutPath "$TEMP"
|
|
|
|
Sleep 3000
|
|
|
|
; Clear a leftover .old from a previous interrupted update.
|
|
${If} ${FileExists} "$FinalInstDir.old"
|
|
DetailPrint "Removing leftover Farm Control.old..."
|
|
RMDir /r "$FinalInstDir.old"
|
|
${EndIf}
|
|
|
|
${If} ${FileExists} "$FinalInstDir"
|
|
!insertmacro progressStatus "Waiting for Farm Control to release install files..."
|
|
DetailPrint "Waiting to rename install directory to Farm Control.old..."
|
|
|
|
; Retry every 500ms; success is verified on disk rather than via the
|
|
; error flag, which Rename does not set reliably.
|
|
StrCpy $R7 0
|
|
swap_rename_old_retry:
|
|
Rename "$FinalInstDir" "$FinalInstDir.old"
|
|
${IfNot} ${FileExists} "$FinalInstDir"
|
|
Goto swap_rename_old_done
|
|
${EndIf}
|
|
IntOp $R7 $R7 + 1
|
|
${If} $R7 < 120
|
|
Sleep 500
|
|
Goto swap_rename_old_retry
|
|
${EndIf}
|
|
!insertmacro progressFailure "Could not move the previous Farm Control installation aside."
|
|
Abort
|
|
swap_rename_old_done:
|
|
DetailPrint "Install directory renamed to Farm Control.old"
|
|
${EndIf}
|
|
|
|
!insertmacro progressStatus "Activating new Farm Control installation..."
|
|
DetailPrint "Renaming Farm Control.new to Farm Control..."
|
|
|
|
StrCpy $R7 0
|
|
swap_rename_new_retry:
|
|
Rename "$INSTDIR" "$FinalInstDir"
|
|
${If} ${FileExists} "$FinalInstDir"
|
|
Goto swap_rename_new_done
|
|
${EndIf}
|
|
IntOp $R7 $R7 + 1
|
|
${If} $R7 < 20
|
|
Sleep 500
|
|
Goto swap_rename_new_retry
|
|
${EndIf}
|
|
; Best-effort rollback so the previous install is usable again.
|
|
${If} ${FileExists} "$FinalInstDir.old"
|
|
Rename "$FinalInstDir.old" "$FinalInstDir"
|
|
${EndIf}
|
|
!insertmacro progressFailure "Could not activate the new Farm Control installation."
|
|
Abort
|
|
swap_rename_new_done:
|
|
|
|
StrCpy $INSTDIR $FinalInstDir
|
|
!insertmacro progressLog "installer:STATUS:Update files activated"
|
|
|
|
${If} ${FileExists} "$FinalInstDir.old"
|
|
!insertmacro progressStatus "Removing previous Farm Control installation..."
|
|
DetailPrint "Removing Farm Control.old..."
|
|
|
|
StrCpy $R7 0
|
|
swap_delete_old_retry:
|
|
RMDir /r "$FinalInstDir.old"
|
|
${If} ${FileExists} "$FinalInstDir.old"
|
|
IntOp $R7 $R7 + 1
|
|
${If} $R7 < 20
|
|
Sleep 500
|
|
Goto swap_delete_old_retry
|
|
${EndIf}
|
|
; New version is already active; leftover .old is non-fatal.
|
|
DetailPrint "Warning: could not fully remove Farm Control.old"
|
|
!insertmacro progressLog "installer:STATUS:Warning: could not fully remove previous installation"
|
|
${EndIf}
|
|
${EndIf}
|
|
!macroend
|
|
|
|
!macro uninstallPreviousFarmControl
|
|
ClearErrors
|
|
ReadRegStr $R0 HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\Farm Control" "UninstallString"
|
|
StrCmp $R0 "" check_install_dir run_uninstall
|
|
|
|
check_install_dir:
|
|
ReadRegStr $R0 HKCU "Software\Tom Butcher\Farm Control" "InstallDir"
|
|
StrCmp $R0 "" check_legacy_uninstall
|
|
StrCpy $R0 "$R0\Uninstall.exe"
|
|
Goto run_uninstall
|
|
|
|
check_legacy_uninstall:
|
|
ReadRegStr $R0 HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Farm Control" "UninstallString"
|
|
StrCmp $R0 "" check_legacy_install_dir run_uninstall
|
|
|
|
check_legacy_install_dir:
|
|
ReadRegStr $R0 HKLM "Software\Tom Butcher\Farm Control" "InstallDir"
|
|
StrCmp $R0 "" check_default_dir
|
|
StrCpy $R0 "$R0\Uninstall.exe"
|
|
Goto run_uninstall
|
|
|
|
check_default_dir:
|
|
StrCpy $R0 "$LOCALAPPDATA\Programs\Farm Control\Uninstall.exe"
|
|
IfFileExists $R0 run_uninstall check_legacy_default_dir
|
|
|
|
check_legacy_default_dir:
|
|
StrCpy $R0 "$PROGRAMFILES64\Farm Control\Uninstall.exe"
|
|
IfFileExists $R0 run_uninstall done_uninstall
|
|
|
|
run_uninstall:
|
|
IfFileExists $R0 0 done_uninstall
|
|
!insertmacro progressPhase "Removing previous version"
|
|
!insertmacro progressStatus "Removing previous Farm Control installation..."
|
|
DetailPrint "Removing previous Farm Control installation..."
|
|
!insertmacro quitFarmControl
|
|
ExecWait '"$R0" /S' $R1
|
|
DetailPrint "Previous installation removed (exit code: $R1)"
|
|
!insertmacro progressLog "installer:STATUS:Previous installation removed"
|
|
|
|
done_uninstall:
|
|
!macroend
|
|
|
|
!macro createDesktopShortcut
|
|
SetShellVarContext current
|
|
SetOutPath "$FinalInstDir\bin"
|
|
CreateShortCut "$DESKTOP\Farm Control.lnk" "$FinalInstDir\bin\FarmControl.exe" "" "$FinalInstDir\bin\FarmControl.exe" 0 SW_SHOWNORMAL "" "Farm Control"
|
|
!macroend
|
|
|
|
!macro createStartMenuShortcut
|
|
SetShellVarContext current
|
|
CreateDirectory "$SMPROGRAMS\Farm Control"
|
|
SetOutPath "$FinalInstDir\bin"
|
|
CreateShortCut "$SMPROGRAMS\Farm Control\Farm Control.lnk" "$FinalInstDir\bin\FarmControl.exe" "" "$FinalInstDir\bin\FarmControl.exe" 0 SW_SHOWNORMAL "" "Farm Control"
|
|
!macroend
|
|
|
|
!macro removeDesktopShortcut
|
|
SetShellVarContext current
|
|
Delete "$DESKTOP\Farm Control.lnk"
|
|
!macroend
|
|
|
|
!macro removeStartMenuShortcut
|
|
SetShellVarContext current
|
|
Delete "$SMPROGRAMS\Farm Control\Farm Control.lnk"
|
|
RMDir "$SMPROGRAMS\Farm Control"
|
|
!macroend
|
|
|
|
!macro customInstall
|
|
!insertmacro progressPhase "Configuring Farm Control"
|
|
!insertmacro progressStatus "Registering farmcontrol URI handler..."
|
|
DetailPrint "Register farmcontrol URI Handler"
|
|
DeleteRegKey HKCU "Software\Classes\farmcontrol"
|
|
WriteRegStr HKCU "Software\Classes\farmcontrol" "" "URL:farmcontrol"
|
|
WriteRegStr HKCU "Software\Classes\farmcontrol" "URL Protocol" ""
|
|
WriteRegStr HKCU "Software\Classes\farmcontrol\DefaultIcon" "" "$FinalInstDir\bin\FarmControl.exe"
|
|
WriteRegStr HKCU "Software\Classes\farmcontrol\shell" "" ""
|
|
WriteRegStr HKCU "Software\Classes\farmcontrol\shell\Open" "" ""
|
|
WriteRegStr HKCU "Software\Classes\farmcontrol\shell\Open\command" "" '"$FinalInstDir\bin\bun.exe" "$FinalInstDir\bin\deeplink.js" "%1"'
|
|
|
|
!insertmacro progressStatus "Creating shortcuts..."
|
|
DetailPrint "Creating shortcuts"
|
|
!insertmacro createDesktopShortcut
|
|
!insertmacro createStartMenuShortcut
|
|
!macroend
|
|
|
|
!macro customUnInstall
|
|
!insertmacro removeDesktopShortcut
|
|
!insertmacro removeStartMenuShortcut
|
|
DeleteRegKey HKCU "Software\Classes\farmcontrol"
|
|
!macroend
|
|
|
|
; Wait for the running app to exit, swap staged update into place, then relaunch.
|
|
; Only used when /RESTARTFC is passed.
|
|
Function restartFarmControlAfterUpdate
|
|
${If} $RestartAfterInstall != "1"
|
|
Return
|
|
${EndIf}
|
|
|
|
; All files are copied and we are about to rename the install directory.
|
|
; Emit the success line (matched by isWindowsInstallSuccessful in
|
|
; winappupdate.js) so the app knows to quit and restart; this installer
|
|
; stays alive to swap the folders and relaunch Farm Control.
|
|
!insertmacro progressSuccess
|
|
|
|
!insertmacro progressStatus "Waiting for Farm Control to close..."
|
|
|
|
${If} $UpdateParentPid > 0
|
|
; Wait for the exact updater process rather than an executable name. CEF
|
|
; builds can keep bun/renderer processes alive after the launcher exits.
|
|
System::Call 'kernel32::OpenProcess(i 0x00100000, i 0, i $UpdateParentPid) p .r0'
|
|
${If} $0 != 0
|
|
; Allow two seconds for a graceful exit, then terminate the process tree.
|
|
System::Call 'kernel32::WaitForSingleObject(p r0, i 2000) i .r1'
|
|
${If} $1 == 258
|
|
!insertmacro progressStatus "Stopping Farm Control..."
|
|
DetailPrint "Farm Control process $UpdateParentPid did not exit; terminating its process tree..."
|
|
ExecWait 'taskkill /F /T /PID $UpdateParentPid' $R0
|
|
|
|
; Verify that taskkill actually terminated the process before swapping.
|
|
System::Call 'kernel32::WaitForSingleObject(p r0, i 10000) i .r1'
|
|
${EndIf}
|
|
|
|
System::Call 'kernel32::CloseHandle(p r0)'
|
|
${If} $1 != 0
|
|
!insertmacro progressFailure "Could not stop the running Farm Control process."
|
|
Abort
|
|
${EndIf}
|
|
${EndIf}
|
|
${Else}
|
|
; Compatibility fallback for manually launched older installers.
|
|
Sleep 2000
|
|
!insertmacro quitFarmControl
|
|
|
|
; Confirm both known launcher names have stopped before continuing.
|
|
StrCpy $R7 0
|
|
restart_verify_loop:
|
|
ExecWait 'cmd.exe /c tasklist /FI "IMAGENAME eq FarmControl.exe" 2>nul | find /I "FarmControl.exe"' $R0
|
|
${If} $R0 != 0
|
|
ExecWait 'cmd.exe /c tasklist /FI "IMAGENAME eq launcher.exe" 2>nul | find /I "launcher.exe"' $R0
|
|
${EndIf}
|
|
${If} $R0 != 0
|
|
Goto restart_wait_done
|
|
${EndIf}
|
|
|
|
IntOp $R7 $R7 + 1
|
|
${If} $R7 < 20
|
|
Sleep 500
|
|
Goto restart_verify_loop
|
|
${EndIf}
|
|
|
|
!insertmacro progressFailure "Could not stop the running Farm Control process."
|
|
Abort
|
|
${EndIf}
|
|
|
|
restart_wait_done:
|
|
; Give CEF descendants a moment to release DLLs before renaming the tree.
|
|
Sleep 3000
|
|
|
|
${If} $IsInAppUpdate == "1"
|
|
!insertmacro swapUpdateStaging
|
|
${EndIf}
|
|
|
|
!insertmacro progressStatus "Starting Farm Control..."
|
|
SetOutPath "$FinalInstDir\bin"
|
|
Exec "$FinalInstDir\bin\FarmControl.exe"
|
|
FunctionEnd
|