packaging/windows: add NSIS installer .exe alongside the portable .zip - #740
Conversation
Refs amule-project#738. The existing portable .zip works well for technical users but leaves the non-technical audience with no desktop / Start Menu entry and no obvious way to keep aMule running across reboots, which the upstream ticket flags as a measurable contributor to network health (clients that aren't running can't seed). This adds a Windows installer .exe that wraps the same portable tree without re-implementing the build. `packaging/windows/installer.nsi` — NSIS 3.x / MUI2 script. * Pages: Welcome, License (project COPYING), Components, Directory, Start Menu Folder, Install, Finish. * Components: aMule core (required, SectionIn RO), desktop shortcut (checked by default), "Start aMule when I log in" (unchecked by default — opt-in writes HKCU\…\Run\aMule, per-user so each account on a shared machine decides independently). * Start Menu folder with shortcuts for aMule (GUI), aMule Daemon, aMuleGUI (remote), and the uninstaller. * Full Add/Remove Programs entry under HKLM (DisplayName, DisplayVersion, Publisher, URLInfoAbout, DisplayIcon, InstallLocation, UninstallString, QuietUninstallString, EstimatedSize via ${GetSize}, NoModify=1, NoRepair=1). * Pre-install / pre-uninstall guard refuses to proceed while amule.exe / amuled.exe in the install dir is open (sharing- violation probe rather than process enumeration so it works regardless of which user owns the running process). * Uninstaller removes the Run-key value only when it still points inside the install dir so a user's hand-set value isn't wiped. * Per-machine install (RequestExecutionLevel admin), $PROGRAMFILES64 default, ARM64 vs x64 selected by !define ARCH so the same script drives both shippable architectures via a shared 32-bit installer stub (NSIS arm64-unicode stub isn't shipped by Chocolatey or Homebrew as of 3.10, and an x86 installer running under WOW64 emulation for ~30 seconds during install is a non-issue). `packaging/windows/build.sh` — new `installer` subcommand that verifies the portable tree from a prior plain `build.sh` invocation exists, copies docs/COPYING into the tree for the MUI2 license page, and runs makensis with the four required defines. Auto-discovers `makensis` at the standard install dir if it isn't on $PATH (MSYS2's default path-type=minimal drops Windows PATH entries, so the bash session won't find a choco-installed makensis without help). Auto- signs the produced .exe when both WIN_CERT_PFX_BASE64 and WIN_CERT_PASSWORD are populated. Default no-argument invocation still builds only the portable .zip. `packaging/windows/sign.sh` — extended to dispatch on extension so `.exe` artifacts (the installer) are signed in place while the existing `.zip` path stays unchanged (extract → sign every .exe + .dll → re-zip). `build.sh sign` now also signs the installer .exe when present in `dist/`. `.github/workflows/packaging.yml` — new `windows-installer` job that depends on `windows-zip` and consumes its portable .zip artifact as the installer payload, rather than rebuilding the portable tree a second time. The coupling is correct: with no payload there's nothing to wrap, so a `windows-zip` failure should skip the installer. NSIS isn't packaged for the clangarm64 MSYS2 environment, so the job installs Windows-native NSIS via Chocolatey (no-op when the runner already ships it); the toolchain runs fine under x64 emulation on Windows-on-ARM since it's a build tool, not part of the runtime payload. Smoke test silent-installs to C:\amule-installtest, verifies amule.exe and Uninstall.exe landed, then silent-uninstalls and verifies the dir is gone. `packaging/windows/README.md` — new "Installer (.exe)" section covering the invocation, output path, what the installer does, admin / per-machine semantics, coexistence with the portable .zip, and the shared signing flow.
|
cpack has nsis support, why not using that? |
|
Both fair questions — quick unpacking: CPack NSIS: Considered it. The catch is the feature surface #738 actually asks for — Components page with an opt-in "Start aMule when I log in" checkbox, sharing-violation probe that refuses to install while The other thing CPack would (theoretically) buy us is "install rules already in CMake → no duplication." The existing portable build already runs Net: for this feature set, a direct On "shipping MSys": worth a precision — we don't actually ship any MSYS subsystem files (no Native Windows build: Different concern, and a much bigger one. Switching to MSVC + vcpkg would replace those 2 runtime DLLs with For this PR specifically, the installer wraps the output of whatever Windows build the project ships. If upstream moves to MSVC later, the same |
Don't say vcpkg, that crap is broken by design and shouldn't be used by anyone using more than 3 braincells. |
…r-data removal Two follow-ups from PR review: * Add CheckPriorInstall — silently runs the old install's Uninstall.exe before staging the new portable tree, so removed files / orphaned DLLs from the older version don't accumulate. Reads prior location from HKLM\Software\aMule\InstallLocation (recorded during the original install). Silent mode respects section defaults: un.SecRemoveUserData is /o (unchecked), so an upgrade preserves %APPDATA%\aMule. * Add un.SecRemoveUserData — opt-in (Section /o, unchecked by default) uninstaller section that nukes %APPDATA%\aMule on explicit request. Default-off matches the common Windows convention of "remove the app, keep my data." MUI_UNPAGE_COMPONENTS shows the checkbox alongside the (RO) main uninstall section. Refs amule-project#740 (comment).
|
Pushed Upgrade flow — new Important consequence of the silent uninstall: NSIS Opt-in user-data removal — added Autostart toggle in Preferences — agreed it's the right UX, but it needs Preferences-dialog work + a 3-OS backend abstraction (Windows registry, macOS New test-plan items still pending (CI for
|
…omponents page
NSIS dispatches install-vs-uninstall sections based on the `un.`
prefix on the DISPLAY NAME (first arg to Section), not on the
section ID. The `Section /o "Remove user data..." un.SecRemoveUserData`
declaration put the prefix on the ID and left the name bare, so NSIS
treated it as an installer section — the checkbox appeared on the
install-time Components page where it had no business being.
Worse, if a user had ticked it during install, the section body
(SetShellVarContext current + RMDir /r "$APPDATA\aMule") would have
wiped user data while installing.
Fix: move the `un.` prefix onto the display name. NSIS strips it
from the rendered label, so the user still sees "Remove user data..."
in the uninstaller's Components page, but the section is correctly
classified as uninstaller-only.
The MUI_DESCRIPTION_TEXT ${un.SecRemoveUserData} reference and the
section's ID stay the same — only the first arg to Section moves.
|
Re-verified end-to-end on Windows 11 ARM64:
All test-plan items are green from my side — ready for merge. |
…r-data removal Two follow-ups from PR review: * Add CheckPriorInstall — silently runs the old install's Uninstall.exe before staging the new portable tree, so removed files / orphaned DLLs from the older version don't accumulate. Reads prior location from HKLM\Software\aMule\InstallLocation (recorded during the original install). Silent mode respects section defaults: un.SecRemoveUserData is /o (unchecked), so an upgrade preserves %APPDATA%\aMule. * Add un.SecRemoveUserData — opt-in (Section /o, unchecked by default) uninstaller section that nukes %APPDATA%\aMule on explicit request. Default-off matches the common Windows convention of "remove the app, keep my data." MUI_UNPAGE_COMPONENTS shows the checkbox alongside the (RO) main uninstall section. Refs #740 (comment).
Adds GenericName[ru] and Comment[ru] to org.amule.aMule.desktop and org.amule.aMule.gui.desktop.
Summary
Closes #738. The portable .zip works well for technical users but leaves the rest with no desktop / Start Menu entry and no obvious way to keep aMule running across reboots, which the upstream ticket flags as a measurable contributor to network health (clients that aren't running can't seed). This PR adds a Windows installer .exe that wraps the existing portable tree as its payload — no second build, no second dependency resolution.
What's in the installer
NSIS 3.x with MUI2. Pages: Welcome, License (project
COPYING), Components, Directory, Start Menu, Install, Finish. Three components surfaced on the Components page: aMule core (required,SectionIn RO), desktop shortcut (checked by default), "Start aMule when I log in" (unchecked by default — opt-in writesHKCU\Software\Microsoft\Windows\CurrentVersion\Run\aMule). Start Menu folder gets shortcuts for aMule (GUI), aMule Daemon, aMuleGUI (remote), and the uninstaller. Per-machine install (RequestExecutionLevel admin,SetShellVarContext allso shortcuts land under All Users, not the elevated admin's per-user profile).$PROGRAMFILES64\aMuledefault — overridable on the Directory page.Full Add/Remove Programs entry under
HKLM\Software\Microsoft\Windows\CurrentVersion\Uninstall\aMulewith DisplayName, DisplayVersion, Publisher, URLInfoAbout, DisplayIcon, InstallLocation, UninstallString, QuietUninstallString, EstimatedSize (computed via${GetSize}), NoModify=1, NoRepair=1.A sharing-violation probe refuses to install or uninstall while
amule.exe/amuled.exein the target dir is open — works regardless of which user owns the running process. The uninstaller removes theRunkey value only when it still points inside the install dir, so a user's hand-set autostart value isn't clobbered. The 32-bit installer stub is used for BOTH x64 and ARM64 payloads — it runs natively on x64 and under WOW64 emulation on Windows-on-ARM. A nativearm64-unicodestub would require an NSIS distribution that ships one (Chocolatey and Homebrew don't as of NSIS 3.10), so for a reproducible CI matrix we use the universally-available 32-bit stub; the installer is short-lived bootstrap code, so the emulation cost is irrelevant.Recipe + CI integration
packaging/windows/build.shgets a newinstallersubcommand on top of the existing default action. It consumes the portable tree from a prior plainbuild.shinvocation (no duplicated build logic) and runsmakensiswith the required defines. Auto-discoversmakensisat the standard install dir (C:\Program Files (x86)\NSIS) if it isn't on$PATH, since MSYS2'spath-type: minimaldrops the Windows PATH entries that Chocolatey writes — self-contained for CI and dev machines alike. Auto-signs the produced.exewhenWIN_CERT_PFX_BASE64+WIN_CERT_PASSWORDare set; the existingsignsubcommand also picks up the installer when present indist/. Default no-argument invocation is unchanged — installer is opt-in.sign.shnow dispatches on file extension:.zipkeeps the existing extract → sign → re-zip dance;.exe(installer) gets signed in place..github/workflows/packaging.ymlgets awindows-installerjob thatneeds: windows-zipand downloads the portable .zip artifact as the installer payload, rather than rebuilding the portable tree a second time. Coupling is correct: with no payload there's nothing to wrap, so awindows-zipfailure should skip the installer. NSIS isn't packaged for the clangarm64 MSYS2 environment, so the job installs Windows-native NSIS via Chocolatey (no-op when the runner already ships it). Smoke test silent-installs toC:\amule-installtest, assertsbin\amule.exe+Uninstall.exeare present, then silent-uninstalls and asserts the dir is gone.Coexistence with the portable .zip
Different artifact name (
aMule-<ver>-Setup-<arch>.exevsaMule-<ver>-Windows-<arch>.zip), different default install path. The two are independent shipping channels — neither replaces the other.Test plan
C:\Program Files\aMule\bin\{amule,amuled,amulegui}.exe)HKLM Software\aMuleSetShellVarContext allfix