Skip to content

fix(msi): ship WinUI 3 GUI; pick host-arch makepri.exe#2

Merged
rodchristiansen merged 2 commits into
mainfrom
fix/msi-gui
May 10, 2026
Merged

fix(msi): ship WinUI 3 GUI; pick host-arch makepri.exe#2
rodchristiansen merged 2 commits into
mainfrom
fix/msi-gui

Conversation

@rodchristiansen
Copy link
Copy Markdown
Contributor

Summary

  • Wire Generate-GuiAppFiles.ps1 into the wixproj as a BeforeBuild target so the WinUI 3 publish output is harvested into a GuiAppFiles ComponentGroup (463 files — BootstrapMate.exe, Microsoft.UI.Xaml runtime, App.xbf/MainWindow.xbf, Assets/, locale .mui).
  • Add <ComponentGroupRef Id="GuiAppFiles" /> to the DefaultFeature; drop the dead single-file BootstrapMateGuiApp component that only shipped BootstrapMate.exe with no deps and was conditional on APP_DIR <> "".
  • Suppress ICE03 — WinUI MUI locales gd-gb, mi-NZ aren't in WiX's ICE validation table, and the master Microsoft.ui.xaml.dll's embedded locale list overflows the File.Language 255-char column. Both are benign at install time.
  • Pick makepri.exe host-arch first in build.ps1. The previous arm64-first order picked a binary that can't run on x64 hosts (The specified executable is not a valid application for this OS platform) and silently fell back to cached publish/app contents.

Result

Before: signed BootstrapMate-x64-2026.04.24.1542.msi was 28 MB and installed only installapplications.exe to C:\Program Files\BootstrapMate\ (1 file).

After: signed BootstrapMate-x64-2026.04.27.1219.msi is 86 MB and installs 544 files including BootstrapMate.exe, App.xbf, MainWindow.xbf, the full Microsoft.UI.Xaml.* runtime, Microsoft.UI.* Windows App SDK, and Assets/.

Test plan

  • dotnet build installer/BootstrapMate.Installer.wixproj -p:Platform=x64 succeeds (463-file harvest, 0 warnings, 0 errors)
  • .\build.ps1 -Architecture both -Thumbprint <cert> produces signed x64 + arm64 MSI and .intunewin
  • x64 MSI Authenticode-signed by EmilyCarrU Intune Windows Enterprise Certificate (6516E0FE…), DigiCert-timestamped
  • After install: C:\Program Files\BootstrapMate\BootstrapMate.exe is present and signed
  • arm64 install validation on an ARM64 host (build artifact present, install untested)
  • Push to Intune via provisioning/deploy.ps1 -App and confirm fleet endpoints land both binaries

- Wire Generate-GuiAppFiles.ps1 into the wixproj as a BeforeBuild
  target so the 463-file WinUI 3 publish output (BootstrapMate.exe,
  Microsoft.UI.Xaml.*, App.xbf, MainWindow.xbf, Assets, locale .mui)
  is harvested into a GuiAppFiles ComponentGroup.
- Add ComponentGroupRef GuiAppFiles to the DefaultFeature; drop the
  dead single-file BootstrapMateGuiApp component that only shipped
  BootstrapMate.exe with no deps.
- Suppress ICE03 — WinUI MUI locales (gd-gb, mi-NZ) and the master
  Microsoft.ui.xaml.dll's >255-char locale list trip ICE03 but are
  benign at install time.
- Pick makepri.exe host-arch first; previous arm64-first ordering
  picked a binary that can't run on x64 hosts ("not a valid
  application for this OS platform") and silently fell back to the
  cached publish/app contents.

Result: signed MSI now installs 544 files including the GUI, vs 1
(installapplications.exe only) before.
Copilot AI review requested due to automatic review settings April 27, 2026 19:40
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the WiX-based MSI packaging so the WinUI 3 GUI app (and its runtime dependencies) is included in the installer, and fixes build.ps1 so it selects a host-compatible makepri.exe when generating PRI resources.

Changes:

  • Wire a pre-build MSBuild target to generate/compile a harvested GuiAppFiles ComponentGroup from the GUI publish output and reference it from the default feature.
  • Remove the old single-file GUI component and suppress ICE03 to accommodate WinUI MUI language metadata edge cases.
  • Prefer host-architecture makepri.exe selection to avoid picking an ARM64 tool on x64 hosts.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
installer/Product.wxs Switches from a single GUI exe component to a harvested GuiAppFiles ComponentGroup referenced by DefaultFeature.
installer/BootstrapMate.Installer.wixproj Adds ICE suppression and a pre-build target to generate/compile GuiAppFiles.wxs; adds a GUI publish-output validation error.
build.ps1 Fixes makepri.exe selection order to prefer host architecture first.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

