feat(nsis): implement passive mode, closes #6955 (#6998)

Co-authored-by: Lucas Nogueira <lucas@tauri.studio>
This commit is contained in:
Amr Bashir 2023-05-24 20:16:12 +03:00 committed by GitHub
parent 60334f9e02
commit df89ccc191
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 147 additions and 81 deletions

View File

@ -0,0 +1,5 @@
---
'tauri-bundler': 'patch'
---
Fix NSIS installer disabling `do not uninstall` button and silent installer aborting, if `allowDowngrades` was disabled even when we are not downgrading.

View File

@ -0,0 +1,5 @@
---
'tauri': 'minor'
---
Support `passive` mode for NSIS updater.

View File

@ -0,0 +1,5 @@
---
'tauri-bundler': 'minor'
---
For NSIS, Add support for `/P` to install or uninstall in passive mode, `/R` to (re)start the app and `/NS` to disable creating shortcuts in `silent` and `passive` modes.

View File

@ -0,0 +1,5 @@
---
'tauri-bundler': 'minor'
---
NSIS `silent` and `passive` installer/updater will auto-kill the app if its running.

View File

@ -0,0 +1,5 @@
---
'tauri-bundler': 'patch'
---
Fix NSIS silent installer not creating Desktop and StartMenu shortcuts. Pass `/NS` to disable creating them.

View File

