mirror of
https://github.com/storytold/photocraft.git
synced 2026-10-14 19:26:51 -04:00
Packaging: Windows MSI setup wizard (folder choice, progress, version, launch on finish)
Double-clicking the MSI now opens WixUI_InstallDir without the licence page: Welcome, install folder (remembered for upgrades in HKLM\Software\PhotoCraft\InstallDir), Ready, progress, and a Finish page with a ticked "Launch PhotoCraft" box (WixShellExec, unelevated). The pages name the full version, pre-release tag included (photocraft.en-us.wxl). package.ps1 draws the banner and side bitmaps from the app icon, so no WiX stock art ships. msiexec /qn still installs silently. The release and ARM64 workflows install the WiX UI and Util extensions; packaging-lint checks the .wxl. Ported from VectorCraft (storytold/vectorcraft@aa548d2). Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01AP7ThRN3kpNmjsuJUerJui
This commit is contained in:
7 files changed
+113
-11
No files matched your search
@@ -49,7 +49,7 @@ jobs:
|
||||
./packaging/windows/check-icons.ps1
|
||||
- name: XML, desktop entry, AppStream, Flatpak manifest
|
||||
run: |
|
||||
xmllint --noout packaging/windows/photocraft.wxs packaging/macos/*.plist* packaging/linux/*.xml*
|
||||
xmllint --noout packaging/windows/photocraft.wxs packaging/windows/photocraft.en-us.wxl packaging/macos/*.plist* packaging/linux/*.xml*
|
||||
desktop-file-validate packaging/linux/ai.storyteller.photocraft.desktop
|
||||
sed -e 's/@VERSION@/0.0.0/' -e 's/@DATE@/2026-01-01/' packaging/linux/ai.storyteller.photocraft.metainfo.xml.in >"$RUNNER_TEMP/ai.storyteller.photocraft.metainfo.xml"
|
||||
appstreamcli validate --no-net --explain "$RUNNER_TEMP/ai.storyteller.photocraft.metainfo.xml"
|
||||
|
||||
@@ -159,7 +159,9 @@ jobs:
|
||||
run: cargo xtask version set "$PHOTOCRAFT_VERSION"
|
||||
- name: Install WiX
|
||||
shell: pwsh
|
||||
run: dotnet tool install --global wix --version 5.0.2
|
||||
run: |
|
||||
dotnet tool install --global wix --version 5.0.2
|
||||
wix extension add -g WixToolset.UI.wixext/5.0.2 WixToolset.Util.wixext/5.0.2
|
||||
- name: Build, sign, package
|
||||
shell: pwsh
|
||||
env:
|
||||
|
||||
@@ -41,7 +41,9 @@ jobs:
|
||||
key: windows-arm64-package
|
||||
- name: Install WiX
|
||||
shell: pwsh
|
||||
run: dotnet tool install --global wix --version 5.0.2
|
||||
run: |
|
||||
dotnet tool install --global wix --version 5.0.2
|
||||
wix extension add -g WixToolset.UI.wixext/5.0.2 WixToolset.Util.wixext/5.0.2
|
||||
- name: Build and package (unsigned)
|
||||
shell: pwsh
|
||||
env:
|
||||
|
||||
+9
-1
@@ -140,6 +140,13 @@ goes in `CARGO_TARGET_<TRIPLE>_RUSTFLAGS`, so host build scripts aren't affected
|
||||
"Open with" for PSD/PSB and image files. It also registers App Paths (Win+R `photocraft`).
|
||||
The MSI version is the numeric `X.Y.Z`, because MSI has no pre-release field. Same-version
|
||||
upgrades are allowed so that release candidates replace each other.
|
||||
- Double-clicking the MSI opens a setup wizard (WixUI_InstallDir without the licence page):
|
||||
Welcome, install folder (remembered for upgrades in `HKLM\Software\PhotoCraft\InstallDir`),
|
||||
Ready, a progress page, and a Finish page that says where to find the app, with a ticked "Launch
|
||||
PhotoCraft" box. The pages name the full version (`photocraft.en-us.wxl`). `package.ps1` draws
|
||||
the wizard's banner and side bitmaps from the app icon, so no WiX stock art ships. `msiexec /qn`
|
||||
still installs silently. Building needs the WiX UI and Util extensions:
|
||||
`wix extension add -g WixToolset.UI.wixext/5.0.2 WixToolset.Util.wixext/5.0.2`.
|
||||
- Shortcut icon identifiers keep the executable's `.exe` extension: MSI uses the identifier
|
||||
as the cached icon filename, and an extensionless filename can render as a blank document
|
||||
icon. `packaging/windows/check-icons.ps1` checks the references and extensions in CI;
|
||||
@@ -159,7 +166,8 @@ goes in `CARGO_TARGET_<TRIPLE>_RUSTFLAGS`, so host build scripts aren't affected
|
||||
|
||||
The script is the single place to change when the Windows signing setup changes.
|
||||
|
||||
Locally on Windows: `dotnet tool install -g wix --version 5.0.2`, then
|
||||
Locally on Windows: `dotnet tool install -g wix --version 5.0.2`,
|
||||
`wix extension add -g WixToolset.UI.wixext/5.0.2 WixToolset.Util.wixext/5.0.2`, then
|
||||
`pwsh packaging/windows/package.ps1 -Arch x64`.
|
||||
|
||||
### Linux
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
Needs: Rust (MSVC toolchain + the target), the Windows SDK (rc.exe, signtool.exe),
|
||||
and WiX v5: dotnet tool install --global wix --version 5.0.2
|
||||
wix extension add -g WixToolset.UI.wixext/5.0.2 WixToolset.Util.wixext/5.0.2
|
||||
|
||||
.EXAMPLE
|
||||
pwsh packaging/windows/package.ps1 -Arch x64
|
||||
@@ -91,11 +92,51 @@ Copy-Item (Join-Path $Bin 'photocraft.exe'), (Join-Path $Bin 'photocraft-cli.exe
|
||||
& (Join-Path $PSScriptRoot 'sign.ps1') (Join-Path $Stage 'photocraft.exe') (Join-Path $Stage 'photocraft-cli.exe')
|
||||
|
||||
# ---- MSI ---------------------------------------------------------------------------------------
|
||||
# The setup wizard's art, drawn here from the app icon so the MSI shows PhotoCraft rather than
|
||||
# WiX's stock bitmaps: a 493x58 banner across the top of the inner pages (the page title is drawn
|
||||
# over its left side, so that stays white) and a 493x312 backdrop for the Welcome and Finish pages
|
||||
# (their text sits on the right; the left 164 px are ours). Both must be 24-bit BMPs.
|
||||
function New-InstallerArt([string] $IconPng, [string] $BannerOut, [string] $DialogOut) {
|
||||
Add-Type -AssemblyName System.Drawing
|
||||
$paper = [System.Drawing.ColorTranslator]::FromHtml('#efe9dc')
|
||||
$accent = [System.Drawing.ColorTranslator]::FromHtml('#2f7bf5')
|
||||
$icon = [System.Drawing.Image]::FromFile($IconPng)
|
||||
try {
|
||||
foreach ($spec in @(@($BannerOut, 493, 58), @($DialogOut, 493, 312))) {
|
||||
$bmp = New-Object System.Drawing.Bitmap $spec[1], $spec[2], ([System.Drawing.Imaging.PixelFormat]::Format24bppRgb)
|
||||
$g = [System.Drawing.Graphics]::FromImage($bmp)
|
||||
try {
|
||||
$g.InterpolationMode = [System.Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic
|
||||
$g.SmoothingMode = [System.Drawing.Drawing2D.SmoothingMode]::AntiAlias
|
||||
$g.Clear([System.Drawing.Color]::White)
|
||||
if ($spec[2] -eq 58) {
|
||||
$g.DrawImage($icon, 493 - 46 - 8, 6, 46, 46)
|
||||
} else {
|
||||
$panel = New-Object System.Drawing.SolidBrush $paper
|
||||
$stripe = New-Object System.Drawing.SolidBrush $accent
|
||||
$g.FillRectangle($panel, 0, 0, 164, 312)
|
||||
$g.FillRectangle($stripe, 160, 0, 4, 312)
|
||||
$g.DrawImage($icon, 18, 64, 124, 124)
|
||||
$panel.Dispose(); $stripe.Dispose()
|
||||
}
|
||||
} finally { $g.Dispose() }
|
||||
$bmp.Save($spec[0], [System.Drawing.Imaging.ImageFormat]::Bmp)
|
||||
$bmp.Dispose()
|
||||
}
|
||||
} finally { $icon.Dispose() }
|
||||
}
|
||||
$Banner = Join-Path $Stage 'installer-banner.bmp'
|
||||
$Dialog = Join-Path $Stage 'installer-dialog.bmp'
|
||||
New-InstallerArt (Join-Path $Root 'assets\app-icon\hicolor\256x256\apps\ai.storyteller.photocraft.png') $Banner $Dialog
|
||||
|
||||
$Msi = Join-Path $Dist "photocraft-$Version-windows-$Arch.msi"
|
||||
& (Join-Path $PSScriptRoot 'check-icons.ps1')
|
||||
Invoke-Native 'wix build' {
|
||||
wix build (Join-Path $PSScriptRoot 'photocraft.wxs') -arch $Arch `
|
||||
-d "Version=$MsiVersion" -d "BinDir=$Stage" -d "IconPath=$(Join-Path $Root 'assets\app-icon\photocraft.ico')" `
|
||||
-ext WixToolset.UI.wixext -ext WixToolset.Util.wixext `
|
||||
-culture en-US -loc (Join-Path $PSScriptRoot 'photocraft.en-us.wxl') `
|
||||
-d "Version=$MsiVersion" -d "DisplayVersion=$Version" -d "BinDir=$Stage" -d "IconPath=$(Join-Path $Root 'assets\app-icon\photocraft.ico')" `
|
||||
-d "BannerBmp=$Banner" -d "DialogBmp=$Dialog" `
|
||||
-o $Msi
|
||||
}
|
||||
Invoke-Native 'MSI shortcut icon validation (ICE50)' {
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Overrides for the setup wizard's WixUI strings, so its pages name the version being installed
|
||||
([PhotocraftVersion] is the full version, pre-release tag included, set in photocraft.wxs).
|
||||
-->
|
||||
<WixLocalization Culture="en-US" xmlns="http://wixtoolset.org/schemas/v4/wxl">
|
||||
<String Id="WelcomeDlgTitle" Value="{\WixUI_Font_Bigger}Welcome to the [ProductName] [PhotocraftVersion] Setup Wizard" />
|
||||
<String Id="WelcomeDlgDescription" Value="The Setup Wizard will install [ProductName] version [PhotocraftVersion] on your computer. Click Next to continue or Cancel to exit the Setup Wizard." />
|
||||
<String Id="VerifyReadyDlgInstallTitle" Value="{\WixUI_Font_Title}Ready to install [ProductName] [PhotocraftVersion]" />
|
||||
<String Id="ExitDialogTitle" Value="{\WixUI_Font_Bigger}Completed the [ProductName] [PhotocraftVersion] Setup Wizard" />
|
||||
</WixLocalization>
|
||||
@@ -1,14 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
PhotoCraft MSI (WiX Toolset v5: `dotnet tool install -g wix`). Built by package.ps1:
|
||||
PhotoCraft MSI (WiX Toolset v5: `dotnet tool install -g wix`, plus the UI and Util extensions:
|
||||
`wix extension add -g WixToolset.UI.wixext/5.0.2 WixToolset.Util.wixext/5.0.2`). Built by package.ps1:
|
||||
|
||||
wix build photocraft.wxs -arch x64 -d Version=1.2.3 -d BinDir=... -d IconPath=... -o out.msi
|
||||
wix build photocraft.wxs -arch x64 -ext WixToolset.UI.wixext -ext WixToolset.Util.wixext
|
||||
-culture en-US -loc photocraft.en-us.wxl -d Version=1.2.3 -d DisplayVersion=1.2.3-rc.1
|
||||
-d BinDir=... -d IconPath=... -d BannerBmp=... -d DialogBmp=... -o out.msi
|
||||
|
||||
Per-machine install into Program Files, a Start Menu shortcut, `.pcraft` owned by PhotoCraft,
|
||||
and PhotoCraft offered under "Open with" for PSD/PSB and the image formats it reads (without
|
||||
taking over the user's default app for them).
|
||||
Per-machine install with a setup wizard that names the version: Welcome, install folder
|
||||
(Program Files\PhotoCraft by default, remembered for upgrades), Ready, a progress page, and a
|
||||
Finish page with a ticked "Launch PhotoCraft" box. Installs a Start Menu shortcut, makes
|
||||
PhotoCraft the owner of `.pcraft`, and offers it under "Open with" for PSD/PSB and the image
|
||||
formats it reads (without taking over the user's default app for them), plus photocraft-cli.
|
||||
`msiexec /qn` still installs silently.
|
||||
-->
|
||||
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
|
||||
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"
|
||||
xmlns:ui="http://wixtoolset.org/schemas/v4/wxs/ui">
|
||||
<Package
|
||||
Name="PhotoCraft"
|
||||
Manufacturer="Learning Machines LLC"
|
||||
@@ -37,9 +44,39 @@
|
||||
</StandardDirectory>
|
||||
<StandardDirectory Id="ProgramMenuFolder" />
|
||||
|
||||
<!-- An upgrade installs where the user put the previous version (written by PhotocraftApp). -->
|
||||
<Property Id="INSTALLFOLDER">
|
||||
<RegistrySearch Id="PreviousInstallFolder" Root="HKLM" Key="Software\PhotoCraft" Name="InstallDir" Type="raw" />
|
||||
</Property>
|
||||
|
||||
<Feature Id="Main" Title="PhotoCraft" Level="1">
|
||||
<ComponentGroupRef Id="PhotocraftFiles" />
|
||||
</Feature>
|
||||
|
||||
<!-- The setup wizard: WixUI_InstallDir without its licence page (the code is MIT OR
|
||||
Apache-2.0; there is nothing to accept). A later NewDialog on the same button wins. -->
|
||||
<ui:WixUI Id="WixUI_InstallDir" InstallDirectory="INSTALLFOLDER" />
|
||||
<UI>
|
||||
<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="InstallDirDlg" Order="2" Condition="NOT Installed" />
|
||||
<Publish Dialog="InstallDirDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg" Order="2" />
|
||||
<Publish Dialog="ExitDialog" Control="Finish" Event="DoAction" Value="LaunchPhotocraft"
|
||||
Condition="WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 AND NOT Installed" />
|
||||
</UI>
|
||||
<!-- Our own art, generated by package.ps1 from the app icon (not WiX's stock bitmaps). -->
|
||||
<WixVariable Id="WixUIBannerBmp" Value="$(var.BannerBmp)" />
|
||||
<WixVariable Id="WixUIDialogBmp" Value="$(var.DialogBmp)" />
|
||||
|
||||
<!-- The full version (Version is numeric, so it drops a pre-release tag), shown on the wizard's
|
||||
pages through photocraft.en-us.wxl. -->
|
||||
<Property Id="PhotocraftVersion" Value="$(var.DisplayVersion)" />
|
||||
|
||||
<!-- Finish page: say where to find the app, and offer to start it (ticked by default). -->
|
||||
<Property Id="WIXUI_EXITDIALOGOPTIONALTEXT" Value="PhotoCraft $(var.DisplayVersion) is installed. You can start it any time from the Start menu." />
|
||||
<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT" Value="Launch PhotoCraft" />
|
||||
<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOX" Value="1" />
|
||||
<Property Id="WixShellExecTarget" Value="[#PhotocraftExe]" />
|
||||
<!-- Runs in the wizard's own (unelevated) process, so the app starts as the user, not as admin. -->
|
||||
<CustomAction Id="LaunchPhotocraft" BinaryRef="Wix4UtilCA_$(sys.BUILDARCHSHORT)" DllEntry="WixShellExec" Impersonate="yes" Return="ignore" />
|
||||
</Package>
|
||||
|
||||
<Fragment>
|
||||
@@ -100,6 +137,7 @@
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\Applications\photocraft.exe\SupportedTypes" Name=".qoi" Value="" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Classes\Applications\photocraft.exe\SupportedTypes" Name=".ico" Value="" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\Microsoft\Windows\CurrentVersion\App Paths\photocraft.exe" Value="[#PhotocraftExe]" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\PhotoCraft" Name="InstallDir" Value="[INSTALLFOLDER]" Type="string" />
|
||||
</Component>
|
||||
|
||||
<Component Id="PhotocraftCli">
|
||||
|
||||
Reference in new issue
Block a user