feat(windows): rewrite NSIS installer, add pgtidy update command
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
# Windows installer
|
||||
|
||||
NSIS script: `installer.nsi`. Output: `dist/pgtidy-setup-windows-amd64.exe`.
|
||||
|
||||
## Build
|
||||
|
||||
```
|
||||
make installer-windows
|
||||
```
|
||||
|
||||
Needs `makensis` (package `nsis`). CI builds and uploads it on release.
|
||||
|
||||
## Installer
|
||||
|
||||
- Installs `pgtidy.exe` to `%ProgramFiles%\PgTidy` (admin).
|
||||
- Adds the install dir to the system `PATH`; removed on uninstall.
|
||||
- Registers in Add/Remove Programs (supports silent `/S` uninstall).
|
||||
|
||||
## Update check
|
||||
|
||||
```
|
||||
pgtidy update # check, prompt, download + run installer (Windows)
|
||||
pgtidy update --check # report only
|
||||
pgtidy update --yes # no prompt
|
||||
```
|
||||
|
||||
- Source: latest Gitea release (`pkg/updatecheck`).
|
||||
- `dev` and commit-hash builds are never reported as outdated.
|
||||
- Non-Windows: prints the release URL.
|
||||
+84
-191
@@ -1,35 +1,43 @@
|
||||
; PgTidy Windows installer
|
||||
; PgTidy Windows installer (NSIS)
|
||||
;
|
||||
; Build with (VERSION must match the release tag, without the leading "v"):
|
||||
; makensis /DVERSION=1.2.3 /DSRC_EXE=path\to\pgtidy.exe windows\installer.nsi
|
||||
;
|
||||
; Installs pgtidy.exe into Program Files, adds the install dir to the
|
||||
; machine-wide PATH, and on startup checks the git.warky.dev Gitea API for a
|
||||
; newer release than the one being installed.
|
||||
; Build: makensis -DVERSION=1.2.3 -DEXE=<abs path to pgtidy.exe> -DOUT=<abs path to installer> windows/installer.nsi
|
||||
; Use absolute paths for EXE and OUT; relative ones resolve unpredictably.
|
||||
|
||||
!ifndef VERSION
|
||||
!define VERSION "0.0.0"
|
||||
!endif
|
||||
!ifndef SRC_EXE
|
||||
!define SRC_EXE "..\dist\pgtidy.exe"
|
||||
!ifndef OUT
|
||||
!define OUT "pgtidy-setup-windows-amd64.exe"
|
||||
!endif
|
||||
!ifndef EXE
|
||||
!define EXE "pgtidy-windows-amd64.exe"
|
||||
!endif
|
||||
|
||||
!define PRODUCT_NAME "PgTidy"
|
||||
!define PRODUCT_PUBLISHER "Warky Devs"
|
||||
!define PRODUCT_HOMEPAGE "https://git.warky.dev/wdevs/pgtidy"
|
||||
!define RELEASES_API_URL "https://git.warky.dev/api/v1/repos/wdevs/pgtidy/releases/latest"
|
||||
!define UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\PgTidy"
|
||||
!define ENV_KEY 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"'
|
||||
!define APPNAME "PgTidy"
|
||||
!define REGKEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\PgTidy"
|
||||
!define ENVKEY "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"
|
||||
|
||||
Unicode true
|
||||
Name "${APPNAME} ${VERSION}"
|
||||
OutFile "${OUT}"
|
||||
InstallDir "$PROGRAMFILES64\${APPNAME}"
|
||||
InstallDirRegKey HKLM "Software\${APPNAME}" "InstallDir"
|
||||
RequestExecutionLevel admin
|
||||
SetCompressor /SOLID lzma
|
||||
|
||||
VIProductVersion "${VERSION}.0"
|
||||
VIAddVersionKey "ProductName" "${APPNAME}"
|
||||
VIAddVersionKey "FileDescription" "${APPNAME} installer"
|
||||
VIAddVersionKey "FileVersion" "${VERSION}"
|
||||
VIAddVersionKey "ProductVersion" "${VERSION}"
|
||||
VIAddVersionKey "LegalCopyright" "Warky Devs"
|
||||
|
||||
!include "MUI2.nsh"
|
||||
!include "LogicLib.nsh"
|
||||
|
||||
Name "${PRODUCT_NAME} ${VERSION}"
|
||||
OutFile "pgtidy-setup-${VERSION}.exe"
|
||||
InstallDir "$PROGRAMFILES64\PgTidy"
|
||||
InstallDirRegKey HKLM "${UNINST_KEY}" "InstallLocation"
|
||||
RequestExecutionLevel admin
|
||||
Unicode true
|
||||
!include "StrFunc.nsh"
|
||||
!include "WinMessages.nsh"
|
||||
${StrStr}
|
||||
${UnStrRep}
|
||||
|
||||
!define MUI_ABORTWARNING
|
||||
!define MUI_ICON "..\assets\logo_128.ico"
|
||||
@@ -40,187 +48,72 @@ Unicode true
|
||||
!insertmacro MUI_PAGE_DIRECTORY
|
||||
!insertmacro MUI_PAGE_INSTFILES
|
||||
!insertmacro MUI_PAGE_FINISH
|
||||
|
||||
!insertmacro MUI_UNPAGE_CONFIRM
|
||||
!insertmacro MUI_UNPAGE_INSTFILES
|
||||
|
||||
!insertmacro MUI_LANGUAGE "English"
|
||||
|
||||
; ---------------------------------------------------------------------------
|
||||
; Check the Gitea releases API for a newer version than the one we are about
|
||||
; to install. Best-effort only: any failure (offline, API down, no
|
||||
; PowerShell) is swallowed and the installer proceeds silently.
|
||||
; ---------------------------------------------------------------------------
|
||||
Function .onInit
|
||||
StrCpy $1 "$TEMP\pgtidy-latest-version.txt"
|
||||
Delete "$1"
|
||||
|
||||
DetailPrint "Checking ${PRODUCT_HOMEPAGE} for a newer release..."
|
||||
nsExec::ExecToLog 'powershell -NoProfile -NonInteractive -Command "try { $$r = Invoke-RestMethod -Uri ''${RELEASES_API_URL}'' -UseBasicParsing -TimeoutSec 5; $$r.tag_name | Out-File -Encoding ascii -NoNewline ''$1'' } catch { exit 0 }"'
|
||||
|
||||
${IfNot} ${FileExists} "$1"
|
||||
Return
|
||||
${EndIf}
|
||||
|
||||
FileOpen $2 "$1" r
|
||||
FileRead $2 $3
|
||||
FileClose $2
|
||||
Delete "$1"
|
||||
|
||||
StrCpy $4 $3
|
||||
; Trim a leading "v" if the tag is e.g. "v1.2.3"
|
||||
StrCpy $5 $4 1
|
||||
${If} $5 == "v"
|
||||
StrCpy $4 $4 "" 1
|
||||
${EndIf}
|
||||
|
||||
${If} $4 != ""
|
||||
${AndIf} $4 != "${VERSION}"
|
||||
MessageBox MB_YESNO|MB_ICONINFORMATION \
|
||||
"A newer version of PgTidy is available: $4 (this installer is ${VERSION}).$\n$\nOpen the releases page to download it now?$\n$\nChoosing No continues installing ${VERSION}." \
|
||||
IDNO +2
|
||||
ExecShell "open" "${PRODUCT_HOMEPAGE}/releases/latest"
|
||||
${EndIf}
|
||||
FunctionEnd
|
||||
|
||||
; ---------------------------------------------------------------------------
|
||||
; Adds $INSTDIR to the machine PATH if it isn't already present.
|
||||
; ---------------------------------------------------------------------------
|
||||
Function AddToPath
|
||||
ReadRegStr $0 ${ENV_KEY} "Path"
|
||||
Push "$0"
|
||||
Push "$INSTDIR"
|
||||
Call StrContains
|
||||
Pop $1
|
||||
${If} $1 == ""
|
||||
${If} $0 == ""
|
||||
StrCpy $0 "$INSTDIR"
|
||||
${Else}
|
||||
StrCpy $0 "$0;$INSTDIR"
|
||||
${EndIf}
|
||||
WriteRegExpandStr ${ENV_KEY} "Path" "$0"
|
||||
SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
|
||||
${EndIf}
|
||||
FunctionEnd
|
||||
|
||||
; ---------------------------------------------------------------------------
|
||||
; Removes $INSTDIR from the machine PATH.
|
||||
; ---------------------------------------------------------------------------
|
||||
Function un.RemoveFromPath
|
||||
ReadRegStr $0 ${ENV_KEY} "Path"
|
||||
Push "$0;"
|
||||
Push "$INSTDIR;"
|
||||
Push ""
|
||||
Call un.StrReplace
|
||||
Pop $0
|
||||
Push "$0"
|
||||
Push "$INSTDIR"
|
||||
Push ""
|
||||
Call un.StrReplace
|
||||
Pop $0
|
||||
; Drop a trailing separator left behind by the replacements above.
|
||||
StrCpy $1 $0 1 -1
|
||||
${If} $1 == ";"
|
||||
StrCpy $0 $0 -1
|
||||
${EndIf}
|
||||
WriteRegExpandStr ${ENV_KEY} "Path" "$0"
|
||||
SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
|
||||
FunctionEnd
|
||||
|
||||
; Returns the index of needle in haystack via $R0, or "" if absent.
|
||||
; Push haystack, Push needle -> Pop result
|
||||
Function StrContains
|
||||
Exch $R1 ; needle
|
||||
Exch
|
||||
Exch $R2 ; haystack
|
||||
Push $R3
|
||||
Push $R4
|
||||
Push $R5
|
||||
StrLen $R3 $R1
|
||||
StrCpy $R4 0
|
||||
${Do}
|
||||
StrCpy $R5 $R2 $R3 $R4
|
||||
${If} $R5 == $R1
|
||||
StrCpy $R0 $R4
|
||||
${ExitDo}
|
||||
${EndIf}
|
||||
${If} $R5 == ""
|
||||
StrCpy $R0 ""
|
||||
${ExitDo}
|
||||
${EndIf}
|
||||
IntOp $R4 $R4 + 1
|
||||
${Loop}
|
||||
Pop $R5
|
||||
Pop $R4
|
||||
Pop $R3
|
||||
Pop $R2
|
||||
Pop $R1
|
||||
Push $R0
|
||||
Exch
|
||||
Pop $R0
|
||||
FunctionEnd
|
||||
|
||||
; Push string, Push search, Push replace -> Pop result
|
||||
Function un.StrReplace
|
||||
Exch $R0 ; replace
|
||||
Exch
|
||||
Exch $R1 ; search
|
||||
Exch 2
|
||||
Exch $R2 ; string
|
||||
Push $R3
|
||||
Push $R4
|
||||
Push $R5
|
||||
Push $R6
|
||||
StrLen $R3 $R1
|
||||
StrCpy $R4 ""
|
||||
${Do}
|
||||
StrCpy $R5 $R2 $R3
|
||||
${If} $R5 == $R1
|
||||
StrCpy $R4 "$R4$R0"
|
||||
StrCpy $R2 $R2 "" $R3
|
||||
${ElseIf} $R2 == ""
|
||||
${ExitDo}
|
||||
${Else}
|
||||
StrCpy $R6 $R2 1
|
||||
StrCpy $R4 "$R4$R6"
|
||||
StrCpy $R2 $R2 "" 1
|
||||
${EndIf}
|
||||
${Loop}
|
||||
Pop $R6
|
||||
Pop $R5
|
||||
Pop $R4
|
||||
Pop $R3
|
||||
Pop $R2
|
||||
Pop $R1
|
||||
Pop $R0
|
||||
Push $R4
|
||||
FunctionEnd
|
||||
|
||||
Section "PgTidy" SEC_MAIN
|
||||
SectionIn RO
|
||||
Section "Install"
|
||||
SetRegView 64
|
||||
SetOutPath "$INSTDIR"
|
||||
File "${SRC_EXE}"
|
||||
File "/oname=pgtidy.exe" "${EXE}"
|
||||
File "..\LICENSE"
|
||||
|
||||
Call AddToPath
|
||||
|
||||
WriteRegStr HKLM "${UNINST_KEY}" "DisplayName" "${PRODUCT_NAME}"
|
||||
WriteRegStr HKLM "${UNINST_KEY}" "DisplayVersion" "${VERSION}"
|
||||
WriteRegStr HKLM "${UNINST_KEY}" "Publisher" "${PRODUCT_PUBLISHER}"
|
||||
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
|
||||
|
||||
WriteUninstaller "$INSTDIR\uninstall.exe"
|
||||
|
||||
WriteRegStr HKLM "Software\${APPNAME}" "InstallDir" "$INSTDIR"
|
||||
WriteRegStr HKLM "${REGKEY}" "DisplayName" "${APPNAME}"
|
||||
WriteRegStr HKLM "${REGKEY}" "DisplayVersion" "${VERSION}"
|
||||
WriteRegStr HKLM "${REGKEY}" "Publisher" "Warky Devs"
|
||||
WriteRegStr HKLM "${REGKEY}" "DisplayIcon" "$INSTDIR\pgtidy.exe"
|
||||
WriteRegStr HKLM "${REGKEY}" "InstallLocation" "$INSTDIR"
|
||||
WriteRegStr HKLM "${REGKEY}" "UninstallString" '"$INSTDIR\uninstall.exe"'
|
||||
WriteRegStr HKLM "${REGKEY}" "QuietUninstallString" '"$INSTDIR\uninstall.exe" /S'
|
||||
WriteRegDWORD HKLM "${REGKEY}" "NoModify" 1
|
||||
WriteRegDWORD HKLM "${REGKEY}" "NoRepair" 1
|
||||
|
||||
; Add the install directory to the system PATH unless it is already there.
|
||||
ReadRegStr $0 HKLM "${ENVKEY}" "Path"
|
||||
${StrStr} $1 ";$0;" ";$INSTDIR;"
|
||||
${If} $1 == ""
|
||||
StrLen $2 $0
|
||||
${If} $2 > 900
|
||||
; NSIS strings are limited; rewriting a very long PATH could truncate it.
|
||||
MessageBox MB_OK|MB_ICONEXCLAMATION "PATH is too long to update automatically. Add $INSTDIR to PATH manually."
|
||||
${Else}
|
||||
${If} $0 == ""
|
||||
WriteRegExpandStr HKLM "${ENVKEY}" "Path" "$INSTDIR"
|
||||
${Else}
|
||||
WriteRegExpandStr HKLM "${ENVKEY}" "Path" "$0;$INSTDIR"
|
||||
${EndIf}
|
||||
SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
SectionEnd
|
||||
|
||||
Section "Uninstall"
|
||||
Call un.RemoveFromPath
|
||||
SetRegView 64
|
||||
Delete "$INSTDIR\pgtidy.exe"
|
||||
Delete "$INSTDIR\LICENSE"
|
||||
Delete "$INSTDIR\uninstall.exe"
|
||||
RMDir "$INSTDIR"
|
||||
DeleteRegKey HKLM "${UNINST_KEY}"
|
||||
|
||||
; Remove the install directory from the system PATH.
|
||||
ReadRegStr $0 HKLM "${ENVKEY}" "Path"
|
||||
; Wrap in separators so every entry, including the first and last, matches.
|
||||
StrCpy $1 ";$0;"
|
||||
${UnStrRep} $1 "$1" ";$INSTDIR;" ";"
|
||||
StrCpy $2 $1 1
|
||||
${If} $2 == ";"
|
||||
StrCpy $1 $1 "" 1
|
||||
${EndIf}
|
||||
StrCpy $2 $1 1 -1
|
||||
${If} $2 == ";"
|
||||
StrCpy $1 $1 -1
|
||||
${EndIf}
|
||||
${If} $1 != $0
|
||||
WriteRegExpandStr HKLM "${ENVKEY}" "Path" "$1"
|
||||
SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
|
||||
${EndIf}
|
||||
|
||||
DeleteRegKey HKLM "${REGKEY}"
|
||||
DeleteRegKey HKLM "Software\${APPNAME}"
|
||||
SectionEnd
|
||||
|
||||
Reference in New Issue
Block a user