返回 DeepSeek-Reasonix
project.nsi
根目录 / desktop / build / windows / installer / project.nsi
1 Unicode true
2
3 SetCompressor /SOLID /FINAL lzma
4 SetCompressorDictSize 32
5
6 ####
7 ## Reasonix per-user NSIS installer (Electron shell).
8 ##
9 ## This file is COMMITTED and fully self-contained: the Electron packaging
10 ## script (desktop/packaging/package.mjs) generates reasonix_project.nsh with
11 ## the INFO_* identity defines, and every macro the old Wails template provided
12 ## is inlined below. The customizations vs. a stock NSIS template:
13 ##
14 ## 1. REQUEST_EXECUTION_LEVEL "user" + InstallDir under $LOCALAPPDATA - install
15 ## without administrator rights. This lets the auto-updater re-run a freshly
16 ## downloaded installer in a visible progress-only mode with no UAC prompt.
17 ## 2. Uninstall registry under HKCU (not HKLM) - a non-admin install cannot
18 ## write HKLM, so the uninstaller macros below use HKCU.
19 ## 3. InstallDir is remembered across updates via InstallDirRegKey +
20 ## InstallLocation (HKCU\...\Uninstall\InstallLocation). When upgrading from
21 ## a build that did not write InstallLocation yet, .onInit falls back to the
22 ## old DisplayIcon path before using the default. Without this, every release
23 ## forces the user back to %LOCALAPPDATA%\Programs\Reasonix even if they had
24 ## moved the install to a different drive (e.g. D:\Tools\Reasonix); the
25 ## auto-updater would overwrite the wrong dir, leaving the old install
26 ## orphaned.
27 ## 4. The payload is the flat Go executables plus the Electron app/ tree,
28 ## installed recursively with `File /r` into the versioned staging
29 ## directory that the signed Go activator publishes as versions/v<ver>/.
30 ####
31
32 ## Install per-user (no admin).
33 !define REQUEST_EXECUTION_LEVEL "user"
34
35 ####
36 ## Product identity. REASONIX_VERSION_TAG is the release/install identity;
37 ## INFO_PRODUCTVERSION is numeric metadata only.
38 ####
39 !if /FileExists "reasonix_project.nsh"
40 !include "reasonix_project.nsh"
41 !else
42 !error "reasonix_project.nsh is missing; run desktop/packaging/package.mjs first"
43 !endif
44 !include "x64.nsh"
45 !include "WinVer.nsh"
46 !include "FileFunc.nsh"
47 !include "LogicLib.nsh"
48
49 # The build script writes this host-specific include before invoking makensis.
50 # Keep a Windows fallback so opening this script directly still behaves like a
51 # native Windows build.
52 !if /FileExists "reasonix_host.nsh"
53 !include "reasonix_host.nsh"
54 !endif
55 !ifndef REASONIX_UNINST_FINALIZE
56 !define REASONIX_UNINST_FINALIZE 'cmd.exe /C copy /Y "%1" "reasonix-uninstall.exe" >NUL'
57 !endif
58
59 # The service executable stays the active version entry the thin launcher
60 # starts; it bootstraps app\Reasonix.exe (Electron) and exits.
61 !define PRODUCT_EXECUTABLE "${INFO_PROJECTNAME}.exe"
62 !define REASONIX_ELECTRON_EXECUTABLE "Reasonix.exe"
63 !define UNINST_KEY_NAME "${INFO_COMPANYNAME}${INFO_PRODUCTNAME}"
64 !define UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINST_KEY_NAME}"
65 RequestExecutionLevel "${REQUEST_EXECUTION_LEVEL}"
66
67 # Exactly one target architecture per installer, selected by the build script
68 # through the binary define it passes (values point at the staged service
69 # executable; only their presence selects the architecture).
70 !ifdef ARG_REASONIX_AMD64_BINARY
71 !define ARCH "amd64"
72 !endif
73 !ifdef ARG_REASONIX_ARM64_BINARY
74 !define ARCH "arm64"
75 !endif
76 !ifndef ARCH
77 !error "one of ARG_REASONIX_AMD64_BINARY or ARG_REASONIX_ARM64_BINARY is required; package-windows-desktop.sh passes it"
78 !endif
79
80 !macro reasonix.checkArchitecture
81 ${If} ${AtLeastWin10}
82 !if "${ARCH}" == "amd64"
83 ${if} ${IsNativeAMD64}
84 Goto reasonix_arch_ok
85 ${EndIf}
86 !else
87 ${if} ${IsNativeARM64}
88 Goto reasonix_arch_ok
89 ${EndIf}
90 !endif
91
92 IfSilent reasonix_arch_silent reasonix_arch_interactive
93 reasonix_arch_silent:
94 SetErrorLevel 65
95 Abort
96 reasonix_arch_interactive:
97 MessageBox MB_OK "This product can't be installed on the current Windows architecture. Supports: ${ARCH}"
98 Quit
99 ${else}
100 IfSilent reasonix_win_silent reasonix_win_interactive
101 reasonix_win_silent:
102 SetErrorLevel 64
103 Abort
104 reasonix_win_interactive:
105 MessageBox MB_OK "This product is only supported on Windows 10 (Server 2016) and later."
106 Quit
107 ${EndIf}
108
109 reasonix_arch_ok:
110 !macroend
111
112 !macro reasonix.setShellContext
113 ${If} ${REQUEST_EXECUTION_LEVEL} == "admin"
114 SetShellVarContext all
115 ${else}
116 SetShellVarContext current
117 ${EndIf}
118 !macroend
119
120 # The release unit: the Go service executable plus the Electron app/ tree.
121 # package-windows-desktop.sh stages both next to this script before makensis.
122 !macro reasonix.files
123 File "/oname=${PRODUCT_EXECUTABLE}" "${PRODUCT_EXECUTABLE}"
124 !if /FileExists "app\${REASONIX_ELECTRON_EXECUTABLE}"
125 File /r "app"
126 !else
127 !error "the Electron app tree is missing; run desktop/packaging/package.mjs first"
128 !endif
129 !macroend
130
131 # Reasonix registers no file associations or custom protocols; keep the hooks
132 # as no-ops so the install/uninstall flow keeps its shape.
133 !macro reasonix.associateFiles
134 !macroend
135
136 !macro reasonix.unassociateFiles
137 !macroend
138
139 !macro reasonix.associateCustomProtocols
140 !macroend
141
142 !macro reasonix.unassociateCustomProtocols
143 !macroend
144
145 # The version information for this two must consist of 4 parts
146 VIProductVersion "${INFO_PRODUCTVERSION}.0"
147 VIFileVersion "${INFO_PRODUCTVERSION}.0"
148
149 VIAddVersionKey "CompanyName" "${INFO_COMPANYNAME}"
150 VIAddVersionKey "FileDescription" "${INFO_PRODUCTNAME} Installer"
151 VIAddVersionKey "ProductVersion" "${REASONIX_DISPLAY_VERSION}"
152 VIAddVersionKey "FileVersion" "${REASONIX_DISPLAY_VERSION}"
153 VIAddVersionKey "LegalCopyright" "${INFO_COPYRIGHT}"
154 VIAddVersionKey "ProductName" "${INFO_PRODUCTNAME}"
155
156 # Enable HiDPI support. https://nsis.sourceforge.io/Reference/ManifestDPIAware
157 ManifestDPIAware true
158
159 !include "MUI.nsh"
160
161 !define MUI_ICON "..\icon.ico"
162 !define MUI_UNICON "..\icon.ico"
163 # !define MUI_WELCOMEFINISHPAGE_BITMAP "resources\leftimage.bmp" #Include this to add a bitmap on the left side of the Welcome Page. Must be a size of 164x314
164 !define MUI_FINISHPAGE_NOAUTOCLOSE # Wait on the INSTFILES page so the user can take a look into the details of the installation steps
165 !define MUI_ABORTWARNING # This will warn the user if they exit from the installer.
166
167 !define MUI_PAGE_CUSTOMFUNCTION_PRE reasonix.skipSetupPageForUpdate
168 !insertmacro MUI_PAGE_WELCOME # Welcome to the installer page.
169 # !insertmacro MUI_PAGE_LICENSE "resources\eula.txt" # Adds a EULA page to the installer
170 !define MUI_PAGE_CUSTOMFUNCTION_PRE reasonix.skipSetupPageForUpdate
171 !insertmacro MUI_PAGE_DIRECTORY # In which folder install page.
172 !define MUI_PAGE_CUSTOMFUNCTION_SHOW reasonix.showUpdateProgress
173 !insertmacro MUI_PAGE_INSTFILES # Installing page.
174 !define MUI_PAGE_CUSTOMFUNCTION_PRE reasonix.skipFinishPageForUpdate
175 !insertmacro MUI_PAGE_FINISH # Finished installation page.
176
177 !insertmacro MUI_UNPAGE_INSTFILES # Uinstalling page
178
179 !insertmacro MUI_LANGUAGE "English"
180 !insertmacro MUI_LANGUAGE "SimpChinese"
181 !insertmacro MUI_LANGUAGE "TradChinese"
182
183 LangString reasonixUpdateTitle ${LANG_ENGLISH} "Updating Reasonix"
184 LangString reasonixUpdateTitle ${LANG_SIMPCHINESE} "正在更新 Reasonix"
185 LangString reasonixUpdateTitle ${LANG_TRADCHINESE} "正在更新 Reasonix"
186 LangString reasonixUpdateSubtitle ${LANG_ENGLISH} "Installing the verified update. Reasonix will restart automatically."
187 LangString reasonixUpdateSubtitle ${LANG_SIMPCHINESE} "正在安装已验证的更新,完成后 Reasonix 将自动重启。"
188 LangString reasonixUpdateSubtitle ${LANG_TRADCHINESE} "正在安裝已驗證的更新,完成後 Reasonix 將自動重新啟動。"
189 LangString reasonixActivateBusy ${LANG_ENGLISH} "Reasonix is still running or another installation is in progress. Close it and click Retry. Details: %APPDATA%\reasonix\desktop-shell\logs\recovery.log"
190 LangString reasonixActivateBusy ${LANG_SIMPCHINESE} "Reasonix 仍在运行,或另一个安装正在进行。请关闭后点击“重试”。详情见 %APPDATA%\reasonix\desktop-shell\logs\recovery.log"
191 LangString reasonixActivateBusy ${LANG_TRADCHINESE} "Reasonix 仍在執行,或另一個安裝正在進行。請關閉後點擊「重試」。詳情見 %APPDATA%\reasonix\desktop-shell\logs\recovery.log"
192 LangString reasonixActivateLocked ${LANG_ENGLISH} "Reasonix could not activate the release, often because a file was temporarily locked by antivirus or sync software. Wait a moment and click Retry. Details: %APPDATA%\reasonix\desktop-shell\logs\recovery.log"
193 LangString reasonixActivateLocked ${LANG_SIMPCHINESE} "Reasonix 无法启用新版本,通常是文件被杀毒或同步软件临时锁定。请稍候再点击“重试”。详情见 %APPDATA%\reasonix\desktop-shell\logs\recovery.log"
194 LangString reasonixActivateLocked ${LANG_TRADCHINESE} "Reasonix 無法啟用新版本,通常是檔案被防毒或同步軟體暫時鎖定。請稍候再點擊「重試」。詳情見 %APPDATA%\reasonix\desktop-shell\logs\recovery.log"
195
196 ## Preserve the first-pass generated uninstaller so the release workflow can
197 ## Authenticode-sign it together with the other installed payload files.
198 ## The second pass provides ARG_REASONIX_SIGNED_UNINSTALLER and embeds that
199 ## signed binary instead of generating another unsigned uninstaller.
200 !ifndef ARG_REASONIX_SIGNED_UNINSTALLER
201 !uninstfinalize '${REASONIX_UNINST_FINALIZE}'
202 !endif
203 #!finalize 'signtool --file "%1"'
204
205 Name "${INFO_PRODUCTNAME}"
206 OutFile "..\..\bin\${INFO_PROJECTNAME}-${ARCH}-installer.exe" # Name of the installer's file.
207 !define REASONIX_DEFAULT_INSTALLDIR "$LOCALAPPDATA\Programs\${INFO_PRODUCTNAME}"
208 !define REASONIX_UPDATE_HELPER "reasonix-update-helper.exe"
209 !define REASONIX_GUARD "reasonix-guard.exe"
210 !define REASONIX_LAUNCHER "reasonix-launcher.exe"
211 !define REASONIX_CLI "reasonix-cli.exe"
212 !define REASONIX_PORTABLE_ENTRY "Reasonix.exe"
213 !define REASONIX_LAYOUT_INSTALLER "reasonix-layout-installer.exe"
214 !define REASONIX_PAYLOAD_MANIFEST "reasonix-payload.json"
215 !define REASONIX_PAYLOAD_SIGNATURE "reasonix-payload.json.minisig"
216 !define REASONIX_LEGACY_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\Reasonix"
217 !define REASONIX_LEGACY_PRODUCT_KEY "Software\reasonix\Reasonix"
218 Var ReasonixUpdateMode
219 Var ReasonixStageMode
220 InstallDirRegKey HKCU "${UNINST_KEY}" "InstallLocation" # Reuse the previous install path on update; .onInit falls back to the default on first install.
221 InstallDir "${REASONIX_DEFAULT_INSTALLDIR}" # Per-user install location (no admin rights required).
222 ShowInstDetails show # This will always show the installation details.
223
224 ####
225 ## Per-user uninstaller registry (HKCU). HKLM writes would fail without admin
226 ## rights, so the uninstaller registration lives entirely under HKCU.
227 ####
228 !macro reasonix.writeUninstaller
229 !ifdef ARG_REASONIX_SIGNED_UNINSTALLER
230 File "/oname=uninstall.exe" "${ARG_REASONIX_SIGNED_UNINSTALLER}"
231 !else
232 WriteUninstaller "$INSTDIR\uninstall.exe"
233 !endif
234
235 WriteRegStr HKCU "${UNINST_KEY}" "Publisher" "${INFO_COMPANYNAME}"
236 WriteRegStr HKCU "${UNINST_KEY}" "DisplayName" "${INFO_PRODUCTNAME}"
237 WriteRegStr HKCU "${UNINST_KEY}" "DisplayVersion" "${REASONIX_DISPLAY_VERSION}"
238 !if /FileExists "${REASONIX_LAUNCHER}"
239 WriteRegStr HKCU "${UNINST_KEY}" "DisplayIcon" "$INSTDIR\${REASONIX_PORTABLE_ENTRY}"
240 !else
241 WriteRegStr HKCU "${UNINST_KEY}" "DisplayIcon" "$INSTDIR\${PRODUCT_EXECUTABLE}"
242 !endif
243 WriteRegStr HKCU "${UNINST_KEY}" "UninstallString" "$\"$INSTDIR\uninstall.exe$\""
244 WriteRegStr HKCU "${UNINST_KEY}" "QuietUninstallString" "$\"$INSTDIR\uninstall.exe$\" /S"
245 # Persist the resolved install path so a subsequent update picks it up
246 # via InstallDirRegKey above. Without this, every release would force the
247 # user back to %LOCALAPPDATA%\Programs\Reasonix even if they had moved
248 # the install to a different drive (e.g. D:\Tools\Reasonix). The auto-
249 # updater trusts this persisted path, so it has to be present before the
250 # visible progress-only re-install.
251 WriteRegStr HKCU "${UNINST_KEY}" "InstallLocation" "$INSTDIR"
252
253 ${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2
254 IntFmt $0 "0x%08X" $0
255 WriteRegDWORD HKCU "${UNINST_KEY}" "EstimatedSize" "$0"
256 !macroend
257
258 ; Tauri 0.53 separately persisted $INSTDIR under its manufacturer/product key
259 ; and restores that value before every later install. Clear only a same-root
260 ; value so re-running 0.53 cannot overwrite the current uninstaller; preserve a
261 ; genuinely separate legacy installation. If this cleanup fails, retain the old
262 ; uninstall alias so a later update can retry the migration.
263 !macro reasonix.deleteLegacyInstallerStateIfOwned
264 StrCpy $1 "1"
265 ClearErrors
266 ReadRegStr $0 HKCU "${REASONIX_LEGACY_PRODUCT_KEY}" ""
267 ${If} $0 == "$INSTDIR"
268 ClearErrors
269 DeleteRegValue HKCU "${REASONIX_LEGACY_PRODUCT_KEY}" ""
270 ${If} ${Errors}
271 StrCpy $1 "0"
272 ${EndIf}
273 ${ElseIf} $0 == "$\"$INSTDIR$\""
274 ClearErrors
275 DeleteRegValue HKCU "${REASONIX_LEGACY_PRODUCT_KEY}" ""
276 ${If} ${Errors}
277 StrCpy $1 "0"
278 ${EndIf}
279 ${EndIf}
280
281 ${If} $1 == "1"
282 ClearErrors
283 ReadRegStr $0 HKCU "${REASONIX_LEGACY_UNINST_KEY}" "InstallLocation"
284 ${If} $0 == "$INSTDIR"
285 DeleteRegKey HKCU "${REASONIX_LEGACY_UNINST_KEY}"
286 ${ElseIf} $0 == "$\"$INSTDIR$\""
287 DeleteRegKey HKCU "${REASONIX_LEGACY_UNINST_KEY}"
288 ${Else}
289 ClearErrors
290 ReadRegStr $0 HKCU "${REASONIX_LEGACY_UNINST_KEY}" "UninstallString"
291 ${If} $0 == "$INSTDIR\uninstall.exe"
292 DeleteRegKey HKCU "${REASONIX_LEGACY_UNINST_KEY}"
293 ${ElseIf} $0 == "$\"$INSTDIR\uninstall.exe$\""
294 DeleteRegKey HKCU "${REASONIX_LEGACY_UNINST_KEY}"
295 ${EndIf}
296 ${EndIf}
297 ${EndIf}
298 !macroend
299
300 !macro reasonix.deleteUninstaller
301 Delete "$INSTDIR\uninstall.exe"
302 DeleteRegKey HKCU "${UNINST_KEY}"
303 !macroend
304
305 Function .onInit
306 !ifdef ARG_REASONIX_UNINSTALLER_ONLY
307 ; This compiler artifact exists only to extract the shared uninstaller.
308 ; It is never an installable or publishable product.
309 Quit
310 !endif
311 !insertmacro reasonix.checkArchitecture
312
313 ; The helper passes /REASONIXUPDATE=1 and a final /D=<current directory>.
314 ; This mode remains visible but skips every page that could change the
315 ; destination, then closes automatically after the file copy so the helper
316 ; can relaunch Reasonix. A normal manual installer keeps the full wizard.
317 StrCpy $ReasonixUpdateMode "0"
318 StrCpy $ReasonixStageMode "0"
319 ${GetParameters} $R0
320 ClearErrors
321 ${GetOptions} $R0 "/REASONIXUPDATE=" $R1
322 IfErrors reasonix_update_mode_done
323 StrCmp $R1 "1" 0 reasonix_update_mode_done
324 StrCpy $ReasonixUpdateMode "1"
325
326 reasonix_update_mode_done:
327 ClearErrors
328 ${GetOptions} $R0 "/REASONIXSTAGE=" $R2
329 IfErrors reasonix_stage_mode_done
330 StrCmp $R2 "1" 0 reasonix_stage_mode_done
331 StrCpy $ReasonixStageMode "1"
332
333 reasonix_stage_mode_done:
334
335 ; InstallDirRegKey leaves $INSTDIR empty when the InstallLocation value is
336 ; missing. Older installers still wrote DisplayIcon, so use its parent folder
337 ; as a compatibility bridge before falling back to the per-user default.
338 StrCmp $INSTDIR "" 0 done
339 ClearErrors
340 ReadRegStr $0 HKCU "${UNINST_KEY}" "DisplayIcon"
341 IfErrors legacy_location
342 StrCmp $0 "" legacy_location
343 ${GetParent} "$0" $INSTDIR
344 StrCmp $INSTDIR "" legacy_location done
345
346 legacy_location:
347 ; Tauri 0.53 used a different uninstall key and may have stored the selected
348 ; directory with surrounding quotes (for example "D:\Reasonix"). Reuse it
349 ; only while its uninstaller still exists so a stale registry value cannot
350 ; redirect the repair installer into an unrelated directory.
351 ClearErrors
352 ReadRegStr $0 HKCU "${REASONIX_LEGACY_UNINST_KEY}" "InstallLocation"
353 IfErrors legacy_uninstaller
354 StrCmp $0 "" legacy_uninstaller
355 StrCpy $1 $0 1
356 StrCmp $1 "$\"" 0 legacy_location_ready
357 StrCpy $1 $0 1 -1
358 StrCmp $1 "$\"" 0 legacy_location_ready
359 StrCpy $0 $0 -1 1
360
361 legacy_location_ready:
362 IfFileExists "$0\uninstall.exe" 0 legacy_uninstaller
363 StrCpy $INSTDIR $0
364 Goto done
365
366 legacy_uninstaller:
367 ClearErrors
368 ReadRegStr $0 HKCU "${REASONIX_LEGACY_UNINST_KEY}" "UninstallString"
369 IfErrors fallback
370 StrCmp $0 "" fallback
371 StrCpy $1 $0 1
372 StrCmp $1 "$\"" 0 legacy_uninstaller_ready
373 StrCpy $1 $0 1 -1
374 StrCmp $1 "$\"" 0 legacy_uninstaller_ready
375 StrCpy $0 $0 -1 1
376
377 legacy_uninstaller_ready:
378 IfFileExists "$0" 0 fallback
379 ${GetParent} "$0" $INSTDIR
380 StrCmp $INSTDIR "" fallback done
381
382 fallback:
383 StrCpy $INSTDIR "${REASONIX_DEFAULT_INSTALLDIR}"
384 done:
385 FunctionEnd
386
387 Function reasonix.skipSetupPageForUpdate
388 StrCmp $ReasonixUpdateMode "1" 0 reasonix_show_setup_page
389 Abort
390
391 reasonix_show_setup_page:
392 FunctionEnd
393
394 Function reasonix.showUpdateProgress
395 StrCmp $ReasonixUpdateMode "1" 0 reasonix_update_progress_done
396 !insertmacro MUI_HEADER_TEXT "$(reasonixUpdateTitle)" "$(reasonixUpdateSubtitle)"
397 SetDetailsView hide
398 SetAutoClose true
399 BringToFront
400
401 reasonix_update_progress_done:
402 FunctionEnd
403
404 Function reasonix.skipFinishPageForUpdate
405 StrCmp $ReasonixUpdateMode "1" 0 reasonix_show_finish_page
406 Abort
407
408 reasonix_show_finish_page:
409 FunctionEnd
410
411 # Check every stable entry point before extracting a replacement. A running
412 # shell may have already exited its Go service while still holding one of
413 # these files open; treating that as an installable state recreates the
414 # "installed but does not open" failure. Silent installs fail closed.
415 Function reasonix.waitForExecutableUnlock
416 StrCpy $3 40
417 reasonix_unlock_check:
418 StrCpy $2 0
419 IfFileExists "$INSTDIR\${PRODUCT_EXECUTABLE}" 0 reasonix_unlock_versioned
420 ClearErrors
421 FileOpen $1 "$INSTDIR\${PRODUCT_EXECUTABLE}" a
422 IfErrors reasonix_unlock_stable_locked
423 FileClose $1
424 Goto reasonix_unlock_versioned
425 reasonix_unlock_stable_locked:
426 StrCpy $2 1
427 reasonix_unlock_versioned:
428 IfFileExists "$INSTDIR\versions\${REASONIX_VERSION_TAG}\${PRODUCT_EXECUTABLE}" 0 reasonix_unlock_guard
429 ClearErrors
430 FileOpen $1 "$INSTDIR\versions\${REASONIX_VERSION_TAG}\${PRODUCT_EXECUTABLE}" a
431 IfErrors reasonix_unlock_versioned_locked
432 FileClose $1
433 Goto reasonix_unlock_guard
434 reasonix_unlock_versioned_locked:
435 StrCpy $2 1
436 reasonix_unlock_guard:
437 IfFileExists "$INSTDIR\${REASONIX_GUARD}" 0 reasonix_unlock_launcher
438 ClearErrors
439 FileOpen $1 "$INSTDIR\${REASONIX_GUARD}" a
440 IfErrors reasonix_unlock_guard_locked
441 FileClose $1
442 Goto reasonix_unlock_launcher
443 reasonix_unlock_guard_locked:
444 StrCpy $2 1
445 reasonix_unlock_launcher:
446 IfFileExists "$INSTDIR\${REASONIX_LAUNCHER}" 0 reasonix_unlock_cli
447 ClearErrors
448 FileOpen $1 "$INSTDIR\${REASONIX_LAUNCHER}" a
449 IfErrors reasonix_unlock_launcher_locked
450 FileClose $1
451 Goto reasonix_unlock_cli
452 reasonix_unlock_launcher_locked:
453 StrCpy $2 1
454 reasonix_unlock_cli:
455 IfFileExists "$INSTDIR\${REASONIX_CLI}" 0 reasonix_unlock_portable
456 ClearErrors
457 FileOpen $1 "$INSTDIR\${REASONIX_CLI}" a
458 IfErrors reasonix_unlock_cli_locked
459 FileClose $1
460 Goto reasonix_unlock_portable
461 reasonix_unlock_cli_locked:
462 StrCpy $2 1
463 reasonix_unlock_portable:
464 IfFileExists "$INSTDIR\${REASONIX_PORTABLE_ENTRY}" 0 reasonix_unlock_result
465 ClearErrors
466 FileOpen $1 "$INSTDIR\${REASONIX_PORTABLE_ENTRY}" a
467 IfErrors reasonix_unlock_portable_locked
468 FileClose $1
469 Goto reasonix_unlock_result
470 reasonix_unlock_portable_locked:
471 StrCpy $2 1
472 reasonix_unlock_result:
473 StrCmp $2 0 reasonix_unlock_ok
474 IntOp $3 $3 - 1
475 IntCmp $3 0 reasonix_unlock_failed reasonix_unlock_retry reasonix_unlock_retry
476 reasonix_unlock_retry:
477 Sleep 500
478 Goto reasonix_unlock_check
479 reasonix_unlock_failed:
480 SetErrorLevel 1618
481 IfSilent reasonix_unlock_abort reasonix_unlock_prompt
482 reasonix_unlock_prompt:
483 MessageBox MB_ICONEXCLAMATION|MB_RETRYCANCEL "Reasonix is still running. Close it and click Retry, or cancel this installation." IDRETRY reasonix_unlock_check
484 reasonix_unlock_abort:
485 Abort
486 reasonix_unlock_ok:
487 FunctionEnd
488
489
490 !ifdef ARG_REASONIX_UNINSTALLER_ONLY
491 Section
492 WriteUninstaller "$INSTDIR\uninstall.exe"
493 SectionEnd
494 !else
495 Section
496 !insertmacro reasonix.setShellContext
497
498 ; /REASONIXSTAGE=1: flat executables plus the Electron app/ tree for
499 ; 1.18–1.19.1 helpers (and the new helper's staging extract). Do not write
500 ; shortcuts/uninstaller.
501 ; Normal install: versioned-v1 layout under versions/${REASONIX_VERSION_TAG}/
502 ; with a permanent thin launcher at InstallRoot. Guard is only present in
503 ; STAGE payloads (as the one-shot legacy migrator) and is not persisted on
504 ; a normal install.
505 StrCmp $ReasonixStageMode "1" reasonix_stage_payload
506 ; The signed activator coordinates all installed versions before committing.
507 Call reasonix.waitForExecutableUnlock
508 Goto reasonix_normal_install
509
510 reasonix_stage_payload:
511 SetOutPath $INSTDIR
512 !if /FileExists "${REASONIX_PAYLOAD_MANIFEST}"
513 File "/oname=${REASONIX_PAYLOAD_MANIFEST}" "${REASONIX_PAYLOAD_MANIFEST}"
514 !endif
515 !if /FileExists "${REASONIX_PAYLOAD_SIGNATURE}"
516 File "/oname=${REASONIX_PAYLOAD_SIGNATURE}" "${REASONIX_PAYLOAD_SIGNATURE}"
517 !endif
518 !insertmacro reasonix.files
519 !if /FileExists "${REASONIX_UPDATE_HELPER}"
520 File "/oname=${REASONIX_UPDATE_HELPER}" "${REASONIX_UPDATE_HELPER}"
521 !endif
522 !if /FileExists "${REASONIX_GUARD}"
523 File "/oname=${REASONIX_GUARD}" "${REASONIX_GUARD}"
524 !endif
525 !if /FileExists "${REASONIX_LAUNCHER}"
526 File "/oname=${REASONIX_LAUNCHER}" "${REASONIX_LAUNCHER}"
527 !endif
528 !if /FileExists "${REASONIX_CLI}"
529 File "/oname=${REASONIX_CLI}" "${REASONIX_CLI}"
530 !endif
531 Goto reasonix_section_done
532
533 reasonix_normal_install:
534 ; Extract into an install-local temporary directory, then let the signed Go
535 ; activator validate the complete release unit, transactionally publish the
536 ; version/root entries, and strictly atomically replace current.json last.
537 ; The normal/recovery installer therefore shares the same commit protocol as
538 ; automatic updates instead of writing live files or current.json in place.
539 System::Call 'kernel32::GetCurrentProcessId() i .R8'
540 CreateDirectory "$INSTDIR\versions"
541 StrCpy $R9 "$INSTDIR\versions\.installer-${REASONIX_VERSION_TAG}-$R8"
542 RMDir /r "$R9"
543 CreateDirectory "$R9"
544 SetOutPath "$R9"
545 !insertmacro reasonix.files
546 !if /FileExists "${REASONIX_UPDATE_HELPER}"
547 File "/oname=${REASONIX_UPDATE_HELPER}" "${REASONIX_UPDATE_HELPER}"
548 !else
549 !warning "${REASONIX_UPDATE_HELPER} was not found; Windows auto-update will fail safely until the helper is installed."
550 !endif
551 !if /FileExists "${REASONIX_CLI}"
552 File "/oname=${REASONIX_CLI}" "${REASONIX_CLI}"
553 !else
554 !warning "${REASONIX_CLI} was not found; remote upload installation will be unavailable."
555 !endif
556 !if /FileExists "${REASONIX_LAUNCHER}"
557 File "/oname=${REASONIX_LAUNCHER}" "${REASONIX_LAUNCHER}"
558 !endif
559
560 SetOutPath "$PLUGINSDIR"
561 !if /FileExists "${REASONIX_GUARD}"
562 File "/oname=${REASONIX_LAYOUT_INSTALLER}" "${REASONIX_GUARD}"
563 !else
564 !error "${REASONIX_GUARD} was not found; normal installs require the signed layout activator."
565 !endif
566 DetailPrint "Reasonix layout activator output:"
567 StrCpy $R7 ""
568 IfSilent +2 0
569 StrCpy $R7 "--interactive-recovery"
570 reasonix_layout_activate:
571 nsExec::ExecToLog /OEM '"$PLUGINSDIR\${REASONIX_LAYOUT_INSTALLER}" --install-root "$INSTDIR" --version "${REASONIX_VERSION_TAG}" --activate-staging "$R9" --no-relaunch $R7'
572 Pop $0
573 StrCmp $0 "0" reasonix_layout_activated
574 DetailPrint "Reasonix layout activation failed with exit code $0; the previous version remains active."
575 ; 1602 is the user's own cancel in the recovery dialog. Every other failure
576 ; keeps $R9 so Retry re-runs the activator against the same verified files;
577 ; the exit code is set only once the attempt is truly abandoned.
578 StrCmp $0 "1602" reasonix_activation_cancelled
579 IfSilent reasonix_activation_failed 0
580 StrCmp $0 "1618" reasonix_activation_busy_prompt reasonix_activation_locked_prompt
581 reasonix_activation_busy_prompt:
582 MessageBox MB_ICONEXCLAMATION|MB_RETRYCANCEL "$(reasonixActivateBusy)" IDRETRY reasonix_layout_activate
583 Goto reasonix_activation_failed
584 reasonix_activation_locked_prompt:
585 MessageBox MB_ICONEXCLAMATION|MB_RETRYCANCEL "$(reasonixActivateLocked)" IDRETRY reasonix_layout_activate
586 reasonix_activation_failed:
587 RMDir /r "$R9"
588 StrCmp $0 "1618" 0 +3
589 SetErrorLevel 1618
590 Goto reasonix_activation_abort
591 SetErrorLevel 1
592 Goto reasonix_activation_abort
593 reasonix_activation_cancelled:
594 RMDir /r "$R9"
595 SetErrorLevel 1602
596 reasonix_activation_abort:
597 Abort "Reasonix could not activate the verified release. The previous version was left unchanged."
598
599 reasonix_layout_activated:
600 RMDir /r "$R9"
601 SetOutPath "$INSTDIR"
602
603 ; Remove flat leftovers from prior 1.18–1.19 installs when overwriting.
604 Delete "$INSTDIR\${PRODUCT_EXECUTABLE}"
605 Delete "$INSTDIR\${REASONIX_GUARD}"
606 Delete "$INSTDIR\${REASONIX_UPDATE_HELPER}"
607
608 !if /FileExists "${REASONIX_LAUNCHER}"
609 ; Keep both target and icon on the stable launcher. Pointing IconLocation at
610 ; versions\vX\reasonix-desktop.exe leaves a blank shortcut as soon as version
611 ; retention removes that directory after a later update.
612 ; Preserve user arguments, icons and working directories on existing links;
613 ; the owned-link repair below migrates their targets without replacing them.
614 IfFileExists "$SMPROGRAMS\${INFO_PRODUCTNAME}.lnk" +2 0
615 CreateShortcut "$SMPROGRAMS\${INFO_PRODUCTNAME}.lnk" "$INSTDIR\${REASONIX_PORTABLE_ENTRY}" "" "$INSTDIR\${REASONIX_PORTABLE_ENTRY}" 0
616 IfFileExists "$DESKTOP\${INFO_PRODUCTNAME}.lnk" +2 0
617 CreateShortCut "$DESKTOP\${INFO_PRODUCTNAME}.lnk" "$INSTDIR\${REASONIX_PORTABLE_ENTRY}" "" "$INSTDIR\${REASONIX_PORTABLE_ENTRY}" 0
618 ; Stamp the exact paths created in this shell context before the user can pin them.
619 nsExec::ExecToLog /OEM '"$INSTDIR\${REASONIX_PORTABLE_ENTRY}" --repair-shortcuts "$SMPROGRAMS\${INFO_PRODUCTNAME}.lnk" "$DESKTOP\${INFO_PRODUCTNAME}.lnk"'
620 Pop $0
621 ${If} $0 != "0"
622 DetailPrint "Warning: shortcut identity repair failed ($0); the next normal launch will retry."
623 ${EndIf}
624 !else
625 CreateShortcut "$SMPROGRAMS\${INFO_PRODUCTNAME}.lnk" "$INSTDIR\versions\${REASONIX_VERSION_TAG}\${PRODUCT_EXECUTABLE}"
626 CreateShortCut "$DESKTOP\${INFO_PRODUCTNAME}.lnk" "$INSTDIR\versions\${REASONIX_VERSION_TAG}\${PRODUCT_EXECUTABLE}"
627 !endif
628
629 !insertmacro reasonix.associateFiles
630 !insertmacro reasonix.associateCustomProtocols
631 !insertmacro reasonix.writeUninstaller
632 !insertmacro reasonix.deleteLegacyInstallerStateIfOwned
633
634 reasonix_section_done:
635 SectionEnd
636 !endif
637
638 Section "uninstall"
639 !insertmacro reasonix.setShellContext
640
641 RMDir /r "$AppData\${PRODUCT_EXECUTABLE}" # Remove the legacy webview data directory
642
643 ; Precision uninstall: flat leftovers, thin entry points, and version trees.
644 Delete "$INSTDIR\${PRODUCT_EXECUTABLE}"
645 Delete "$INSTDIR\${REASONIX_UPDATE_HELPER}"
646 Delete "$INSTDIR\${REASONIX_GUARD}"
647 Delete "$INSTDIR\${REASONIX_LAUNCHER}"
648 Delete "$INSTDIR\${REASONIX_CLI}"
649 Delete "$INSTDIR\${REASONIX_PORTABLE_ENTRY}"
650 Delete "$INSTDIR\current.json"
651 RMDir /r "$INSTDIR\versions"
652
653 Delete "$SMPROGRAMS\${INFO_PRODUCTNAME}.lnk"
654 Delete "$DESKTOP\${INFO_PRODUCTNAME}.lnk"
655
656 !insertmacro reasonix.unassociateFiles
657 !insertmacro reasonix.unassociateCustomProtocols
658
659 !insertmacro reasonix.deleteUninstaller
660 !insertmacro reasonix.deleteLegacyInstallerStateIfOwned
661
662 ; Only remove the installation directory if it is empty to prevent data loss
663 RMDir $INSTDIR
664 SectionEnd
665
665 lines Plain Text