<Target Name="BeforeBuild">
<Error Text="BootstrapMate executable not found at $(BinDir)\installapplications.exe. Please run build.ps1 first."
Condition="!Exists('$(BinDir)\installapplications.exe')" />
<Error Text="BootstrapMate GUI app directory not found at $(AppDir). Run build.ps1 to publish the GUI before building the MSI."
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error text says the GUI app directory is not found, but the Condition checks for a specific file ($(AppDir)\BootstrapMate.exe). Update the message to reflect what’s actually being validated (or change the Condition to check the directory) so troubleshooting output is accurate.

Suggested change
<Error Text="BootstrapMate GUI app directory not found at $(AppDir). Run build.ps1 to publish the GUI before building the MSI."
<Error Text="BootstrapMate GUI app executable not found at $(AppDir)\BootstrapMate.exe. Run build.ps1 to publish the GUI before building the MSI."

Copilot uses AI. Check for mistakes.
Comment thread installer/Product.wxs Outdated
<File Id="BootstrapMateGuiExe" Source="$(var.APP_DIR)\BootstrapMate.exe" Vital="yes" />
</Component>
<!-- GUI Application files (BootstrapMate.exe + WinUI 3 dependencies) are
harvested from $(APP_DIR) into the GuiAppFiles ComponentGroup by the
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment refers to $(APP_DIR), but WiX preprocessor variables elsewhere in this file use the $(var.X) form (e.g., $(var.BIN_DIR)). To avoid confusion for future edits, consider updating the comment to reference $(var.APP_DIR) (or explicitly call out that APP_DIR is a DefineConstant provided by the wixproj).

Suggested change
harvested from $(APP_DIR) into the GuiAppFiles ComponentGroup by the
harvested from $(var.APP_DIR) into the GuiAppFiles ComponentGroup by the

Copilot uses AI. Check for mistakes.
Comment on lines +41 to +47
<Target Name="GenerateGuiAppFiles" BeforeTargets="BeforeBuild">
<MakeDir Directories="$(IntermediateOutputPath)" />
<Exec Command="pwsh -NoProfile -ExecutionPolicy Bypass -File &quot;$(MSBuildProjectDirectory)\Generate-GuiAppFiles.ps1&quot; -AppDir &quot;$(AppDir)&quot; -OutputPath &quot;$(IntermediateOutputPath)GuiAppFiles.wxs&quot;" />
<ItemGroup>
<Compile Include="$(IntermediateOutputPath)GuiAppFiles.wxs" />
</ItemGroup>
</Target>
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generate-GuiAppFiles.ps1 currently generates sequential Component/File IDs (c_00001, f_00001, …) which will shift whenever files are added/removed. Because the script also uses Guid="*", this can churn many component GUIDs across releases and lead to unreliable MSI upgrades (old files orphaned / components treated as new). Prefer deterministic IDs derived from each file’s relative path (e.g., a stable hash) so unchanged files keep the same Component Id (and therefore GUID) between builds.

Copilot uses AI. Check for mistakes.
<SuppressIces>ICE03</SuppressIces>
</PropertyGroup>

<Target Name="GenerateGuiAppFiles" BeforeTargets="BeforeBuild">
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GenerateGuiAppFiles runs BeforeBuild, but the missing-GUI check is implemented in the BeforeBuild target. If $(AppDir) doesn’t exist, the script will fail first and the more actionable MSBuild won’t be shown. Consider adding a Condition to the Exec/Target (or moving this target to run after the validation) so the build consistently fails with the intended error message when the GUI publish output is missing.

Suggested change
<Target Name="GenerateGuiAppFiles" BeforeTargets="BeforeBuild">
<Target Name="GenerateGuiAppFiles"
BeforeTargets="BeforeBuild"
Condition="Exists('$(AppDir)\BootstrapMate.exe')">

Copilot uses AI. Check for mistakes.
- Generate-GuiAppFiles.ps1: derive Component/File IDs from a SHA1 hash
  of the relative path (truncated to 16 hex chars) instead of a sequential
  counter. Unchanged files now keep the same Component Id across builds —
  and therefore the same Guid="*"-derived GUID — so MSI upgrade tracking
  is stable when WinUI 3 ships a new locale or assets are added/removed.
  Hash input is lowercased for case-insensitive Windows path equivalence.
- BootstrapMate.Installer.wixproj: gate the GenerateGuiAppFiles target on
  Exists($(AppDir)\BootstrapMate.exe) so the friendlier <Error> in the
  BeforeBuild target fires when the GUI publish output is missing,
  rather than the harvester script's stack trace.
- BootstrapMate.Installer.wixproj: error text now references the executable
  path (was: "GUI app directory") to match the Condition's actual check.
- Product.wxs: comment now uses $(var.APP_DIR) to match the WiX
  preprocessor form used elsewhere in this file.
@rodchristiansen rodchristiansen merged commit 5c74f6c into main May 10, 2026
3 checks passed
@rodchristiansen rodchristiansen deleted the fix/msi-gui branch May 10, 2026 00:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants