Skip to content

Commit ca6a552

Browse files
committed
Installer(wix6): complete WiX 6 build pipeline + parity/evidence tooling
Resolve Heat from repo/NuGet, normalize paths, and generate harvest .wxs before compile Stabilize bundle StdBA payloads (theme/localization/logo/license) and align branding/version outputs Improve MSI upgrade/ARP behavior and fix bundle UI/title + MSI-cancel handling Add installer evidence capture + compare scripts, plus VS Code tasks for end-to-end installer checks Add deterministic Hyper-V parity lane (WiX3 vs WiX6) with checkpoint restore, runs, and evidence publishing Add installer artifact validation tests; set C# 8 defaults; update docs/specs and fix startup crash ordering
1 parent bebe79f commit ca6a552

File tree

91 files changed

+8084
-583
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

91 files changed

+8084
-583
lines changed

.github/instructions/installer.instructions.md

Lines changed: 104 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,42 +3,120 @@ applyTo: "FLExInstaller/**"
33
name: "installer.instructions"
44
description: "FieldWorks installer (WiX) development guidelines"
55
---
6-
# Installer development guidelines (WiX)
6+
# Installer development guidelines (WiX v6)
77

88
## Purpose & Scope
9-
Guidance for the installer project, packaging configuration, and localization of installer strings.
9+
Guidance for building, validating, and debugging the FieldWorks installer (WiX v6): MSI (`FieldWorks.msi`) and bootstrapper bundle (`FieldWorksBundle.exe`).
1010

11-
## Context loading
12-
- Only build the installer when changing installer logic or packaging; prefer app/library builds in inner loop.
13-
- Review `FLExInstaller/` and related `.wxs/.wixproj` files; confirm WiX 3.14.x tooling.
14-
- See `Docs/installer-build-guide.md` for local build instructions and CI workflow details.
11+
## Key facts (FieldWorks repo)
12+
- WiX projects live in `FLExInstaller/`:
13+
- `FieldWorks.Installer.wixproj` (MSI)
14+
- `FieldWorks.Bundle.wixproj` (bundle)
15+
- Builds are orchestrated via `Build/Orchestrator.proj` (preferred entrypoints are `./build.ps1` and `./test.ps1`).
16+
- Default installer artifacts (Debug) land under:
17+
- `FLExInstaller\bin\x64\Debug\en-US\FieldWorks.msi`
18+
- `FLExInstaller\bin\x64\Debug\FieldWorksBundle.exe`
1519

16-
## Quick Setup
20+
## Build commands
1721
```powershell
18-
# Validate installer build prerequisites
19-
.\Build\Agent\Setup-InstallerBuild.ps1 -ValidateOnly
22+
# Preferred: traversal build wrapper (Debug)
23+
./build.ps1 -BuildInstaller
2024
21-
# Set up for patch builds (downloads base artifacts)
22-
.\Build\Agent\Setup-InstallerBuild.ps1 -SetupPatch
25+
# Direct MSBuild (equivalent to the orchestration target)
26+
msbuild Build/Orchestrator.proj /t:BuildInstaller /p:Configuration=Debug /p:Platform=x64
27+
```
28+
29+
## Debugging flow: “double-click does nothing”
30+
When a bundle/MSI exits with no UI, assume one of:
31+
1) immediate process exit (argument parsing, prerequisite detection, policy)
32+
2) crash before UI (bad BA init, missing dependency, load failure)
33+
3) UI suppressed (quiet/passive mode, elevation issue)
34+
35+
Work from most observable → deepest inspection:
36+
37+
### 1) Run via the repo helper script (recommended)
38+
Use the agent-friendly wrapper script to run the installer and collect logs in a deterministic evidence folder:
39+
```powershell
40+
# Bundle (default path for the chosen configuration/platform)
41+
.\scripts\Agent\Invoke-Installer.ps1 -InstallerType Bundle -Configuration Debug -Platform x64
42+
43+
# Bundle with extra args and additional temp-log capture (useful for chained package logs)
44+
.\scripts\Agent\Invoke-Installer.ps1 -InstallerType Bundle -Configuration Release -Arguments @('/passive') -IncludeTempLogs
2345
24-
# Build base installer
25-
msbuild Build/Orchestrator.proj /t:BuildInstaller /p:Configuration=Debug /p:Platform=x64 /p:config=release /m
46+
# MSI (direct Windows Installer engine, with /l*v logging)
47+
.\scripts\Agent\Invoke-Installer.ps1 -InstallerType Msi -Configuration Debug -Platform x64
2648
27-
# Build patch installer (after -SetupPatch)
28-
msbuild Build/Orchestrator.proj /t:BuildPatchInstaller /p:Configuration=Debug /p:Platform=x64 /p:config=release /m
49+
# Custom path (if you're testing a copied artifact)
50+
.\scripts\Agent\Invoke-Installer.ps1 -InstallerType Bundle -InstallerPath 'C:\Path\To\FieldWorksBundle.exe'
2951
```
3052