@ -2822,7 +2822,7 @@
]
},
{
"description": "Specifies unattended mode, which means the installation only shows a progress bar. **msi (Wix) Only**",
"description": "Specifies unattended mode, which means the installation only shows a progress bar.",
"type": "string",
"enum": [
"passive"

View File

@ -2466,7 +2466,7 @@ pub enum WindowsUpdateInstallMode {
/// The quiet mode means there's no user interaction required.
/// Requires admin privileges if the installer does.
Quiet,
/// Specifies unattended mode, which means the installation only shows a progress bar. **msi (Wix) Only**
/// Specifies unattended mode, which means the installation only shows a progress bar.
Passive,
// to add more modes, we need to check if the updater relaunch makes sense
// i.e. for a full UI mode, the user can also mark the installer to start the app
@ -2485,6 +2485,7 @@ impl WindowsUpdateInstallMode {
/// Returns the associated nsis arguments.
pub fn nsis_args(&self) -> &'static [&'static str] {
match self {
Self::Passive => &["/P", "/R"],
Self::Quiet => &["/S", "/R"],
_ => &[],
}

View File

@ -735,15 +735,11 @@ fn copy_files_and_run<R: Read + Seek>(
// If it's an `exe` we expect an installer not a runtime.
if found_path.extension() == Some(OsStr::new("exe")) {
// Run the EXE
let mut installer = Command::new(found_path);
if crate::utils::config::WindowsUpdateInstallMode::Quiet
== config.tauri.updater.windows.install_mode
{
installer.args(config.tauri.updater.windows.install_mode.nsis_args());
}
installer.args(&config.tauri.updater.windows.installer_args);
installer.spawn().expect("installer failed to start");
Command::new(found_path)
.args(config.tauri.updater.windows.install_mode.nsis_args())
.args(&config.tauri.updater.windows.installer_args)
.spawn()
.expect("installer failed to start");
exit(0);
} else if found_path.extension() == Some(OsStr::new("msi")) {

View File

@ -93,18 +93,20 @@ VIAddVersionKey "ProductVersion" "${VERSION}"
!define MUI_LANGDLL_REGISTRY_KEY "${MANUPRODUCTKEY}"
!define MUI_LANGDLL_REGISTRY_VALUENAME "Installer Language"
; Installer pages, must be ordered as they should appear to the user
;
; Installer pages, must be ordered as they appear
; 1. Welcome Page
!define MUI_PAGE_CUSTOMFUNCTION_PRE SkipIfPassive
!insertmacro MUI_PAGE_WELCOME
; 2. License Page (if defined)
!if "${LICENSE}" != ""
!define MUI_PAGE_CUSTOMFUNCTION_PRE SkipIfPassive
!insertmacro MUI_PAGE_LICENSE "${LICENSE}"
!endif
; 3. Install mode (if it is set to `both`)
!if "${INSTALLMODE}" == "both"
!define MUI_PAGE_CUSTOMFUNCTION_PRE SkipIfPassive
!insertmacro MULTIUSER_PAGE_INSTALLMODE
!endif
@ -162,14 +164,14 @@ Function PageReinstall
StrCpy $R2 "$(addOrReinstall)"
StrCpy $R3 "$(uninstallApp)"
!insertmacro MUI_HEADER_TEXT "$(alreadyInstalled)" "$(chooseMaintenanceOption)"
StrCpy $R0 "2"
StrCpy $R5 "2"
; Upgrading
${ElseIf} $R0 == 1
StrCpy $R1 "$(olderOrUnknownVersionInstalled)"
StrCpy $R2 "$(uninstallBeforeInstalling)"
StrCpy $R3 "$(dontUninstall)"
!insertmacro MUI_HEADER_TEXT "$(alreadyInstalled)" "$(choowHowToInstall)"
StrCpy $R0 "1"
StrCpy $R5 "1"
; Downgrading
${ElseIf} $R0 == -1
StrCpy $R1 "$(newerVersionInstalled)"
@ -180,16 +182,16 @@ Function PageReinstall
StrCpy $R3 "$(dontUninstallDowngrade)"
!endif
!insertmacro MUI_HEADER_TEXT "$(alreadyInstalled)" "$(choowHowToInstall)"
StrCpy $R0 "1"
StrCpy $R5 "1"
${Else}
Abort
${EndIf}
Call SkipIfPassive
nsDialogs::Create 1018
Pop $R4
${If} $(^RTL) == 1
nsDialogs::SetRTL $(^RTL)
${EndIf}
${IfThen} $(^RTL) == 1 ${|} nsDialogs::SetRTL $(^RTL) ${|}
${NSD_CreateLabel} 0 0 100% 24u $R1
Pop $R1
@ -200,12 +202,15 @@ Function PageReinstall
${NSD_CreateRadioButton} 30u 70u -30u 8u $R3
Pop $R3
; disable this radio button if downgrades are not allowed
; disable this radio button if downgrading and downgrades are disabled
!if "${ALLOWDOWNGRADES}" == "false"
EnableWindow $R3 0
${IfThen} $R0 == -1 ${|} EnableWindow $R3 0 ${|}
!endif
${NSD_OnClick} $R3 PageReinstallUpdateSelection
; Check the first radio button if this the first time
; we enter this page or if the second button wasn't
; selected the last time we were on this page
${If} $ReinstallPageCheck != 2
SendMessage $R2 ${BM_SETCHECK} ${BST_CHECKED} 0
${Else}
@ -213,14 +218,10 @@ Function PageReinstall
${EndIf}
${NSD_SetFocus} $R2
nsDialogs::Show
FunctionEnd
Function PageReinstallUpdateSelection
Pop $R1
${NSD_GetState} $R2 $R1
${If} $R1 == ${BST_CHECKED}
StrCpy $ReinstallPageCheck 1
${Else}
@ -230,18 +231,19 @@ FunctionEnd
Function PageLeaveReinstall
${NSD_GetState} $R2 $R1
; $R0 holds whether we are reinstalling the same version or not
; $R0 == "1" -> different versions
; $R0 == "2" -> same version
; $R5 holds whether we are reinstalling the same version or not
; $R5 == "1" -> different versions
; $R5 == "2" -> same version
;
; $R1 holds the radio buttons state. its meaning is dependant on the context
StrCmp $R0 "1" 0 +2 ; Existing install is not the same version?
StrCmp $R5 "1" 0 +2 ; Existing install is not the same version?
StrCmp $R1 "1" reinst_uninstall reinst_done ; $R1 == "1", then user chose to uninstall existing version, otherwise skip uninstalling
StrCmp $R1 "1" reinst_done ; Same version? skip uninstalling
reinst_uninstall:
HideWindow
ClearErrors
ExecWait '$R1 /P _?=$4' $0
${If} $R5 == "wix"
ReadRegStr $R1 HKLM "$R6" "UninstallString"
@ -259,7 +261,7 @@ Function PageLeaveReinstall
${If} $0 <> 0
${OrIf} ${FileExists} "$INSTDIR\${MAINBINARYNAME}.exe"
${If} $0 = 1 ; User aborted uninstaller?
StrCmp $R0 "2" 0 +2 ; Is the existing install the same version?
StrCmp $R5 "2" 0 +2 ; Is the existing install the same version?
Quit ; ...yes, already installed, we are done
Abort
${EndIf}
@ -271,14 +273,15 @@ Function PageLeaveReinstall
Delete $R1
RMDir $INSTDIR
${EndIf}
reinst_done:
FunctionEnd
; 5. Choose install directoy page
!define MUI_PAGE_CUSTOMFUNCTION_PRE SkipIfPassive
!insertmacro MUI_PAGE_DIRECTORY
; 6. Start menu shortcut page
!define MUI_PAGE_CUSTOMFUNCTION_PRE SkipIfPassive
Var AppStartMenuFolder
!insertmacro MUI_PAGE_STARTMENU Application $AppStartMenuFolder
@ -294,12 +297,9 @@ Var AppStartMenuFolder
!define MUI_FINISHPAGE_SHOWREADME
!define MUI_FINISHPAGE_SHOWREADME_TEXT "$(createDesktop)"
!define MUI_FINISHPAGE_SHOWREADME_FUNCTION CreateDesktopShortcut
Function CreateDesktopShortcut
CreateShortcut "$DESKTOP\${MAINBINARYNAME}.lnk" "$INSTDIR\${MAINBINARYNAME}.exe"
ApplicationID::Set "$DESKTOP\${MAINBINARYNAME}.lnk" "${BUNDLEID}"
FunctionEnd
; Show run app after installation.
!define MUI_FINISHPAGE_RUN "$INSTDIR\${MAINBINARYNAME}.exe"
!define MUI_PAGE_CUSTOMFUNCTION_PRE SkipIfPassive
!insertmacro MUI_PAGE_FINISH
; Uninstaller Pages
@ -337,7 +337,12 @@ FunctionEnd
!include "{{this}}"
{{/each}}
Var PassiveMode
Function .onInit
${GetOptions} $CMDLINE "/P" $PassiveMode
IfErrors +2 0
StrCpy $PassiveMode 1
!if "${DISPLAYLANGUAGESELECTOR}" == "true"
!insertmacro MUI_LANGDLL_DISPLAY
!endif
@ -385,22 +390,27 @@ Function .onInit
!endif
FunctionEnd
Section EarlyChecks
; Abort silent installer if downgrades is disabled
!if "${ALLOWDOWNGRADES}" == "false"
IfSilent 0 done
System::Call 'kernel32::AttachConsole(i -1)i.r0'
${If} $0 != 0
System::Call 'kernel32::GetStdHandle(i -11)i.r0'
System::call 'kernel32::SetConsoleTextAttribute(i r0, i 0x0004)' ; set red color
FileWrite $0 "$(silentDowngrades)"
IfSilent 0 silent_downgrades_done
; If downgrading
${If} $R0 == -1
System::Call 'kernel32::AttachConsole(i -1)i.r0'
${If} $0 != 0
System::Call 'kernel32::GetStdHandle(i -11)i.r0'
System::call 'kernel32::SetConsoleTextAttribute(i r0, i 0x0004)' ; set red color
FileWrite $0 "$(silentDowngrades)"
${EndIf}
Abort
${EndIf}
Abort
done:
silent_downgrades_done:
!endif
SectionEnd
Section Webview2
Section WebView2
; Check if Webview2 is already installed and skip this section
${If} ${RunningX64}
ReadRegStr $4 HKLM "SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" "pv"
@ -409,8 +419,8 @@ Section Webview2
${EndIf}
ReadRegStr $5 HKCU "SOFTWARE\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" "pv"
StrCmp $4 "" 0 done
StrCmp $5 "" 0 done
StrCmp $4 "" 0 webview2_done
StrCmp $5 "" 0 webview2_done
; Webview2 install modes
!if "${INSTALLWEBVIEW2MODE}" == "downloadBootstrapper"
@ -444,7 +454,7 @@ Section Webview2
Goto install_webview2
!endif
Goto done
Goto webview2_done
install_webview2:
DetailPrint "$(installingWebview2)"
@ -456,38 +466,38 @@ Section Webview2
DetailPrint "$(webview2InstallError)"
Abort "$(webview2AbortError)"
${EndIf}
done:
webview2_done:
SectionEnd
!macro CheckIfAppIsRunning
nsis_tauri_utils::FindProcess "${MAINBINARYNAME}.exe"
Pop $R0
${If} $R0 = 0
IfSilent silent ui
silent:
System::Call 'kernel32::AttachConsole(i -1)i.r0'
${If} $0 != 0
System::Call 'kernel32::GetStdHandle(i -11)i.r0'
System::call 'kernel32::SetConsoleTextAttribute(i r0, i 0x0004)' ; set red color
FileWrite $0 "$(appRunning)$\n"
${EndIf}
Abort
ui:
MessageBox MB_OKCANCEL "$(appRunningOkKill)" IDOK ok IDCANCEL cancel
ok:
IfSilent kill 0
${IfThen} $PassiveMode != 1 ${|} MessageBox MB_OKCANCEL "$(appRunningOkKill)" IDOK kill IDCANCEL cancel ${|}
kill:
nsis_tauri_utils::KillProcess "${MAINBINARYNAME}.exe"
Pop $R0
Sleep 500
${If} $R0 = 0
Goto done
Goto app_check_done
${Else}
Abort "$(failedToKillApp)"
IfSilent silent ui
silent:
System::Call 'kernel32::AttachConsole(i -1)i.r0'
${If} $0 != 0
System::Call 'kernel32::GetStdHandle(i -11)i.r0'
System::call 'kernel32::SetConsoleTextAttribute(i r0, i 0x0004)' ; set red color
FileWrite $0 "$(appRunning)$\n"
${EndIf}
Abort
ui:
Abort "$(failedToKillApp)"
${EndIf}
cancel:
Abort "$(appRunning)"
${EndIf}
done:
app_check_done:
!macroend
Section Install
@ -534,24 +544,39 @@ Section Install
IntFmt $0 "0x%08X" $0
WriteRegDWORD SHCTX "${UNINSTKEY}" "EstimatedSize" "$0"
; Create start menu shortcut
; Create start menu shortcut (GUI)
!insertmacro MUI_STARTMENU_WRITE_BEGIN Application
CreateDirectory "$SMPROGRAMS\$AppStartMenuFolder"
CreateShortcut "$SMPROGRAMS\$AppStartMenuFolder\${MAINBINARYNAME}.lnk" "$INSTDIR\${MAINBINARYNAME}.exe"
ApplicationID::Set "$SMPROGRAMS\$AppStartMenuFolder\${MAINBINARYNAME}.lnk" "${BUNDLEID}"
Call CreateStartMenuShortcut
!insertmacro MUI_STARTMENU_WRITE_END
; Create shortcuts for silent and passive installers, which
; can be disabled by passing `/NS` flag
; GUI installer has buttons for users to control creating them
IfSilent check_ns_flag 0
${IfThen} $PassiveMode == 1 ${|} Goto check_ns_flag ${|}
Goto shortcuts_done
check_ns_flag:
${GetOptions} $CMDLINE "/NS" $R0
IfErrors 0 shortcuts_done
Call CreateDesktopShortcut
Call CreateStartMenuShortcut
shortcuts_done:
; Auto close this page for passive mode
${IfThen} $PassiveMode == 1 ${|} SetAutoClose true ${|}
SectionEnd
Var Restart
Function .onInstSuccess
; Check for `/R` flag only in silent installer because
; gui installer has a toggle for the user to restart the app
IfSilent 0 done
${GetOptions} $CMDLINE "/R" $Restart
IfErrors done 0 ; if errors were found then `/R` wasn't passed, so we skip restarting
; Check for `/R` flag only in silent and passive installers because
; GUI installer has a toggle for the user to (re)start the app
IfSilent check_r_flag 0
${IfThen} $PassiveMode == 1 ${|} Goto check_r_flag ${|}
Goto run_done
check_r_flag:
${GetOptions} $CMDLINE "/R" $R0
IfErrors run_done 0
Exec '"$INSTDIR\${MAINBINARYNAME}.exe"'
done:
run_done:
FunctionEnd
Function un.onInit
@ -619,6 +644,10 @@ Section Uninstall
!endif
DeleteRegValue HKCU "${MANUPRODUCTKEY}" "Installer Language"
${GetOptions} $CMDLINE "/P" $R0
IfErrors +2 0
SetAutoClose true
SectionEnd
Function RestorePreviousInstallLocation
@ -626,3 +655,18 @@ Function RestorePreviousInstallLocation
StrCmp $4 "" +2 0
StrCpy $INSTDIR $4
FunctionEnd
Function SkipIfPassive
${IfThen} $PassiveMode == 1 ${|} Abort ${|}
FunctionEnd
Function CreateDesktopShortcut
CreateShortcut "$DESKTOP\${MAINBINARYNAME}.lnk" "$INSTDIR\${MAINBINARYNAME}.exe"
ApplicationID::Set "$DESKTOP\${MAINBINARYNAME}.lnk" "${BUNDLEID}"
FunctionEnd
Function CreateStartMenuShortcut
CreateDirectory "$SMPROGRAMS\$AppStartMenuFolder"
CreateShortcut "$SMPROGRAMS\$AppStartMenuFolder\${MAINBINARYNAME}.lnk" "$INSTDIR\${MAINBINARYNAME}.exe"
ApplicationID::Set "$SMPROGRAMS\$AppStartMenuFolder\${MAINBINARYNAME}.lnk" "${BUNDLEID}"
FunctionEnd

10
tooling/cli/Cargo.lock generated
View File

@ -2992,11 +2992,11 @@ dependencies = [
[[package]]
name = "serde_with"
version = "2.3.3"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07ff71d2c147a7b57362cead5e22f772cd52f6ab31cfcd9edcd7f6aeb2a0afbe"
checksum = "9f02d8aa6e3c385bf084924f660ce2a3a6bd333ba55b35e8590b321f35d88513"
dependencies = [
"base64 0.13.1",
"base64 0.21.1",
"chrono",
"hex",
"indexmap",
@ -3008,9 +3008,9 @@ dependencies = [
[[package]]
name = "serde_with_macros"
version = "2.3.3"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "881b6f881b17d13214e5d494c939ebab463d01264ce1811e9d4ac3a882e7695f"
checksum = "edc7d5d3932fb12ce722ee5e64dd38c504efba37567f0c402f6ca728c3b8b070"
dependencies = [
"darling",
"proc-macro2",

View File

@ -2822,7 +2822,7 @@
]
},
{
"description": "Specifies unattended mode, which means the installation only shows a progress bar. **msi (Wix) Only**",
"description": "Specifies unattended mode, which means the installation only shows a progress bar.",
"type": "string",
"enum": [
"passive"