quick_search/packaging/quicksearch.nsi
= cf40196546
Some checks failed
CI / linux (push) Successful in 2m49s
CI / windows-cross (push) Failing after 1m20s
CI / release (push) Has been skipped
Added index-preserving pruning. Added windows installer to CI, added automated GUI screenshot generation for documentation.
2026-08-04 23:33:28 -04:00

222 lines
9.1 KiB
NSIS

;
; NSIS installer for QuickSearch.
;
; Compiled by packaging/build-installer.sh, which supplies every define below
; and stages the files this installs. Running makensis on this file by hand
; will fail on the !error checks rather than build something half-configured.
;
; VERSION workspace version, e.g. 0.9.1
; VERSION_QUAD the same padded to a.b.c.d, which is all VIProductVersion takes
; STAGE directory holding the exact files to install
; OUTFILE path of the installer to write
;
; makensis runs on Linux, so the installer comes out of the same cross-compile
; job as the .zip and no Windows machine is involved anywhere in the pipeline.
; That also means file paths below use forward slashes: they are read by
; makensis on the build host, unlike $INSTDIR paths, which are Windows strings.
!ifndef VERSION | VERSION_QUAD | STAGE | OUTFILE
!error "build this with packaging/build-installer.sh, which defines VERSION, VERSION_QUAD, STAGE and OUTFILE"
!endif
Unicode true
; The payload is two ~20 MB binaries; solid LZMA is worth the compression time.
SetCompressor /SOLID lzma
!include "MUI2.nsh"
!include "LogicLib.nsh"
!include "FileFunc.nsh"
!include "x64.nsh"
!define APP "QuickSearch"
!define PUBLISHER "Jeremy <jeremy@karsttech.com>"
!define HOMEPAGE "https://code.karsttech.com/jeremy/quick_search"
; Where Add/Remove Programs looks, and where an upgrade finds the directory the
; previous version went into.
!define UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APP}"
Name "${APP} ${VERSION}"
OutFile "${OUTFILE}"
InstallDir "$PROGRAMFILES64\${APP}"
; Program Files and HKLM both need elevation, and it is what puts the shortcuts
; in front of every account on the machine rather than just the installing one.
RequestExecutionLevel admin
ShowInstDetails show
ShowUninstDetails show
VIProductVersion "${VERSION_QUAD}"
VIAddVersionKey "ProductName" "${APP}"
VIAddVersionKey "ProductVersion" "${VERSION}"
VIAddVersionKey "FileVersion" "${VERSION_QUAD}"
VIAddVersionKey "CompanyName" "${PUBLISHER}"
VIAddVersionKey "LegalCopyright" "GPL-3.0-or-later"
VIAddVersionKey "FileDescription" "${APP} ${VERSION} installer"
!define MUI_ABORTWARNING
!define MUI_ICON "${STAGE}/quicksearch.ico"
!define MUI_UNICON "${STAGE}/quicksearch.ico"
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_LICENSE "${STAGE}/LICENSE.txt"
!insertmacro MUI_PAGE_COMPONENTS
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
!define MUI_FINISHPAGE_RUN
!define MUI_FINISHPAGE_RUN_TEXT "Run ${APP}"
!define MUI_FINISHPAGE_RUN_FUNCTION LaunchAsUser
!insertmacro MUI_PAGE_FINISH
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
!insertmacro MUI_LANGUAGE "English"
; ------------------------------------------------------------------ init ----
Function .onInit
${IfNot} ${RunningX64}
MessageBox MB_OK|MB_ICONSTOP \
"${APP} is 64-bit only and this is a 32-bit Windows installation." /SD IDOK
Abort
${EndIf}
; The installer stub is 32-bit (it runs anywhere), so without this every
; HKLM write below would be redirected into Wow6432Node - out of sight of
; 64-bit Add/Remove Programs and of the lookup two lines down.
SetRegView 64
; Shortcuts and the uninstall entry are machine-wide, matching where the
; files go.
SetShellVarContext all
; Deliberately not InstallDirRegKey: that is read before .onInit runs, which
; is before SetRegView 64, so it would look in the wrong registry view.
; $INSTDIR still holds the InstallDir default unless /D= overrode it on the
; command line, and an explicit /D= must win over the previous location.
${If} $INSTDIR == "$PROGRAMFILES64\${APP}"
ReadRegStr $0 HKLM "${UNINST_KEY}" "InstallLocation"
${If} $0 != ""
StrCpy $INSTDIR $0
${EndIf}
${EndIf}
FunctionEnd
Function un.onInit
SetRegView 64
SetShellVarContext all
FunctionEnd
; -------------------------------------------------------------- sections ----
Section "!${APP}" SecApp
SectionIn RO
SetOutPath "$INSTDIR"
; Windows will not replace a running executable, and the File commands below
; would stop halfway through with a write error. Delete fails on a mapped
; image and succeeds quietly when there is nothing there, which makes it a
; plugin-free "is it still running?" test.
ClearErrors
Delete "$INSTDIR\quicksearch.exe"
Delete "$INSTDIR\quicksearch-cli.exe"
${If} ${Errors}
MessageBox MB_OK|MB_ICONSTOP \
"${APP} is still running. Close it and start this installer again." /SD IDOK
Abort
${EndIf}
File "${STAGE}/quicksearch.exe"
File "${STAGE}/quicksearch-cli.exe"
File "${STAGE}/quicksearch.ico"
File "${STAGE}/README.md"
File "${STAGE}/LICENSE.txt"
File "${STAGE}/config_example.toml"
; No config.toml is installed, for the same reason the .deb ships none: one
; sitting next to the binary is portable mode, and it would override the
; personal config of every account on the machine. The app writes
; %APPDATA%\quicksearch\config.toml on first run instead.
WriteUninstaller "$INSTDIR\uninstall.exe"
WriteRegStr HKLM "${UNINST_KEY}" "DisplayName" "${APP}"
WriteRegStr HKLM "${UNINST_KEY}" "DisplayVersion" "${VERSION}"
WriteRegStr HKLM "${UNINST_KEY}" "DisplayIcon" "$INSTDIR\quicksearch.ico"
WriteRegStr HKLM "${UNINST_KEY}" "Publisher" "${PUBLISHER}"
WriteRegStr HKLM "${UNINST_KEY}" "URLInfoAbout" "${HOMEPAGE}"
WriteRegStr HKLM "${UNINST_KEY}" "InstallLocation" "$INSTDIR"
WriteRegStr HKLM "${UNINST_KEY}" "UninstallString" '"$INSTDIR\uninstall.exe"'
WriteRegStr HKLM "${UNINST_KEY}" "QuietUninstallString" '"$INSTDIR\uninstall.exe" /S'
WriteRegDWORD HKLM "${UNINST_KEY}" "NoModify" 1
WriteRegDWORD HKLM "${UNINST_KEY}" "NoRepair" 1
; Add/Remove Programs reads this as a DWORD of kilobytes, which is exactly
; what /S=0K returns.
${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2
IntFmt $0 "0x%08X" $0
WriteRegDWORD HKLM "${UNINST_KEY}" "EstimatedSize" $0
SectionEnd
Section "Start Menu shortcut" SecStartMenu
; One shortcut, no program folder: a single-application folder is noise in
; the Windows 10/11 Start menu, and the uninstaller lives in Add/Remove
; Programs rather than next to it.
CreateShortcut "$SMPROGRAMS\${APP}.lnk" "$INSTDIR\quicksearch.exe" "" "$INSTDIR\quicksearch.ico"
SectionEnd
Section /o "Desktop shortcut" SecDesktop
CreateShortcut "$DESKTOP\${APP}.lnk" "$INSTDIR\quicksearch.exe" "" "$INSTDIR\quicksearch.ico"
SectionEnd
; There is deliberately no "add to PATH" section, tempting as one is for
; quicksearch-cli. Editing PATH means reading the machine value, appending and
; writing it back, and NSIS strings in the standard build are capped at
; NSIS_MAX_STRLEN (1024 characters, `makensis -HDRINFO`). ReadRegStr truncates
; silently at that cap, so on any machine with a long PATH the write-back would
; destroy the rest of it - a spectacular failure for an optional checkbox. The
; README tells people to add the directory themselves.
!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
!insertmacro MUI_DESCRIPTION_TEXT ${SecApp} \
"The desktop app and quicksearch-cli, the terminal search tool."
!insertmacro MUI_DESCRIPTION_TEXT ${SecStartMenu} \
"Add ${APP} to the Start menu for all users."
!insertmacro MUI_DESCRIPTION_TEXT ${SecDesktop} \
"Add a ${APP} shortcut to the desktop."
!insertmacro MUI_FUNCTION_DESCRIPTION_END
; The installer is elevated and anything it starts inherits that. Handing the
; path to the already-running Explorer starts the app as the logged-on user
; instead, so a first run creates its config and index in the right profile.
Function LaunchAsUser
Exec '"$WINDIR\explorer.exe" "$INSTDIR\quicksearch.exe"'
FunctionEnd
; ----------------------------------------------------------- uninstaller ----
Section "Uninstall"
; Same running-process test as the install side. Stopping here leaves the
; installation intact rather than gutted.
ClearErrors
Delete "$INSTDIR\quicksearch.exe"
Delete "$INSTDIR\quicksearch-cli.exe"
${If} ${Errors}
MessageBox MB_OK|MB_ICONSTOP \
"${APP} is still running. Close it and try again." /SD IDOK
Abort
${EndIf}
Delete "$INSTDIR\quicksearch.ico"
Delete "$INSTDIR\README.md"
Delete "$INSTDIR\LICENSE.txt"
Delete "$INSTDIR\config_example.toml"
Delete "$INSTDIR\uninstall.exe"
Delete "$SMPROGRAMS\${APP}.lnk"
Delete "$DESKTOP\${APP}.lnk"
; Plain RMDir, never /r: a portable-mode config.toml and the index beside it
; may be sitting in this directory, and neither is ours to delete. A
; directory holding anything the installer did not put there simply stays.
RMDir "$INSTDIR"
DeleteRegKey HKLM "${UNINST_KEY}"
; The per-user config in %APPDATA%\quicksearch and the index in
; %LOCALAPPDATA%\quicksearch are left alone, the way apt leaves ~/.config
; alone: reinstalling picks up where the last install left off, and nobody
; loses an index to an upgrade.
SectionEnd