All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good
- Adjusted the success signal emission in the `restartFarmControlAfterUpdate` function to ensure the application quits and restarts correctly during in-app updates. - Added a conditional check to control the progress success indication based on the update state, improving the user experience during installations.
336 lines
11 KiB
NSIS
336 lines
11 KiB
NSIS
!include "FileFunc.nsh"
|
|
!insertmacro GetParameters
|
|
!insertmacro GetOptions
|
|
|
|
Var ProgressLogFile
|
|
Var IsInAppUpdate
|
|
Var RestartAfterInstall
|
|
Var FinalInstDir
|
|
|
|
!macro initProgressLog
|
|
StrCpy $ProgressLogFile ""
|
|
StrCpy $IsInAppUpdate "0"
|
|
StrCpy $RestartAfterInstall "0"
|
|
StrCpy $FinalInstDir ""
|
|
|
|
${GetParameters} $R9
|
|
|
|
ClearErrors
|
|
${GetOptions} $R9 "/UPDATE" $R8
|
|
${IfNot} ${Errors}
|
|
StrCpy $IsInAppUpdate "1"
|
|
${EndIf}
|
|
|
|
ClearErrors
|
|
${GetOptions} $R9 "/RESTARTFC" $R8
|
|
${IfNot} ${Errors}
|
|
StrCpy $RestartAfterInstall "1"
|
|
${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 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..."
|
|
Sleep 1500
|
|
|
|
restart_wait_loop:
|
|
ExecWait 'cmd.exe /c tasklist /FI "IMAGENAME eq FarmControl.exe" 2>nul | find /I "FarmControl.exe"' $R0
|
|
${If} $R0 == 0
|
|
Sleep 1000
|
|
Goto restart_wait_loop
|
|
${EndIf}
|
|
ExecWait 'cmd.exe /c tasklist /FI "IMAGENAME eq launcher.exe" 2>nul | find /I "launcher.exe"' $R0
|
|
${If} $R0 == 0
|
|
Sleep 1000
|
|
Goto restart_wait_loop
|
|
${EndIf}
|
|
|
|
${If} $IsInAppUpdate == "1"
|
|
!insertmacro swapUpdateStaging
|
|
${EndIf}
|
|
|
|
!insertmacro progressStatus "Starting Farm Control..."
|
|
SetOutPath "$FinalInstDir\bin"
|
|
Exec "$FinalInstDir\bin\FarmControl.exe"
|
|
FunctionEnd
|