From 1c7fcccef2dff51bc7bd8c8f44f6e477aaa8612b Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 10 Oct 2026 22:18:06 +0000 Subject: [PATCH] Packaging: the Windows installer offers a desktop shortcut, on by default (#343) The MSI only installed a Start Menu shortcut. The wizard's folder page now has a "Create a desktop shortcut" box (DESKTOPSHORTCUT, 1 by default; DESKTOPSHORTCUT=0 for silent installs) that adds an EffectCraft shortcut to the Public desktop. The choice is recorded under HKLM\Software\EffectCraft so an upgrade keeps it, and an upgrade from a release before this option adds none. test-msi.ps1 checks the shortcut is created by default, kept by upgrades, removed on uninstall and absent with DESKTOPSHORTCUT=0. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01QiAJeFttZp83Fh7YuzAR5f --- packaging/windows/effectcraft.wxs | 23 ++++++++++++++++++++++- packaging/windows/installer-ui.wxs | 3 ++- packaging/windows/test-msi.ps1 | 18 +++++++++++++++--- 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/packaging/windows/effectcraft.wxs b/packaging/windows/effectcraft.wxs index 6757c446..00b0c5d6 100644 --- a/packaging/windows/effectcraft.wxs +++ b/packaging/windows/effectcraft.wxs @@ -4,7 +4,8 @@ wix build effectcraft.wxs installer-ui.wxs -arch x64 -d Version=1.2.3 -d BinDir=... -d IconPath=... -o out.msi - Per-machine install into Program Files (or a folder the user picks) and a Start Menu shortcut. + Per-machine install into Program Files (or a folder the user picks), a Start Menu shortcut and, + unless DESKTOPSHORTCUT=0 (the wizard's "Create a desktop shortcut" box), a desktop shortcut. --> + + + + + + + + @@ -70,11 +82,20 @@ + + + + + + + + diff --git a/packaging/windows/installer-ui.wxs b/packaging/windows/installer-ui.wxs index 14989932..16ab58d7 100644 --- a/packaging/windows/installer-ui.wxs +++ b/packaging/windows/installer-ui.wxs @@ -3,7 +3,7 @@ EffectCraft installer wizard: original dialogs built from Windows Installer's own controls and events (no WixUI library), included by effectcraft.wxs with . - fresh install: DestDlg (folder, Change... opens BrowseDlg) -> ReadyDlg -> ProgressDlg -> ExitDlg + fresh install: DestDlg (folder, Change... opens BrowseDlg, desktop shortcut) -> ReadyDlg -> ProgressDlg -> ExitDlg upgrade: ReadyDlg (the installed copy's folder, not editable) -> ProgressDlg -> ExitDlg already there: MaintDlg (Repair or Remove) -> ProgressDlg -> ExitDlg @@ -29,6 +29,7 @@ + diff --git a/packaging/windows/test-msi.ps1 b/packaging/windows/test-msi.ps1 index 7cdaef86..5868c77b 100644 --- a/packaging/windows/test-msi.ps1 +++ b/packaging/windows/test-msi.ps1 @@ -10,8 +10,10 @@ installed effectcraft.exe, as there is no Path value yet), 3. upgrades to 0.0.3 the same way (found from the Path value 0.0.2 wrote), 4. repairs a damaged (unversioned) CLI there with the wizard's Repair mode, - 5. uninstalls: files and the App Paths registration go, - 6. installs 0.0.3 with no folder: it must land in Program Files, then uninstalls. + 5. uninstalls: files, the App Paths registration and the desktop shortcut go, + 6. installs 0.0.3 with no folder and DESKTOPSHORTCUT=0: it must land in Program Files with no + desktop shortcut, then uninstalls. + The installs before step 6 take the default desktop shortcut, which upgrades keep. The wizard itself needs a person (or the snapshot steps in the PR); this covers what msiexec /qn and upgrades do with the folder. Needs an elevated shell and WiX v5 (`wix`) on PATH. Run by .github/workflows/packaging-lint.yml. @@ -24,6 +26,8 @@ $ErrorActionPreference = 'Stop' trap { Write-Output "FAILED: $_"; exit 1 } $Root = (Resolve-Path (Join-Path $PSScriptRoot '..\..')).Path $AppPaths = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\effectcraft.exe' +# A per-machine install's desktop is the Public one. +$DesktopLink = Join-Path ([Environment]::GetFolderPath('CommonDesktopDirectory')) 'EffectCraft.lnk' function Invoke-Native([string] $What, [scriptblock] $Block) { Write-Output "==> $What" @@ -50,6 +54,10 @@ function Assert-InstalledIn([string] $Dir, [switch] $NoPathValue) { } Write-Output "ok installed in $Dir" } +function Assert-DesktopShortcut([bool] $Present) { + if ((Test-Path -LiteralPath $DesktopLink) -ne $Present) { throw "desktop shortcut present: $(-not $Present), expected $Present" } + Write-Output "ok desktop shortcut present: $Present" +} Remove-Item -Recurse -Force $Work -ErrorAction SilentlyContinue $Bin = Join-Path $Work 'bin' @@ -87,6 +95,7 @@ if (Test-Path -LiteralPath $AppPaths) { throw 'EffectCraft is already installed Invoke-Msiexec 'install the legacy package to a custom folder' @('/i', "`"$($Msi['0.0.1'])`"", "INSTALLFOLDER=`"$Custom`"") Assert-InstalledIn $Custom -NoPathValue +Assert-DesktopShortcut $true if (Test-Path -LiteralPath $Default) { throw "a custom install also wrote $Default" } Invoke-Msiexec 'upgrade the legacy install without a folder' @('/i', "`"$($Msi['0.0.2'])`"") @@ -95,6 +104,7 @@ if (Test-Path -LiteralPath $Default) { throw "the upgrade moved the install to $ Invoke-Msiexec 'upgrade again without a folder' @('/i', "`"$($Msi['0.0.3'])`"") Assert-InstalledIn $Custom +Assert-DesktopShortcut $true # Read single values: Get-ItemProperty throws on some machines' malformed Uninstall entries. $arp = @(Get-ChildItem 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall' | ForEach-Object { if ($_.GetValue('DisplayName') -eq 'EffectCraft') { $_.GetValue('DisplayVersion') } }) @@ -117,9 +127,11 @@ Write-Output "ok repair ($mode) restored effectcraft-cli.exe" Invoke-Msiexec 'uninstall the custom install' @('/x', "`"$($Msi['0.0.3'])`"") if (Test-Path -LiteralPath (Join-Path $Custom 'effectcraft.exe')) { throw 'uninstall left effectcraft.exe behind' } if (Test-Path -LiteralPath $AppPaths) { throw 'uninstall left the App Paths registration behind' } +Assert-DesktopShortcut $false -Invoke-Msiexec 'install to the default folder' @('/i', "`"$($Msi['0.0.3'])`"") +Invoke-Msiexec 'install to the default folder without a desktop shortcut' @('/i', "`"$($Msi['0.0.3'])`"", 'DESKTOPSHORTCUT=0') Assert-InstalledIn $Default +Assert-DesktopShortcut $false Invoke-Msiexec 'uninstall the default install' @('/x', "`"$($Msi['0.0.3'])`"") if (Test-Path -LiteralPath (Join-Path $Default 'effectcraft.exe')) { throw 'uninstall left effectcraft.exe behind' } Write-Output 'MSI folder handling ok'