Merge PR #520
This commit is contained in:
commit
dea37269ba
|
|
@ -51,6 +51,11 @@ jobs:
|
|||
if grep -oE '<Icon Id="[^"]*"' packaging/windows/lightcraft.wxs | grep -vE '\.(ico|exe)"$'; then
|
||||
echo "::error::Icon Id must end in .ico or .exe"; exit 1
|
||||
fi
|
||||
# The interactive installer asks where to install and ends on a completion page (issues #18, #399).
|
||||
for want in '<UIRef Id="LightcraftUI" />' 'Value="InstallDirDlg"' '<Property Id="WIXUI_INSTALLDIR" Value="INSTALLFOLDER" />' \
|
||||
'Key="Software\LightCraft" Name="InstallFolder"' 'Value="LaunchLightcraft"'; do
|
||||
grep -qF "$want" packaging/windows/lightcraft.wxs || { echo "::error::lightcraft.wxs lost: $want"; exit 1; }
|
||||
done
|
||||
desktop-file-validate packaging/linux/ai.storyteller.lightcraft.desktop
|
||||
sed -e 's/@VERSION@/0.0.0/' -e 's/@DATE@/2026-01-01/' packaging/linux/ai.storyteller.lightcraft.metainfo.xml.in >"$RUNNER_TEMP/ai.storyteller.lightcraft.metainfo.xml"
|
||||
appstreamcli validate --no-net --explain "$RUNNER_TEMP/ai.storyteller.lightcraft.metainfo.xml"
|
||||
|
|
|
|||
|
|
@ -147,6 +147,7 @@ jobs:
|
|||
if: inputs.version != ''
|
||||
shell: bash
|
||||
run: cargo xtask version set "$LIGHTCRAFT_VERSION"
|
||||
# package.ps1 adds the matching WixToolset.UI/Util.wixext (5.0.2) to the global extension cache.
|
||||
- name: Install WiX
|
||||
shell: pwsh
|
||||
run: dotnet tool install --global wix --version 5.0.2
|
||||
|
|
|
|||
|
|
@ -445,7 +445,9 @@ Questions, ideas or a bug you'd like to talk through first? Bring them to [Disco
|
|||
| arm64 (Snapdragon and other ARM PCs) | `lightcraft-<ver>-windows-arm64.msi` | `lightcraft-<ver>-windows-arm64-portable.zip` |
|
||||
| x86 (32-bit) | `lightcraft-<ver>-windows-x86.msi` | `lightcraft-<ver>-windows-x86-portable.zip` |
|
||||
|
||||
Installers and executables are code-signed.
|
||||
Installers and executables are code-signed. The installer asks where to install (`C:\Program Files\LightCraft` by
|
||||
default; upgrades stay in the folder you chose) and ends on a page confirming LightCraft was installed, with an
|
||||
option to start it. For unattended installs: `msiexec /i lightcraft-<ver>-windows-x64.msi /qn INSTALLFOLDER="D:\Apps\LightCraft\"`.
|
||||
|
||||
### macOS
|
||||
|
||||
|
|
|
|||
|
|
@ -145,6 +145,10 @@
|
|||
)
|
||||
|
||||
### Reliability
|
||||
- The Windows installer asks where to install LightCraft (Program Files by default; upgrades keep the folder you
|
||||
chose) and ends on a page saying it was installed, with a "Launch LightCraft" box. It used to finish without a word,
|
||||
so a successful install looked like nothing had happened (issues #18, #399). Silent installs (`/qn`, `/passive`)
|
||||
show no dialogs and take `INSTALLFOLDER=...`.
|
||||
- If the desktop app can't open its window (for example when no graphics device can be used), it now says so in a
|
||||
message box that names the log file, instead of quitting without a trace (issue #260).
|
||||
- On macOS, single-key shortcuts that appear in the menu bar now work: E, C, H, M, ⇧P, I, K, D, ratings 0–5,
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
LightCraft MSI (WiX Toolset v5: `dotnet tool install -g wix`). Built by package.ps1:
|
||||
LightCraft 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 lightcraft.wxs -arch x64 -d Version=1.2.3 -d BinDir=... -d IconPath=... -o out.msi
|
||||
wix build lightcraft.wxs -arch x64 -ext WixToolset.UI.wixext/5.0.2 -ext WixToolset.Util.wixext/5.0.2
|
||||
-d Version=1.2.3 -d BinDir=... -d IconPath=... -d UiDialogBmp=... -d UiBannerBmp=... -o out.msi
|
||||
|
||||
Per-machine install into Program Files, a Start Menu shortcut, `.pcraft` owned by LightCraft,
|
||||
Per-machine install into Program Files (or the folder chosen in the installer, or
|
||||
`INSTALLFOLDER=...` on the msiexec command line; upgrades keep it), a Start Menu shortcut, `.pcraft` owned by LightCraft,
|
||||
and LightCraft offered under "Open with" for PSD/PSB and the image formats it reads (without
|
||||
taking over the user's default app for them).
|
||||
-->
|
||||
|
|
@ -33,13 +36,133 @@
|
|||
<StandardDirectory Id="ProgramFiles6432Folder">
|
||||
<Directory Id="INSTALLFOLDER" Name="LightCraft" />
|
||||
</StandardDirectory>
|
||||
<!-- The folder of the installed version (written by LightcraftApp), so an upgrade installs
|
||||
where the user chose instead of moving back to Program Files (issue #399). Searched into
|
||||
its own property: AppSearch would otherwise overwrite INSTALLFOLDER=... given to msiexec. -->
|
||||
<Property Id="PREVIOUSINSTALLFOLDER">
|
||||
<RegistrySearch Id="PreviousInstallFolder" Root="HKLM" Key="Software\LightCraft" Name="InstallFolder" Type="raw" />
|
||||
</Property>
|
||||
<SetProperty Id="INSTALLFOLDER" Value="[PREVIOUSINSTALLFOLDER]" After="AppSearch" Sequence="both"
|
||||
Condition="PREVIOUSINSTALLFOLDER AND NOT INSTALLFOLDER" />
|
||||
<StandardDirectory Id="ProgramMenuFolder" />
|
||||
|
||||
<Feature Id="Main" Title="LightCraft" Level="1">
|
||||
<ComponentGroupRef Id="LightcraftFiles" />
|
||||
</Feature>
|
||||
|
||||
<UIRef Id="LightcraftUI" />
|
||||
</Package>
|
||||
|
||||
<!--
|
||||
Interactive install UI: Welcome -> Destination Folder -> Ready to install -> progress ->
|
||||
"Completed" (Finish), with a "Launch LightCraft" checkbox (checked by default). Destination
|
||||
Folder (issue #399) shows Program Files\LightCraft, or the installed version's folder on an
|
||||
upgrade; Change... browses, and a folder that can't be used (InvalidDirDlg) is refused before
|
||||
anything is installed. Failure and cancellation end on WiX's standard FatalError / UserExit
|
||||
dialogs instead of the completion page. Maintenance (running the MSI again, Repair/Remove from
|
||||
Apps) uses the standard maintenance dialogs.
|
||||
|
||||
This is WixUI_InstallDir without its licence-acceptance page: LightCraft is MIT OR Apache-2.0,
|
||||
which ask nothing of people who just install and run it, so a click-through EULA would only
|
||||
add a step. Silent (/qn) and basic-UI (/passive, /qb) installs never show these dialogs, so
|
||||
they are unaffected, and nothing is launched.
|
||||
|
||||
The dialogs, strings and error/cancel pages come from WixToolset.UI.wixext; the side and
|
||||
banner bitmaps are plain panels generated by package.ps1 (no third-party artwork).
|
||||
-->
|
||||
<Fragment>
|
||||
<WixVariable Id="WixUIDialogBmp" Value="$(var.UiDialogBmp)" />
|
||||
<WixVariable Id="WixUIBannerBmp" Value="$(var.UiBannerBmp)" />
|
||||
|
||||
<Property Id="WIXUI_EXITDIALOGOPTIONALTEXT"
|
||||
Value="LightCraft has been installed. You can start it at any time from the Start menu." />
|
||||
<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT" Value="Launch LightCraft" />
|
||||
<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOX" Value="1" />
|
||||
|
||||
<!-- "Launch LightCraft" on Finish. WixUnelevatedShellExec (Util extension) asks the desktop
|
||||
shell (Explorer) to start the program, so it runs as the signed-in user at normal
|
||||
integrity even when the installer UI itself is elevated (e.g. msiexec started from an
|
||||
admin prompt); it never runs from the elevated/SYSTEM server side. It is a UI-only control
|
||||
event, so silent and passive installs never run it. Return="ignore": a failed launch must
|
||||
not turn a successful install into an error. -->
|
||||
<Property Id="WixUnelevatedShellExecTarget" Value="[#LightcraftExe]" />
|
||||
<CustomAction Id="LaunchLightcraft" BinaryRef="Wix4UtilCA_$(sys.BUILDARCHSHORT)"
|
||||
DllEntry="WixUnelevatedShellExec" Execute="immediate" Impersonate="yes" Return="ignore" />
|
||||
|
||||
<UI Id="LightcraftUI">
|
||||
<TextStyle Id="WixUI_Font_Normal" FaceName="Tahoma" Size="8" />
|
||||
<TextStyle Id="WixUI_Font_Bigger" FaceName="Tahoma" Size="12" />
|
||||
<TextStyle Id="WixUI_Font_Title" FaceName="Tahoma" Size="9" Bold="yes" />
|
||||
<Property Id="DefaultUIFont" Value="WixUI_Font_Normal" />
|
||||
|
||||
<DialogRef Id="ErrorDlg" />
|
||||
<DialogRef Id="FatalError" />
|
||||
<DialogRef Id="FilesInUse" />
|
||||
<DialogRef Id="MsiRMFilesInUse" />
|
||||
<DialogRef Id="PrepareDlg" />
|
||||
<DialogRef Id="ProgressDlg" />
|
||||
<DialogRef Id="ResumeDlg" />
|
||||
<DialogRef Id="UserExit" />
|
||||
<DialogRef Id="WelcomeDlg" />
|
||||
<DialogRef Id="InstallDirDlg" />
|
||||
<DialogRef Id="BrowseDlg" />
|
||||
<DialogRef Id="InvalidDirDlg" />
|
||||
<DialogRef Id="DiskCostDlg" />
|
||||
<DialogRef Id="VerifyReadyDlg" />
|
||||
<DialogRef Id="MaintenanceWelcomeDlg" />
|
||||
<DialogRef Id="MaintenanceTypeDlg" />
|
||||
|
||||
<!-- The folder InstallDirDlg edits. -->
|
||||
<Property Id="WIXUI_INSTALLDIR" Value="INSTALLFOLDER" />
|
||||
|
||||
<!-- First install / upgrade: Welcome -> Destination Folder -> Ready to install. A patch skips
|
||||
the folder (it can't move an installed product). -->
|
||||
<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="InstallDirDlg" Condition="NOT Installed" />
|
||||
<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg" Condition="Installed AND PATCH" />
|
||||
|
||||
<!-- Destination Folder, as in WixUI_InstallDir: Next sets the folder, validates it (WiX's
|
||||
WixUIValidatePath) and refuses one that can't be used; Change... opens the folder browser,
|
||||
whose OK validates too. -->
|
||||
<Publish Dialog="InstallDirDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg" />
|
||||
<Publish Dialog="InstallDirDlg" Control="Next" Event="SetTargetPath" Value="[WIXUI_INSTALLDIR]" Order="1" />
|
||||
<Publish Dialog="InstallDirDlg" Control="Next" Event="DoAction" Value="WixUIValidatePath_$(sys.BUILDARCHSHORT)" Order="2"
|
||||
Condition="NOT WIXUI_DONTVALIDATEPATH" />
|
||||
<Publish Dialog="InstallDirDlg" Control="Next" Event="SpawnDialog" Value="InvalidDirDlg" Order="3"
|
||||
Condition="NOT WIXUI_DONTVALIDATEPATH AND WIXUI_INSTALLDIR_VALID<>"1"" />
|
||||
<Publish Dialog="InstallDirDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg" Order="4"
|
||||
Condition="WIXUI_DONTVALIDATEPATH OR WIXUI_INSTALLDIR_VALID="1"" />
|
||||
<Publish Dialog="InstallDirDlg" Control="ChangeFolder" Property="_BrowseProperty" Value="[WIXUI_INSTALLDIR]" Order="1" />
|
||||
<Publish Dialog="InstallDirDlg" Control="ChangeFolder" Event="SpawnDialog" Value="BrowseDlg" Order="2" />
|
||||
<Publish Dialog="BrowseDlg" Control="OK" Event="DoAction" Value="WixUIValidatePath_$(sys.BUILDARCHSHORT)" Order="3"
|
||||
Condition="NOT WIXUI_DONTVALIDATEPATH" />
|
||||
<Publish Dialog="BrowseDlg" Control="OK" Event="SpawnDialog" Value="InvalidDirDlg" Order="4"
|
||||
Condition="NOT WIXUI_DONTVALIDATEPATH AND WIXUI_INSTALLDIR_VALID<>"1"" />
|
||||
|
||||
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="InstallDirDlg" Order="1"
|
||||
Condition="NOT Installed" />
|
||||
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg" Order="3"
|
||||
Condition="Installed AND PATCH" />
|
||||
|
||||
<!-- Maintenance: Welcome -> Repair / Remove -> Ready. -->
|
||||
<Publish Dialog="MaintenanceWelcomeDlg" Control="Next" Event="NewDialog" Value="MaintenanceTypeDlg" />
|
||||
<Publish Dialog="MaintenanceTypeDlg" Control="RepairButton" Event="NewDialog" Value="VerifyReadyDlg" />
|
||||
<Publish Dialog="MaintenanceTypeDlg" Control="RemoveButton" Event="NewDialog" Value="VerifyReadyDlg" />
|
||||
<Publish Dialog="MaintenanceTypeDlg" Control="Back" Event="NewDialog" Value="MaintenanceWelcomeDlg" />
|
||||
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="MaintenanceTypeDlg" Order="2"
|
||||
Condition="Installed AND NOT PATCH" />
|
||||
|
||||
<!-- Completion page: optionally launch, then close. -->
|
||||
<Publish Dialog="ExitDialog" Control="Finish" Event="DoAction" Value="LaunchLightcraft" Order="1"
|
||||
Condition="WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 AND NOT Installed" />
|
||||
<Publish Dialog="ExitDialog" Control="Finish" Event="EndDialog" Value="Return" Order="999" />
|
||||
|
||||
<!-- One feature, nothing to change: no "Change" in Apps & features. -->
|
||||
<Property Id="ARPNOMODIFY" Value="1" />
|
||||
</UI>
|
||||
|
||||
<UIRef Id="WixUI_Common" />
|
||||
</Fragment>
|
||||
|
||||
<Fragment>
|
||||
<ComponentGroup Id="LightcraftFiles" Directory="INSTALLFOLDER">
|
||||
<Component Id="LightcraftApp" Guid="8A1D3A39-F7C1-47B1-97AE-40C80BF82F4A">
|
||||
|
|
@ -50,6 +173,7 @@
|
|||
</File>
|
||||
|
||||
<RegistryValue Root="HKLM" Key="Software\Microsoft\Windows\CurrentVersion\App Paths\lightcraft.exe" Value="[#LightcraftExe]" Type="string" />
|
||||
<RegistryValue Root="HKLM" Key="Software\LightCraft" Name="InstallFolder" Value="[INSTALLFOLDER]" Type="string" />
|
||||
</Component>
|
||||
|
||||
<Component Id="LightcraftCli">
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@
|
|||
|
||||
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
|
||||
The WiX UI and Util extensions (installer dialogs, "Launch LightCraft" on Finish) are added to
|
||||
the global WiX extension cache by this script (wix extension add -g ..., needs network once).
|
||||
|
||||
.EXAMPLE
|
||||
pwsh packaging/windows/package.ps1 -Arch x64
|
||||
|
|
@ -32,6 +34,29 @@ function Invoke-Native([string] $What, [scriptblock] $Block) {
|
|||
if ($LASTEXITCODE -ne 0) { throw "$What failed with exit code $LASTEXITCODE" }
|
||||
}
|
||||
|
||||
# A 24-bit BMP of two flat colours: $SplitX pixels of $Left, then $Right (colours as R,G,B).
|
||||
# Used for the installer's side/banner bitmaps so the MSI ships no third-party artwork.
|
||||
function New-PanelBitmap([string] $Path, [int] $Width, [int] $Height, [int] $SplitX, [byte[]] $Left, [byte[]] $Right) {
|
||||
$stride = [int][Math]::Ceiling($Width * 3 / 4) * 4
|
||||
$row = New-Object byte[] $stride
|
||||
for ($x = 0; $x -lt $Width; $x++) {
|
||||
$c = if ($x -lt $SplitX) { $Left } else { $Right }
|
||||
$row[$x * 3] = $c[2]; $row[$x * 3 + 1] = $c[1]; $row[$x * 3 + 2] = $c[0] # BMP pixels are B,G,R
|
||||
}
|
||||
$pixels = $stride * $Height
|
||||
$ms = [IO.MemoryStream]::new()
|
||||
$w = [IO.BinaryWriter]::new($ms)
|
||||
$w.Write([byte]0x42); $w.Write([byte]0x4D) # 'BM'
|
||||
$w.Write([uint32](54 + $pixels)); $w.Write([uint32]0); $w.Write([uint32]54)
|
||||
$w.Write([uint32]40); $w.Write([int32]$Width); $w.Write([int32]$Height) # BITMAPINFOHEADER
|
||||
$w.Write([uint16]1); $w.Write([uint16]24); $w.Write([uint32]0); $w.Write([uint32]$pixels)
|
||||
$w.Write([int32]2835); $w.Write([int32]2835); $w.Write([uint32]0); $w.Write([uint32]0)
|
||||
for ($y = 0; $y -lt $Height; $y++) { $w.Write($row) }
|
||||
$w.Flush()
|
||||
[IO.File]::WriteAllBytes($Path, $ms.ToArray())
|
||||
$w.Dispose()
|
||||
}
|
||||
|
||||
# The version lives in one place: [workspace.package] version in the root Cargo.toml.
|
||||
$Version = $env:LIGHTCRAFT_VERSION
|
||||
if (-not $Version) {
|
||||
|
|
@ -91,10 +116,26 @@ Copy-Item (Join-Path $Bin 'lightcraft.exe'), (Join-Path $Bin 'lightcraft-cli.exe
|
|||
& (Join-Path $PSScriptRoot 'sign.ps1') (Join-Path $Stage 'lightcraft.exe') (Join-Path $Stage 'lightcraft-cli.exe')
|
||||
|
||||
# ---- MSI ---------------------------------------------------------------------------------------
|
||||
# Extensions must match the WiX tool version (5.0.2, see release.yml). Re-adding is harmless.
|
||||
$WixVersion = '5.0.2'
|
||||
$WixExtensions = 'WixToolset.UI.wixext', 'WixToolset.Util.wixext'
|
||||
foreach ($ext in $WixExtensions) {
|
||||
Invoke-Native "wix extension add $ext/$WixVersion" { wix extension add -g "$ext/$WixVersion" }
|
||||
}
|
||||
$ExtArgs = foreach ($ext in $WixExtensions) { '-ext'; "$ext/$WixVersion" }
|
||||
|
||||
# Installer dialog bitmaps (WixUI sizes): a brand-blue side panel on the welcome/finish pages and
|
||||
# a plain white banner on the inner pages. Generated here, never committed.
|
||||
$UiDialogBmp = Join-Path $Stage 'ui-dialog.bmp'
|
||||
$UiBannerBmp = Join-Path $Stage 'ui-banner.bmp'
|
||||
New-PanelBitmap $UiDialogBmp 493 312 164 ([byte[]](0x4E, 0x7B, 0xFB)) ([byte[]](0xFF, 0xFF, 0xFF))
|
||||
New-PanelBitmap $UiBannerBmp 493 58 0 ([byte[]](0xFF, 0xFF, 0xFF)) ([byte[]](0xFF, 0xFF, 0xFF))
|
||||
|
||||
$Msi = Join-Path $Dist "lightcraft-$Version-windows-$Arch.msi"
|
||||
Invoke-Native 'wix build' {
|
||||
wix build (Join-Path $PSScriptRoot 'lightcraft.wxs') -arch $Arch `
|
||||
wix build (Join-Path $PSScriptRoot 'lightcraft.wxs') -arch $Arch -culture en-us @ExtArgs `
|
||||
-d "Version=$MsiVersion" -d "BinDir=$Stage" -d "IconPath=$(Join-Path $Root 'assets\app-icon\lightcraft.ico')" `
|
||||
-d "UiDialogBmp=$UiDialogBmp" -d "UiBannerBmp=$UiBannerBmp" `
|
||||
-o $Msi
|
||||
}
|
||||
# wix writes its debug symbols (.wixpdb) next to the MSI; keep them out of the release assets.
|
||||
|
|
|
|||
Loading…
Reference in New Issue