mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-14 19:27:04 -04:00
* #305: harden Windows MSI deployment and progress UI Co-authored-by: Codex <[email protected]> * Keep the MSI manufacturer as Learning Machines LLC The company name matches the Linux packages; changing it is the owner's call. Co-Authored-By: Claude Opus 5.5 <[email protected]> * #305: keep compiled-MSI checks compatible with PowerShell COM Validated the compiled x64 MSI and negative scope, publisher and progress mutations. Co-authored-by: Codex <[email protected]> * ROADMAP: the publisher name is unchanged Co-Authored-By: Claude Opus 5.5 <[email protected]> --------- Co-authored-by: Codex <[email protected]> Co-authored-by: Brandon Thomas <[email protected]> Co-authored-by: Claude Opus 5.5 <[email protected]>
This commit is contained in:
6 files changed
+75
-6
No files matched your search
@@ -42,9 +42,8 @@
|
||||
|
||||
<Dialog Id="InstallProgress" Width="370" Height="180" Title="[ProductName] Setup" Modeless="yes">
|
||||
<Control Id="Title" Type="Text" X="20" Y="20" Width="330" Height="25" Text="{\InstallerTitle}Setting up [ProductName]" />
|
||||
<Control Id="Status" Type="Text" X="20" Y="60" Width="330" Height="40" Text="Please wait while setup completes.">
|
||||
<Subscribe Event="ActionText" Attribute="Text" />
|
||||
</Control>
|
||||
<!-- No ActionText table is authored: subscribing would blank this message (#305). -->
|
||||
<Control Id="Status" Type="Text" X="20" Y="60" Width="330" Height="40" Text="Please wait while setup completes." />
|
||||
<Control Id="Progress" Type="ProgressBar" X="20" Y="110" Width="330" Height="15" ProgressBlocks="yes">
|
||||
<Subscribe Event="SetProgress" Attribute="Progress" />
|
||||
</Control>
|
||||
|
||||
@@ -19,6 +19,10 @@
|
||||
Compressed="yes"
|
||||
Language="1033">
|
||||
<SummaryInformation Description="PdfCraft PDF workbench" Manufacturer="Learning Machines LLC" />
|
||||
<!-- HKLM registrations must never point into a per-user installation (#305). Allow removal
|
||||
of an existing installation even if an older MSI let it install per-user. -->
|
||||
<Launch Condition="ALLUSERS = 1 OR (Installed AND REMOVE = "ALL")"
|
||||
Message="PdfCraft must be installed for all users. Run setup with administrator privileges and ALLUSERS=1; per-user installation is not supported." />
|
||||
<!-- Same-version upgrades let successive pre-releases of one X.Y.Z replace each other
|
||||
(MSI versions are numeric, so 1.2.3-rc.1 and 1.2.3-rc.2 are both 1.2.3). -->
|
||||
<MajorUpgrade AllowSameVersionUpgrades="yes"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Check the compiled MSI's shortcuts, desktop-shortcut checkbox and full-UI outcome wiring (#143), without installing it.
|
||||
Check the compiled MSI's install scope, publisher, shortcuts and native UI, without installing it.
|
||||
.EXAMPLE
|
||||
pwsh packaging/windows/test-msi.ps1 dist/release/pdfcraft-0.2.1-windows-x64.msi
|
||||
#>
|
||||
@@ -24,6 +24,55 @@ function Assert-Equal($Actual, $Expected, [string] $What) {
|
||||
if ($Actual -cne $Expected) { throw "$What`: expected '$Expected', got '$Actual'" }
|
||||
}
|
||||
|
||||
function Assert-NoRow([string] $Sql, [string] $What) {
|
||||
$view = $Database.OpenView($Sql)
|
||||
try {
|
||||
[void] $view.Execute()
|
||||
if ($view.Fetch()) { throw "Unexpected MSI row ($What): $Sql" }
|
||||
} finally { [void] $view.Close() }
|
||||
}
|
||||
|
||||
# Test the compiled condition with Windows Installer's evaluator, in a restricted session that
|
||||
# cannot change machine state. Normal installs/repairs work, per-user overrides fail, and removal
|
||||
# of an older incorrectly scoped installation remains possible (#305).
|
||||
$scopeMessage = 'PdfCraft must be installed for all users. Run setup with administrator privileges and ALLUSERS=1; per-user installation is not supported.'
|
||||
$scopeCondition = Read-Row ('SELECT `Condition` FROM `LaunchCondition` WHERE `Description` = ''' + $scopeMessage + '''') 1
|
||||
$Installer.UILevel = 2
|
||||
$session = $null
|
||||
try {
|
||||
$session = $Installer.OpenPackage((Resolve-Path -LiteralPath $Path).Path, 1)
|
||||
foreach ($case in @(
|
||||
@('machine install', '1', '', '', '', 1),
|
||||
@('machine repair', '1', '', '1', '', 1),
|
||||
@('forced per-user install', '2', '1', '', '', 0),
|
||||
@('empty ALLUSERS', '', '', '', '', 0),
|
||||
@('resolved per-user install', '', '1', '', '', 0),
|
||||
@('forced per-user repair', '2', '1', '1', '', 0),
|
||||
@('machine uninstall', '1', '', '1', 'ALL', 1),
|
||||
@('legacy per-user uninstall', '', '1', '1', 'ALL', 1))) {
|
||||
$session.Property('ALLUSERS') = $case[1]
|
||||
$session.Property('MSIINSTALLPERUSER') = $case[2]
|
||||
$session.Property('Installed') = $case[3]
|
||||
$session.Property('REMOVE') = $case[4]
|
||||
Assert-Equal ($session.EvaluateCondition($scopeCondition[0])) $case[5] $case[0]
|
||||
}
|
||||
} finally {
|
||||
if ($session) { [void] [Runtime.InteropServices.Marshal]::FinalReleaseComObject($session) }
|
||||
}
|
||||
foreach ($sequence in @('InstallUISequence', 'InstallExecuteSequence')) {
|
||||
$launch = Read-Row ('SELECT `Condition`, `Sequence` FROM `' + $sequence + '` WHERE `Action` = ''LaunchConditions''') 2
|
||||
Assert-Equal $launch[0] '' "$sequence launch conditions are unconditional"
|
||||
$cost = Read-Row ('SELECT `Sequence` FROM `' + $sequence + '` WHERE `Action` = ''CostInitialize''') 1
|
||||
if ([int] $launch[1] -le 0 -or [int] $launch[1] -ge [int] $cost[0]) {
|
||||
throw "$sequence must reject per-user overrides before costing"
|
||||
}
|
||||
}
|
||||
$manufacturer = Read-Row 'SELECT `Value` FROM `Property` WHERE `Property` = ''Manufacturer''' 1
|
||||
Assert-Equal $manufacturer[0] 'Learning Machines LLC' 'MSI manufacturer'
|
||||
$status = Read-Row 'SELECT `Text` FROM `Control` WHERE `Dialog_` = ''InstallProgress'' AND `Control` = ''Status''' 1
|
||||
Assert-Equal $status[0] 'Please wait while setup completes.' 'Persistent progress message'
|
||||
Assert-NoRow 'SELECT `Event` FROM `EventMapping` WHERE `Dialog_` = ''InstallProgress'' AND `Control_` = ''Status''' 'progress text subscription'
|
||||
|
||||
# Plain (non-advertised) shortcuts to pdfcraft.exe, each in its own component; the desktop one is
|
||||
# gated by INSTALLDESKTOPSHORTCUT, which defaults to 1 and is secure so the UI choice reaches the
|
||||
# elevated install.
|
||||
@@ -86,4 +135,4 @@ $rm = Read-Row 'SELECT `Dialog` FROM `Dialog` WHERE `Dialog` = ''MsiRMFilesInUse
|
||||
Assert-Equal $rm[0] 'MsiRMFilesInUse' 'Files-in-use dialog'
|
||||
[void] [Runtime.InteropServices.Marshal]::FinalReleaseComObject($Database)
|
||||
[void] [Runtime.InteropServices.Marshal]::FinalReleaseComObject($Installer)
|
||||
Write-Output 'ok MSI: Start Menu shortcut, optional desktop shortcut (default on, checkbox), icon/key path, full-UI success/cancel/error and Finish controls, files-in-use dialog'
|
||||
Write-Output 'ok MSI: per-machine scope guard, publisher, persistent progress text, Start Menu shortcut, optional desktop shortcut (default on, checkbox), icon/key path, full-UI success/cancel/error and Finish controls, files-in-use dialog'
|
||||
Reference in new issue
Block a user