31-
## Deterministic requirements
32-
- Versioning: Maintain consistent ProductCode/UpgradeCode policies; ensure patches use higher build numbers than bases.
33-
- Components/Features: Keep component GUID stability; avoid reshuffling that breaks upgrades.
34-
- Files: Use build outputs; avoid hand-copying artifacts.
35-
- Localization: Ensure installer strings align with repository localization patterns.
53+
Notes:
54+
- The script writes evidence under `Output\InstallerEvidence\<timestamp>\` and prints the primary log path.
55+
- `-IncludeTempLogs` copies common related logs from `%TEMP%` that were written during the run.
56+
57+
### 1a) Manual commands (when you need full control)
58+
Create an evidence folder and run explicitly:
59+
```powershell
60+
$e = Join-Path $env:TEMP ('FwInstallerEvidence\\' + (Get-Date -Format yyyy-MM-dd))
61+
$null = New-Item -ItemType Directory -Force -Path $e
62+
63+
# Bundle (preferred for end-user scenario)
64+
.\FLExInstaller\bin\x64\Debug\FieldWorksBundle.exe /log "$e\bundle.log"
65+
66+
# MSI (direct Windows Installer engine)
67+
msiexec /i .\FLExInstaller\bin\x64\Debug\en-US\FieldWorks.msi /l*v "$e\msi-install.log"
68+
```
69+
70+
Notes:
71+
- Windows Installer requires the target log directory to exist; if logging fails, create the folder first.
72+
73+
### 2) Check Windows Event Viewer and crash dumps
74+
If there is still “nothing”, check for an early crash:
75+
- Event Viewer → Windows Logs → Application
76+
- `.NET Runtime` and `Application Error`
77+
- `MsiInstaller` events for MSI failures
78+
- Crash dumps: `%LOCALAPPDATA%\CrashDumps\` for `*.dmp` related to the bundle or `msiexec.exe`.
79+
80+
### 3) Re-run with reduced noise (optional)
81+
MSI UI levels can hide dialogs; explicitly request UI:
82+
```powershell
83+
# Full UI (if authored):
84+
msiexec /i .\FLExInstaller\bin\x64\Debug\en-US\FieldWorks.msi /qf /l*v C:\Temp\FwInstallerEvidence\msi-ui.log
85+
86+
# Basic UI (progress only):
87+
msiexec /i .\FLExInstaller\bin\x64\Debug\en-US\FieldWorks.msi /qb /l*v C:\Temp\FwInstallerEvidence\msi-basic.log
88+
```
89+
90+
### 4) If the bundle starts but install fails: differentiate bundle vs MSI
91+
- Bundle log shows prerequisite detection, downloads, and chaining decisions.
92+
- MSI log shows property resolution, component/file install, registry writes, custom action execution.
93+
94+
### 5) If custom actions are suspected
95+
- Search the MSI log for:
96+
- `Return value 3` (classic MSI failure marker)
97+
- `CustomAction` lines and the action name that failed
98+
- Prefer reproducing with a single variable change at a time (different App/Data dirs; feature set; upgrade vs clean).
99+
100+
## WiX v6 build-time diagnostics (authoring/build)
101+
- WiX v6 is used via SDK-style `.wixproj` with MSBuild properties.
102+
- Useful knobs live on the project (or can be passed on the command line) such as:
103+
- `DefineConstants` (preprocessor variables)
104+
- `SuppressIces` / `Ices` / `SuppressValidation` (validation control)
105+
- `VerboseOutput` (more build output)
106+
- `*AdditionalOptions` properties to pass arbitrary `wix.exe` args
107+
108+
## Harvesting note (WiX v6)
109+
- FieldWorks currently uses Heat (via `WixToolset.Heat` NuGet) to generate harvested `.wxs` inputs.
110+
- Heat emits a deprecation warning (HEAT5149) and is expected to go away in WiX v7; treat this as technical debt to retire.
36111

37-
## Structured output
38-
- Always validate a local installer build when touching installer config.
39-
- Keep changes minimal and documented in commit messages.
112+
## Spec-backed verification (what to capture)
113+
- Verification matrix: `specs/001-wix-v6-migration/verification-matrix.md`
114+
- Golden install checklist: `specs/001-wix-v6-migration/golden-install-checklist.md`
115+
- Recommended evidence folder convention: `C:\Temp\FwInstallerEvidence\YYYY-MM-DD\`
40116

41117
## References
42-
- Build: See `Build/Installer.targets` and top-level build scripts.
43-
- CI: Patch/base installer workflows live under `.github/workflows/`.
44-
- Setup: `Build/Agent/Setup-InstallerBuild.ps1` for environment validation.
118+
- Windows Installer logging & command line:
119+
- https://learn.microsoft.com/windows/win32/msi/windows-installer-logging
120+
- https://learn.microsoft.com/windows/win32/msi/command-line-options
121+
- WiX v6 MSBuild SDK concepts/properties:
122+
- https://docs.firegiant.com/wix/tools/msbuild/

.github/workflows/base-installer-cd.yml

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,6 @@ on:
2222
description: "Commit-ish for helps repository"
2323
required: false
2424
default: "develop"
25-
installer_ref:
26-
description: "Commit-ish for PatchableInstaller repository"
27-
required: false
28-
default: "master"
2925
localizations_ref:
3026
description: "Commit-ish for localization repository"
3127
required: false
@@ -77,15 +73,6 @@ jobs:
7773
fetch-depth: 0
7874
path: "DistFiles/Helps"
7975

80-
- name: Checkout PatchableInstaller
81-
uses: actions/checkout@v4
82-
id: installer-checkout
83-
with:
84-
repository: "sillsdev/genericinstaller"
85-
ref: ${{ github.event.inputs.installer_ref || 'master' }}
86-
fetch-depth: 0
87-
path: "PatchableInstaller"
88-
8976
- name: Checkout Localizations
9077
uses: actions/checkout@v4
9178
id: loc-checkout
@@ -99,7 +86,7 @@ jobs:
9986
id: liblcm-checkout
10087
with:
10188
repository: "sillsdev/liblcm"
102-
ref: ${{ github.event.inputs.installer_ref || 'master' }}
89+
ref: ${{ github.event.inputs.lcm_ref || 'master' }}
10390
fetch-depth: 0
10491
path: "Localizations/LCM"
10592

@@ -265,7 +252,7 @@ jobs:
265252
$false
266253
)
267254
[System.IO.Compression.ZipFile]::CreateFromDirectory(
268-
"${{ github.workspace }}\PatchableInstaller\ProcRunner\ProcRunner\bin\Release\net48",
255+
"${{ github.workspace }}\FLExInstaller\Shared\ProcRunner\ProcRunner\bin\Release\net48",
269256
"${{ github.workspace }}\ProcRunner.zip",
270257
[System.IO.Compression.CompressionLevel]::Optimal,
271258
$false

.github/workflows/patch-installer-cd.yml

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,6 @@ on:
2222
description: "Commit-ish for helps repository"
2323
required: false
2424
default: "develop"
25-
installer_ref:
26-
description: "Commit-ish for PatchableInstaller repository"
27-
required: false
28-
default: "master"
2925
localizations_ref:
3026
description: "Commit-ish for localization repository"
3127
required: false
@@ -92,14 +88,6 @@ jobs:
9288
fetch-depth: 0
9389
path: "DistFiles/Helps"
9490

95-
- name: Checkout PatchableInstaller
96-
uses: actions/checkout@v4
97-
id: installer-checkout
98-
with:
99-
repository: "sillsdev/genericinstaller"
100-
ref: ${{ github.event.inputs.installer_ref || 'master' }}
101-
fetch-depth: 0
102-
path: "PatchableInstaller"
10391

10492
- name: Checkout Localizations
10593
uses: actions/checkout@v4
@@ -175,7 +163,7 @@ jobs:
175163
Write-Host "Expanded BuildDir.zip -> ./BuildDir"
176164
177165
# Ensure ProcRunner target path exists
178-
$procTarget = "PatchableInstaller/ProcRunner/ProcRunner/bin/Release/net48"
166+
$procTarget = "FLExInstaller/Shared/ProcRunner/ProcRunner/bin/Release/net48"
179167
if (-not (Test-Path $procTarget)) {
180168
New-Item -ItemType Directory -Path $procTarget -Force | Out-Null
181169
}

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ Downloads/
2626
Installer/
2727
Localizations/
2828
packages/
29-
PatchableInstaller/
3029
Bin/_setLatestBuildConfig.bat
3130
Bin/_setroot.bat
3231
Lib/debug/DebugProcs.lib
@@ -186,3 +185,4 @@ TestResults/
186185

187186
# Standard .NET
188187
bin/
188+
FLExInstaller/cabcache/*

.vscode/tasks.json

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,30 @@
5252
"type": "promptString",
5353
"description": "Branch name for worktree (e.g., spec/my-branch or release/9.3)",
5454
"default": ""
55+
},
56+
{
57+
"id": "installerSnapshotOut",
58+
"type": "promptString",
59+
"description": "Snapshot output path (file or directory)",
60+
"default": "Output/InstallerEvidence/Snapshots"
61+
},
62+
{
63+
"id": "installerSnapshotName",
64+
"type": "promptString",
65+
"description": "Snapshot name (used in filename when output is a directory)",
66+
"default": "manual"
67+
},
68+
{
69+
"id": "installerSnapshotBefore",
70+
"type": "promptString",
71+
"description": "Path to BEFORE snapshot JSON",
72+
"default": ""
73+
},
74+
{
75+
"id": "installerSnapshotAfter",
76+
"type": "promptString",
77+
"description": "Path to AFTER snapshot JSON",
78+
"default": ""
5579
}
5680
],
5781
"tasks": [
@@ -426,6 +450,45 @@
426450
},
427451
"problemMatcher": []
428452
},
453+
{
454+
"label": "Installer Evidence: Snapshot",
455+
"type": "shell",
456+
"command": "./scripts/Agent/Collect-InstallerSnapshot.ps1 -OutputPath '${input:installerSnapshotOut}' -Name '${input:installerSnapshotName}'",
457+
"detail": "Collect a machine snapshot (registry/files/uninstall entries) for installer evidence",
458+
"options": {
459+
"shell": {
460+
"executable": "powershell.exe",
461+
"args": ["-NoProfile", "-ExecutionPolicy", "Bypass", "-Command"]
462+
}
463+
},
464+
"problemMatcher": []
465+
},
466+
{
467+
"label": "Installer Evidence: Compare snapshots",
468+
"type": "shell",
469+
"command": "./scripts/Agent/Compare-InstallerSnapshots.ps1 -BeforeSnapshotPath '${input:installerSnapshotBefore}' -AfterSnapshotPath '${input:installerSnapshotAfter}'",
470+
"detail": "Compare two snapshot JSON files and print a diff report",
471+
"options": {
472+
"shell": {
473+
"executable": "powershell.exe",
474+
"args": ["-NoProfile", "-ExecutionPolicy", "Bypass", "-Command"]
475+
}
476+
},
477+
"problemMatcher": []
478+
},
479+
{
480+
"label": "Installer Check: Bundle (Debug, x64)",
481+
"type": "shell",
482+
"command": "./scripts/Agent/Invoke-InstallerCheck.ps1 -InstallerType Bundle -Configuration Debug -Platform x64",
483+
"detail": "Run snapshot -> install bundle -> snapshot -> diff (writes evidence under Output/InstallerEvidence/<RunId>)",
484+
"options": {
485+
"shell": {
486+
"executable": "powershell.exe",
487+
"args": ["-NoProfile", "-ExecutionPolicy", "Bypass", "-Command"]
488+
}
489+
},
490+
"problemMatcher": []
491+
},
429492

430493
// ==================== Git ====================
431494
{

0 commit comments

Comments
 (0)