diff --git a/.github/skills/fieldworks-installer-diagnostics/SKILL.md b/.github/skills/fieldworks-installer-diagnostics/SKILL.md new file mode 100644 index 0000000000..f852366af7 --- /dev/null +++ b/.github/skills/fieldworks-installer-diagnostics/SKILL.md @@ -0,0 +1,85 @@ +--- +name: fieldworks-installer-diagnostics +description: Use this skill for FieldWorks installer logging, diagnostics, evidence collection, and failure triage: double-click does nothing, bundle exits or hangs, uninstall hangs, ARP issues, prerequisite failures, custom action failures, MSI Return value 3, Burn detect/plan/apply analysis, Event Viewer/crash dumps, VM evidence folders, and before/after installer snapshots. Trigger for any FieldWorks installer debugging or diagnostic request. +--- + +# FieldWorks Installer Diagnostics + +This skill is evidence-first. Installer problems are often ambiguous until Burn logs, MSI logs, and machine state snapshots are collected. + +## Load References When Needed + +- Read `references/evidence-and-commands.md` for exact commands and evidence locations. +- Read `references/log-triage.md` for Burn/MSI/custom action triage recipes. + +## First Moves + +1. Read `.github/instructions/installer.instructions.md` and `.github/instructions/debugging.instructions.md`. +2. Read `scripts/Agent/Invoke-Installer.ps1`, `Invoke-InstallerCheck.ps1`, `Collect-InstallerSnapshot.ps1`, and `Compare-InstallerSnapshots.ps1` if changing or using diagnostics scripts. +3. Read `specs/001-wix-v6-migration/verification-matrix.md`, `golden-install-checklist.md`, and `REMAINING_WIX6_ISSUES.md` for the expected evidence shape. +4. Identify which artifact is under test: WiX 3 fallback/current default or WiX 6 migration path. Do not assume helper defaults point at WiX 6. + +## Evidence-First Workflow + +1. Create or choose an evidence folder. Repo convention: `Output/InstallerEvidence//` for agent scripts, or `C:\Temp\FwInstallerEvidence\YYYY-MM-DD\` for VM/manual runs. +2. Capture the command, artifact path, version, SHA256 if relevant, and machine snapshot/VM state. +3. Run the bundle or MSI with logging. For WiX 6 artifacts, pass explicit `-InstallerPath` when using helper scripts. +4. Include temp logs when chained packages or Burn package logs may be separate. +5. If the UI exits silently, capture Event Viewer entries and crash dumps. +6. Summarize the first failing layer: Burn bootstrapper, chained package/MSI, custom action, Windows Installer engine, or environment. + +## WinApp MCP Runtime Evidence + +When the WinApp MCP server is available, use it to make UI diagnostics repeatable: + +- Launch or attach to installer windows and wait for input idle before diagnosing UI state. +- If the app snapshot is empty, list visible desktop windows and use `get_focused_element` plus keyboard navigation to identify and exercise controls. +- Record the observed focus path and control names in the evidence notes, especially for silent/blank UI, MSI handoff, uninstall prompts, and ARP uninstall hangs. +- For safe UI-only smoke tests, stop after proving the license checkbox/Install/Cancel path; do not proceed into install unless the scenario requires machine changes. +- For hangs, combine WinApp focused-element/window evidence with bundle/MSI logs, before/after snapshots, Event Viewer entries, and process lists. +- If the bundle reaches elevated MSI internal UI, a non-elevated WinApp host may be limited to observation. Treat visible-but-non-clickable MSI controls as an automation integrity boundary until proven otherwise; capture the focused element, MSI log action, screenshots, process integrity/window ownership evidence, and rerun elevated for full manual UI control. + +## Commands + +Prefer the repo helper for repeatable runs: + +```powershell +./scripts/Agent/Invoke-Installer.ps1 -InstallerType Bundle -InstallerPath '.\FLExInstaller\wix6\bin\x64\Debug\FieldWorksBundle.exe' -IncludeTempLogs +./scripts/Agent/Invoke-Installer.ps1 -InstallerType Msi -InstallerPath '.\FLExInstaller\wix6\bin\x64\Debug\en-US\FieldWorks.msi' -IncludeTempLogs +``` + +Use explicit manual commands when needed: + +```powershell +.\FLExInstaller\wix6\bin\x64\Debug\FieldWorksBundle.exe /log C:\Temp\FwInstallerEvidence\bundle.log +msiexec /i .\FLExInstaller\wix6\bin\x64\Debug\en-US\FieldWorks.msi /l*v C:\Temp\FwInstallerEvidence\msi-install.log +msiexec /x {PRODUCT-CODE} /l*v C:\Temp\FwInstallerEvidence\msi-uninstall.log +``` + +For before/after evidence: + +```powershell +./scripts/Agent/Collect-InstallerSnapshot.ps1 -OutputPath C:\Temp\FwInstallerEvidence\before.json -Name before +./scripts/Agent/Collect-InstallerSnapshot.ps1 -OutputPath C:\Temp\FwInstallerEvidence\after.json -Name after +./scripts/Agent/Compare-InstallerSnapshots.ps1 -BeforeSnapshotPath C:\Temp\FwInstallerEvidence\before.json -AfterSnapshotPath C:\Temp\FwInstallerEvidence\after.json +``` + +## Triage Priorities + +- Bundle log first for Burn detection, prerequisite planning, cache/download failures, related bundle handling, and package log paths. +- MSI verbose log first for directory properties, feature states, standard action sequencing, and custom action failures. +- Search MSI logs for `Return value 3`, then walk upward to the failing action and property context. +- Search bundle logs for `Detected package`, `Planned package`, `Applying execute package`, `Error 0x`, and final result code. +- For custom actions, find the WiX action ID in `Framework.wxs`, then map it to the DllEntry in `CustomAction.cs`. +- For hangs, identify the last completed phase/action and whether a UI prompt, process-close prompt, files-in-use dialog, or elevation prompt is waiting. + +## Report Format + +When reporting diagnostics, include: + +- Artifact tested and command used. +- Evidence folder and primary logs. +- Failure layer and first meaningful error. +- Relevant Burn/MSI action/package names. +- Machine state: clean, prereqs present, old FieldWorks installed, offline, upgrade, repair, or uninstall. +- Next smallest fix or next evidence needed. diff --git a/.github/skills/fieldworks-installer-diagnostics/references/evidence-and-commands.md b/.github/skills/fieldworks-installer-diagnostics/references/evidence-and-commands.md new file mode 100644 index 0000000000..2fab7ba94b --- /dev/null +++ b/.github/skills/fieldworks-installer-diagnostics/references/evidence-and-commands.md @@ -0,0 +1,55 @@ +# FieldWorks Installer Evidence And Commands + +## Artifact Paths + +WiX 3 fallback/current default: + +- `FLExInstaller/bin/x64/Debug/en-US/FieldWorks.msi` +- `FLExInstaller/bin/x64/Debug/FieldWorksBundle.exe` + +WiX 6 migration path: + +- `FLExInstaller/wix6/bin/x64/Debug/en-US/FieldWorks.msi` +- `FLExInstaller/wix6/bin/x64/Debug/FieldWorksBundle.exe` +- `FLExInstaller/wix6/bin/x64/Debug/FieldWorksOfflineBundle.exe` + +Adjust `Debug` to `Release` as needed. + +## Helper Script Notes + +- `scripts/Agent/Invoke-Installer.ps1` creates `Output/InstallerEvidence//` by default. +- The helper's default artifact resolver historically points at `FLExInstaller/bin/...`; pass `-InstallerPath` explicitly for WiX 6 artifacts. +- Use `-IncludeTempLogs` for Burn package logs and chained package logs. +- Use `-SummarizeMsiFileAccess` when validating installed file payloads from MSI logs. + +## Common Runs + +Interactive WiX 6 bundle: + +```powershell +./scripts/Agent/Invoke-Installer.ps1 -InstallerType Bundle -InstallerPath '.\FLExInstaller\wix6\bin\x64\Debug\FieldWorksBundle.exe' -IncludeTempLogs +``` + +Passive WiX 6 bundle: + +```powershell +./scripts/Agent/Invoke-Installer.ps1 -InstallerType Bundle -InstallerPath '.\FLExInstaller\wix6\bin\x64\Debug\FieldWorksBundle.exe' -Arguments @('/passive') -IncludeTempLogs +``` + +Direct MSI full UI: + +```powershell +msiexec /i .\FLExInstaller\wix6\bin\x64\Debug\en-US\FieldWorks.msi /qf /l*v C:\Temp\FwInstallerEvidence\msi-ui.log +``` + +Uninstall by product code: + +```powershell +msiexec /x {PRODUCT-CODE} /l*v C:\Temp\FwInstallerEvidence\msi-uninstall.log +``` + +## Crash Evidence + +- Event Viewer -> Windows Logs -> Application: `.NET Runtime`, `Application Error`, and `MsiInstaller`. +- Crash dumps: `%LOCALAPPDATA%\CrashDumps\` for bundle, custom BA, or `msiexec.exe` crashes. +- FieldWorks debug traces can be enabled with `FieldWorks.Diagnostics.config` as described in `.github/instructions/debugging.instructions.md`. diff --git a/.github/skills/fieldworks-installer-diagnostics/references/log-triage.md b/.github/skills/fieldworks-installer-diagnostics/references/log-triage.md new file mode 100644 index 0000000000..24ef13ac86 --- /dev/null +++ b/.github/skills/fieldworks-installer-diagnostics/references/log-triage.md @@ -0,0 +1,69 @@ +# Installer Log Triage + +## Burn Bundle Log + +Read in phase order: + +1. Header/version/command line. +2. Detect phase: `Detected package`, related bundle detection, registry searches, prerequisite state. +3. Plan phase: `Planned package`, requested action, cache strategy, dependency registration, feature states. +4. Cache/download phase: URL, payload hash/signature, cache path. +5. Apply phase: `Applying execute package`, package arguments, package-specific log path. +6. Completion: package result, restart state, final HRESULT. + +Useful strings: + +- `WixBundleLog` +- `WixBundleLog_AppMsiPackage` +- `Detected package:` +- `Planned package:` +- `Applying execute package:` +- `Error 0x` +- `restart:` +- `Will not uninstall package` +- `found dependents` + +## MSI Verbose Log + +Read around the first real failure, not the final summary only. + +Useful strings: + +- `Return value 3` +- `CustomAction` +- `Action start` +- `Action ended` +- `APPFOLDER` +- `DATAFOLDER` +- `WIX_UPGRADE_DETECTED` +- `FindRelatedProducts` +- `RemoveExistingProducts` +- `InstallValidate` +- `PATCH` +- `PATCHNEWSUMMARYSUBJECT` + +When `Return value 3` appears, walk upward to find the custom action or standard action that failed, then inspect properties and command line immediately before it. + +## Custom Actions + +Map WiX action ID to DLL entry: + +- `CheckApplicationPath` -> `CheckAppPath` +- `VerifyDataPath` -> `VerifyDataDirPath` +- `CloseApplications` -> `ClosePrompt` +- `DeleteRegistryVersionNumber` -> `DeleteVersionNumberFromRegistry` + +Common causes: + +- Required session properties not initialized before UI sequence. +- Deferred action needs `CustomActionData` but reads session properties directly. +- Missing `CustomActions.CA.dll` or runtime config in the Binary payload. +- Process-close prompt waiting for user action. + +## Symptom Shortcuts + +- Double-click does nothing: run with `/log`; check Burn condition failures, BA/theme load failure, Event Viewer, crash dumps. +- Bundle waits after detect: interactive bundle may be waiting for user action. Use `/passive` or full UI intentionally. +- MSI UI missing from bundle: inspect `bal:DisplayInternalUICondition`, UI level, and package log. +- Uninstall hangs: find whether Burn, MSI, files-in-use, CloseApplications, or ARP invocation is waiting. +- Duplicate ARP entries: inventory bundle vs MSI registrations and check package visibility/ARP properties. diff --git a/.github/skills/fieldworks-wix6-build-migration/SKILL.md b/.github/skills/fieldworks-wix6-build-migration/SKILL.md new file mode 100644 index 0000000000..5a47d3a8af --- /dev/null +++ b/.github/skills/fieldworks-wix6-build-migration/SKILL.md @@ -0,0 +1,71 @@ +--- +name: fieldworks-wix6-build-migration +description: Use this skill for FieldWorks WiX Toolset build migration work: moving PatchableInstaller/genericinstaller behavior into WiX 6, fixing Build/Installer*.targets, SDK-style .wixproj files, InstallerToolset selection, prerequisite download/staging, CI installer builds, signing knobs, artifact paths, and local developer build failures. Trigger whenever the user mentions FieldWorks WiX build, Wix3/Wix6 toolset selection, PatchableInstaller migration, genericinstaller removal, or installer artifacts. +--- + +# FieldWorks WiX 6 Build Migration + +This skill handles build infrastructure and migration mechanics. Keep it separate from UI behavior, runtime diagnostics, and patch/upgrade validation unless those areas are the cause of a build failure. + +## Load References When Needed + +- Read `references/repo-build-map.md` for FieldWorks-specific targets, projects, commands, and artifact paths. +- Read `references/official-wix-build-notes.md` for WiX Toolset v4/v6 migration and MSBuild facts. + +## First Moves + +1. Confirm current branch/worktree and whether the task is about the WiX 3 fallback, the WiX 6 migration path, or the switch to WiX 6 as default. +2. Read `FLExInstaller/AGENTS.md`, `.github/instructions/installer.instructions.md`, and `.github/instructions/build.instructions.md`. +3. Inspect `Build/InstallerBuild.proj` to confirm which target file is imported for `InstallerToolset=Wix3|Wix6`. +4. Inspect `Build/Installer.targets`, `Build/Installer.Wix3.targets`, and relevant `.wixproj` files before editing. +5. Search for active references to `PatchableInstaller`, `genericinstaller`, `candle.exe`, `light.exe`, `insignia.exe`, and `heat.exe`; classify each as WiX 3 legacy, WiX 6 active, or documentation/reference. + +## FieldWorks Build Rules + +- Current default installer build is WiX 3 fallback: `./build.ps1 -BuildInstaller`. +- WiX 6 is currently selected explicitly: `./build.ps1 -BuildInstaller -InstallerToolset Wix6`; treat that path as the migration target. +- Do not make WiX 6 schema changes in `FLExInstaller/` root files. Keep WiX 6 authoring under `FLExInstaller/wix6/`. +- Do not reintroduce a `genericinstaller` submodule. If old behavior is needed, migrate the specific source/config into the repo and document why. +- Do not remove or break the WiX 3 path during the transition. +- Use pinned repo package versions, not whatever WiX happens to be globally installed. +- WiX 6 projects should be SDK-style and use WiX extension `PackageReference`s such as Bal, Util, NetFx, UI, and Heat as appropriate. +- WiX v6 source uses `http://wixtoolset.org/schemas/v4/wxs`; there is no v6 schema namespace. + +## PatchableInstaller Migration Workflow + +When moving behavior out of the old generic installer/PatchableInstaller model: + +1. Identify the old source of behavior in `PatchableInstaller/`, `Build/Installer.legacy.targets`, or WiX 3 includes. +2. Find the WiX 6 destination: `FLExInstaller/wix6/Shared/Base`, `Shared/Common`, `Shared/CustomActions`, `Shared/ProcRunner`, or a `.wixproj` target. +3. Port only the needed behavior. Avoid copying whole legacy scripts or batch-driven assumptions into the WiX 6 path. +4. Convert old command-line parameters into MSBuild properties and `DefineConstants` rather than batch variables. +5. Add validation that fails loudly when required payloads or prerequisites are missing. +6. Update specs/docs only when behavior or build commands change. + +## Build Failure Workflow + +1. Run or ask for the exact command and configuration, including `InstallerToolset`. +2. Validate prerequisites with `./Build/Agent/Setup-InstallerBuild.ps1 -ValidateOnly` when the failure smells environmental. +3. If WiX 6 fails, inspect `FieldWorks.Installer.wixproj`, `FieldWorks.Bundle.wixproj`, and `FieldWorks.OfflineBundle.wixproj` before touching MSBuild targets. +4. For WiX errors, map the error to authoring/project/tooling. Do not hide warnings by suppression unless the repo already documents that suppression. +5. Preserve `.wixpdb` outputs; they are useful for diagnostics and future patch baselines. + +## Validation + +Prefer these checks, scaled to the change: + +- `./Build/Agent/Setup-InstallerBuild.ps1 -ValidateOnly` +- `./build.ps1 -BuildInstaller -InstallerToolset Wix6 -Configuration Debug` +- `./build.ps1 -BuildInstaller -InstallerToolset Wix6 -Configuration Release` for release-path changes +- Confirm artifacts under `FLExInstaller/wix6/bin/x64//` and `en-US/`. +- For CI or pre-commit work, use the repo VS Code task `CI: Full local check` when appropriate. + +## Output Expectations + +When reporting a build migration result, include: + +- The active toolset and entry point. +- The target/project files touched. +- The legacy behavior replaced or isolated. +- The artifacts expected and where they land. +- The validation run and remaining risk. diff --git a/.github/skills/fieldworks-wix6-build-migration/references/official-wix-build-notes.md b/.github/skills/fieldworks-wix6-build-migration/references/official-wix-build-notes.md new file mode 100644 index 0000000000..7339f6bc78 --- /dev/null +++ b/.github/skills/fieldworks-wix6-build-migration/references/official-wix-build-notes.md @@ -0,0 +1,32 @@ +# Official WiX Build And Migration Notes + +Use these notes for WiX Toolset, not the Wix website builder. + +## Official Sources + +- WiX source: https://github.com/wixtoolset/wix +- FireGiant docs: https://docs.firegiant.com/wix/ +- v3 to v4 migration WIP: https://docs.firegiant.com/wix/development/wips/4561-migrate-v3-source-code-to-v4/ +- MSBuild SDK docs: https://docs.firegiant.com/wix/tools/msbuild/ +- Preprocessor docs: https://docs.firegiant.com/wix/tools/preprocessor/ +- Extension docs: https://docs.firegiant.com/wix/tools/wixext/ + +## Migration Facts To Remember + +- WiX v6 authoring still uses the v4 XML namespace. +- v3 `candle.exe` and `light.exe` thinking maps to `wix build` or SDK-style MSBuild. +- SDK-style `.wixproj` files can use `ProjectReference`s; WiX creates bind paths and preprocessor variables for referenced projects. +- Extensions are NuGet packages in modern WiX, not only `-ext` paths to globally installed DLLs. +- Useful MSBuild properties include `DefineConstants`, `BindPath`, `InstallerPlatform`, `OutputType`, `VerboseOutput`, `SuppressIces`, `SuppressValidation`, and `*AdditionalOptions`. +- `Product` became `Package`; old `Package` metadata moved/reorganized. +- `Component/@Win64` became `Bitness`; default bitness follows `InstallerPlatform`/`-arch`. +- `RemotePayload` became package-specific payload elements such as `ExePackagePayload`, `MsiPackagePayload`, and `MspPackagePayload`. +- `ExePackage` command attributes became `InstallArguments`, `RepairArguments`, and `UninstallArguments`. +- `DisplayInternalUI` moved out of core Burn package authoring; use Bal `DisplayInternalUICondition` when appropriate. + +## Build Quality Rules + +- Do not suppress validation to get past real authoring problems. +- Keep warnings visible unless the repo has an intentional suppression with a reason. +- Preserve `.wixpdb` outputs for diagnostics and patch baselines. +- Treat Heat as technical debt. If using generated fragments, guard component/file identity carefully. diff --git a/.github/skills/fieldworks-wix6-build-migration/references/repo-build-map.md b/.github/skills/fieldworks-wix6-build-migration/references/repo-build-map.md new file mode 100644 index 0000000000..c5e774734d --- /dev/null +++ b/.github/skills/fieldworks-wix6-build-migration/references/repo-build-map.md @@ -0,0 +1,60 @@ +# FieldWorks Installer Build Map + +## Core Files + +- `build.ps1`: top-level wrapper. Installer builds are requested with `-BuildInstaller`; patch builds with `-BuildPatch`; WiX 6 is selected with `-InstallerToolset Wix6`. +- `Build/InstallerBuild.proj`: central installer MSBuild project. Defaults `InstallerToolset` to `Wix3`; imports `Installer.Wix3.targets` or `Installer.targets`. +- `Build/Installer.Wix3.targets`: WiX 3 fallback route. Verifies legacy inputs and delegates to legacy genericinstaller/PatchableInstaller scripts when that external checkout is present. +- `Build/Installer.targets`: WiX 6 route. Stages payloads and builds `FieldWorks.Bundle.wixproj` plus `FieldWorks.OfflineBundle.wixproj`. +- `Build/Installer.legacy.targets`: historical WiX 3 reference. Use for understanding old patch/build behavior, not as a WiX 6 implementation pattern. + +## WiX 6 Project Files + +- `FLExInstaller/wix6/FieldWorks.Installer.wixproj`: MSI project; builds the main package from staged inputs and custom action output. +- `FLExInstaller/wix6/FieldWorks.Bundle.wixproj`: online Burn bundle; downloads/stages prerequisite payloads and chains the MSI. +- `FLExInstaller/wix6/FieldWorks.OfflineBundle.wixproj`: offline Burn bundle; embeds repo-local prerequisite payloads from `FLExInstaller/wix6/libs`. +- `FLExInstaller/wix6/Shared/CustomActions/CustomActions/CustomActions.csproj`: WiX DTF custom action project. +- `FLExInstaller/wix6/Shared/ProcRunner/`: helper executable source used by installer custom behavior. + +## Commands + +Preferred local commands: + +```powershell +./Build/Agent/Setup-InstallerBuild.ps1 -ValidateOnly +./build.ps1 -BuildInstaller +./build.ps1 -BuildInstaller -InstallerToolset Wix6 +./build.ps1 -BuildInstaller -Configuration Release -InstallerToolset Wix6 +``` + +Use direct MSBuild only when debugging build infrastructure: + +```powershell +msbuild Build/InstallerBuild.proj /t:BuildInstaller /p:Configuration=Debug /p:Platform=x64 /p:InstallerToolset=Wix6 +``` + +Patch caution: `build.ps1 -BuildPatch` routes to `/t:BuildPatch`. Before promising WiX 6 patch support, inspect whether the WiX 6 import actually defines the target and whether `.msp` authoring exists. + +## Artifacts + +WiX 3 fallback/current default: + +- `FLExInstaller/bin/x64//en-US/FieldWorks.msi` +- `FLExInstaller/bin/x64//FieldWorksBundle.exe` + +WiX 6 migration path: + +- `FLExInstaller/wix6/bin/x64//en-US/FieldWorks.msi` +- `FLExInstaller/wix6/bin/x64//en-US/FieldWorks.wixpdb` +- `FLExInstaller/wix6/bin/x64//FieldWorksBundle.exe` +- `FLExInstaller/wix6/bin/x64//FieldWorksBundle.wixpdb` +- `FLExInstaller/wix6/bin/x64//FieldWorksOfflineBundle.exe` when the offline project builds successfully. + +## Hidden-Dependency Checks + +- Staged payload root: `BuildDir/FieldWorks_InstallerInput__/`. +- Localization staging depends on `Output///` existing. +- Bundle theme files are staged by flat filename into the culture output folder. +- Offline prerequisites depend on files in `FLExInstaller/wix6/libs`. +- Heat/harvesting changes can break component identity and future patching. +- Custom action entry points referenced in `Framework.wxs` must match methods in `CustomAction.cs`. diff --git a/.github/skills/fieldworks-wix6-migration-coordinator/SKILL.md b/.github/skills/fieldworks-wix6-migration-coordinator/SKILL.md new file mode 100644 index 0000000000..475dfc0492 --- /dev/null +++ b/.github/skills/fieldworks-wix6-migration-coordinator/SKILL.md @@ -0,0 +1,58 @@ +--- +name: fieldworks-wix6-migration-coordinator +description: Use this skill whenever the user mentions FieldWorks, FLEx, WiX installer migration, WiX 3, WiX 6, PatchableInstaller, genericinstaller, Burn, MSI bundles, installer UI, installer patching, or installer diagnostics in the FieldWorks repo. Always use it for broad FieldWorks WiX Toolset migration questions, even if the user only says "wix"; do not use Wix.com website-builder sources. +--- + +# FieldWorks WiX 6 Migration Coordinator + +Use this as the routing and guardrail skill for FieldWorks installer migration work. It keeps broad migration requests from becoming one undifferentiated installer soup. + +## First Moves + +1. Confirm the repo root by finding `FieldWorks.sln`, `build.ps1`, and `FLExInstaller/`. +2. Read the active repo guidance before edits: `AGENTS.md`, `FLExInstaller/AGENTS.md`, `.github/instructions/installer.instructions.md`, `.github/instructions/navigation.instructions.md`, and `.github/instructions/terminal.instructions.md`. +3. Read the current migration state: `specs/001-wix-v6-migration/spec.md`, `tasks.md`, `quickstart.md`, `wix3-to-wix6-audit.md`, `REMAINING_WIX6_ISSUES.md`, `BUNDLE_UI.md`, `verification-matrix.md`, and `golden-install-checklist.md`. +4. Treat all web research as WiX Toolset research. Avoid Wix.com/site-builder material entirely. + +## Pick The Right Specialist + +- Use `fieldworks-wix6-build-migration` for build orchestration, toolset selection, MSBuild targets, `.wixproj` changes, prerequisite downloads, CI checks, and moving old PatchableInstaller/genericinstaller build logic into the WiX 6 path. +- Use `fieldworks-wix6-ui` for bundle/MSI GUI problems, blank or misrendered UI, WixStdBA theme assets, MSI internal UI, dual-directory selection, feature-tree dialogs, and UI screenshot/evidence work. +- Use `fieldworks-wix6-upgrade-patching` for installing WiX 6 over WiX 3, single-instance guarantees, ARP duplication, uninstall/repair compatibility, base builds, MSP patches, `.wixpdb` baselines, and component/product-code stability. +- Use `fieldworks-installer-diagnostics` for runtime failures, "double-click does nothing", uninstall hangs, custom-action failures, evidence folders, Burn/MSI log triage, Event Viewer, crash dumps, and snapshot comparisons. + +## Non-Negotiable Context + +- FieldWorks is in a transition period: the repo currently defaults installer builds to WiX 3, but the migration target is WiX 6-first and ultimately WiX 6 as default. +- WiX 3 authoring lives in `FLExInstaller/` and legacy jobs may still checkout `sillsdev/genericinstaller` as `PatchableInstaller/`; `PatchableInstaller/` is not restored in this worktree. +- WiX 6 authoring lives under `FLExInstaller/wix6/` and should not reuse WiX 3 `.wxi` files. +- The genericinstaller submodule should remain removed. Do not reintroduce a submodule checkout as a solution. +- WiX 6 authoring still uses the v4 XML namespace: `http://wixtoolset.org/schemas/v4/wxs`. +- Prefer `./build.ps1` and `./test.ps1` over ad-hoc `msbuild` or direct tool invocations unless debugging build infrastructure. +- Installer changes often cross build targets, WiX XML, custom actions, Burn behavior, registry state, and VM evidence. Use structural navigation for hidden dependencies before editing. + +## Safe Work Pattern + +1. State which specialist area applies and why. +2. Gather evidence from repo files and current logs before proposing changes. +3. Keep WiX 3 and WiX 6 paths isolated unless the task explicitly concerns compatibility between them. +4. Make the smallest change that addresses the root cause. +5. Validate with the relevant build/test/evidence checklist, or clearly report why validation could not be run. + +## WinApp MCP UI Automation + +When the WinApp MCP server is installed, prefer it for Windows installer UI proof before falling back to raw screenshots or manual notes. + +- Use WinApp app/window tools to launch or attach to `FieldWorksBundle.exe`, `FieldWorksOfflineBundle.exe`, or `msiexec.exe` and collect UI snapshots where available. +- WixStdBA may expose little or no UI Automation tree at the launcher PID. If the app snapshot is empty, inspect visible desktop windows, use the focused element, and drive the UI with keyboard navigation (`Tab`, `Shift+Tab`, `Space`, `Return`, `Esc`) or coordinates. +- For safe click-through smoke tests, prove focus and interaction by toggling the license checkbox, reaching the Install button, then canceling and confirming exit. Do not click Install unless the task explicitly calls for an install/upgrade/uninstall run on that machine. +- Always pair WinApp UI evidence with bundle/MSI logs in `Output/InstallerEvidence//` or the VM evidence folder. + +## Source Credibility + +Prefer sources in this order: + +1. FieldWorks repo docs, specs, and current code. +2. Official WiX Toolset and FireGiant docs. +3. WiX maintainer posts, WiX GitHub issues/discussions, and WiX source/tests. +4. Stack Overflow/blog posts only as practical hints that must be converted into repo-specific tests. diff --git a/.github/skills/fieldworks-wix6-ui/SKILL.md b/.github/skills/fieldworks-wix6-ui/SKILL.md new file mode 100644 index 0000000000..25caeeeb24 --- /dev/null +++ b/.github/skills/fieldworks-wix6-ui/SKILL.md @@ -0,0 +1,81 @@ +--- +name: fieldworks-wix6-ui +description: Use this skill for FieldWorks WiX 6 installer GUI work: bundle window does not display, MSI internal UI handoff, WixStdBA theme/layout/assets, dual-directory App + Project Data selection, feature-tree dialogs, full/passive/quiet UI behavior, screenshots, and bundle/MSI UI parity with the old WiX 3 installer. Trigger strongly for any FieldWorks installer UI, GUI, dialog, theme, UX, or DisplayInternalUICondition task. +--- + +# FieldWorks WiX 6 UI Skill + +This skill keeps FieldWorks installer UI work grounded in the actual Burn + MSI architecture. The goal is not a pretty standalone screen; it is a correct installer flow with evidence. + +## Load References When Needed + +- Read `references/ui-repo-map.md` for the FieldWorks UI file map and checks. +- Read `references/official-ui-notes.md` for WiX Toolset UI/Burn facts. + +## First Moves + +1. Read `specs/001-wix-v6-migration/BUNDLE_UI.md` and `verification-matrix.md`. +2. Inspect `FLExInstaller/wix6/Shared/Base/Bundle.wxs`, `OfflineBundle.wxs`, `BundleTheme.xml`, `BundleTheme.wxl`, and `FieldWorks.Bundle.wixproj` for bundle UI behavior. +3. Inspect `Framework.wxs`, `WixUI_DialogFlow.wxs`, `GIInstallDirDlg.wxs`, and `GICustomizeDlg.wxs` for MSI UI behavior. +4. If custom actions affect UI, inspect `Shared/CustomActions/CustomActions/CustomAction.cs` and the custom action IDs in `Framework.wxs`. +5. Gather screenshots/logs before changing visual or sequencing behavior. If WinApp MCP is available, use it to drive the visible installer UI and record the controls/states observed. + +## Mental Model + +- FieldWorks uses Burn/WixStdBA as a prerequisite/bootstrapper shell and the MSI UI for the real FieldWorks choices. +- The expected interactive flow is usually: bundle welcome/license -> prerequisite progress -> MSI internal UI opens for directories/features -> bundle success/failure. +- Seeing two windows during the MSI handoff can be correct: WixStdBA window behind, Windows Installer UI in front. +- Bundle Options UI is intentionally suppressed so MSI owns app/data directories and feature selection. +- Full UI should show MSI internal UI. `/quiet` must not show UI; `/passive` should remain non-interactive/minimal. + +## FieldWorks UI Anchors + +- `Bundle.wxs`: `bal:WixStandardBootstrapperApplication`, theme payloads, `SuppressOptionsUI`, `MsiPackage`, `bal:DisplayInternalUICondition`, `LogPathVariable`, `Visible`. +- `FieldWorks.Bundle.wixproj`: `StageBundlePayloads` and flat filename staging for theme assets. +- `Framework.wxs`: `MajorUpgrade`, `WIXUI_INSTALLDIR=APPFOLDER`, `WIXUI_PROJECTSDIR=DATAFOLDER`, data/app folder registry searches, custom actions, UIRefs. +- `WixUI_DialogFlow.wxs`: dialog transitions, browse events, path validation, maintenance/update path. +- `GIInstallDirDlg.wxs`: dual directory controls and data-folder lock explanation. +- `GICustomizeDlg.wxs`: feature tree selection UI. +- `CustomFeatures.wxi` and `CustomComponents.wxi`: feature/component definitions that must match the UI tree. + +## Common UI Failure Modes + +- Bundle appears blank or exits: missing theme payload, BA load failure, condition failure, wrong architecture, or no visible full UI. +- MSI UI never appears from bundle: `bal:DisplayInternalUICondition` not authored, condition false, wrong namespace/extension, or non-full UI mode. +- MSI UI appears in `/quiet` or `/passive`: condition too broad. +- Directory fields do not behave: `WIXUI_INSTALLDIR`, `WIXUI_PROJECTSDIR`, `APPFOLDER`, `DATAFOLDER`, or registry search values are not initialized when the dialog appears. +- Feature tree is wrong: feature IDs, levels, component refs, or feature-group wiring drifted from `CustomFeatures.wxi`/`CustomComponents.wxi`. +- ARP or uninstall UI path is odd: bundle/MSI visibility and maintenance behavior are interacting; use the upgrade/patching skill too. + +## UI Change Workflow + +1. Identify whether the issue is bundle UI, MSI UI, or handoff between them. +2. Prove the current behavior with screenshots and a bundle log. For MSI UI issues, capture an MSI verbose log too. +3. Make one class of change at a time: theme asset/staging, bundle BA metadata, MSI dialog wiring, custom action/property initialization, or feature authoring. +4. Preserve localization: put translatable strings in existing `.wxl`/`.resx` patterns, not hardcoded source where localization is expected. +5. Validate full, passive, and quiet modes when changing `DisplayInternalUICondition` or dialog sequencing. + +## WinApp MCP Click-Through Workflow + +Use WinApp MCP for bundle/MSI UI smoke tests whenever the server is available. + +1. Launch the bundle with an explicit log path, for example `FieldWorksBundle.exe /log Output\InstallerEvidence\\bundle.log`. +2. Wait for input idle, then request a UI snapshot. If the app-root snapshot has no accessible elements, list desktop windows and use the focused element/keyboard navigation. +3. Safe bundle smoke test: focus Cancel, Tab/Shift+Tab through `license terms`, `I agree to the license terms.`, `Install`, and `Cancel`; press Space on the license checkbox; verify focus can reach `Install`; then Cancel and confirm exit. +4. For install-intent tests, click `Install` only when the machine/VM is prepared for an install run and evidence collection is in place. +5. If the MSI internal UI opens, attach to or inspect the visible `msiexec` window and capture the Destination Folders and feature-selection screens. + +Known behavior: the Burn launcher PID may not own the visible WixStdBA window after startup. If `get_snapshot` returns no accessible elements, use desktop window discovery, `get_focused_element`, keyboard commands, or coordinate clicks rather than concluding the UI is untestable. + +Elevation boundary: a non-elevated VS Code/WinApp MCP host can usually drive the bundle UI before elevation, but may only observe the elevated MSI internal UI. If the MSI welcome dialog shows focus on `&Next` but UIA click, keyboard, coordinate click, and direct button messages do not advance it, rerun with an elevated automation host or switch to quiet install/uninstall evidence for completion. + +## Evidence Checklist + +- Bundle command and UI level used. +- WinApp MCP app/window IDs or visible window title/PID used for automation. +- Focus/click path used to reach key controls when UI Automation does not expose a full element tree. +- Screenshot of bundle welcome/license screen. +- Screenshot of MSI destination folders dialog. +- Screenshot of feature selection dialog if touched. +- Bundle log path and `WixBundleLog_AppMsiPackage` MSI log path. +- Confirmation that `/quiet` and `/passive` do not show unexpected MSI UI. diff --git a/.github/skills/fieldworks-wix6-ui/references/official-ui-notes.md b/.github/skills/fieldworks-wix6-ui/references/official-ui-notes.md new file mode 100644 index 0000000000..27ae89c145 --- /dev/null +++ b/.github/skills/fieldworks-wix6-ui/references/official-ui-notes.md @@ -0,0 +1,28 @@ +# Official WiX UI And Burn Notes + +Use these for WiX Toolset, not Wix.com. + +## Important Sources + +- MsiPackage schema: https://docs.firegiant.com/wix/schema/wxs/msipackage/ +- Bundle schema: https://docs.firegiant.com/wix/schema/wxs/bundle/ +- Bal extension source/tests: https://github.com/wixtoolset/wix/tree/main/src/ext/Bal +- Built-in BA migration post: https://rseanhall.com/blog/2021/06/06/v4-breaking-changes-ref-builtin-ba/ +- Bundle architecture post: https://rseanhall.com/blog/2021/06/08/v4-breaking-changes-bundles-respect-arch/ + +## Facts To Encode In UI Work + +- `bal:DisplayInternalUICondition` controls whether Burn shows the authored MSI UI. It is from the Bal extension namespace. +- `DisplayInternalUI` as a core `MsiPackage` attribute is not the FieldWorks WiX 6 approach. +- When internal MSI UI is shown, it appears on top of the bootstrapper UI; it is not embedded in WixStdBA. +- WixStdBA does not support EmbeddedUI. +- `MsiPackage/@Visible` controls whether the MSI appears in Programs and Features. The default is no, and FieldWorks currently authors `Visible="no"`. +- `MsiPackage/@LogPathVariable` defaults to `WixBundleLog_[PackageId]`; FieldWorks uses `WixBundleLog_AppMsiPackage` explicitly. +- WiX 4+ bundles respect architecture. In an x64-only product, review registry searches, BA payload architecture, and old Win64 assumptions. +- Theme assets must be present in the BA container/output using the exact names referenced by the theme. + +## Debugging Hints From WiX Source + +- WixStdBA creates its UI on a UI thread and reports theme/window creation failures to Burn logs. +- WixStdBA evaluates `DisplayInternalUICondition` during MSI planning and only sets MSI UI level when the condition is true. +- WixInternalUIBootstrapperApplication is a different BA with different warnings and behavior; do not assume WixStdBA and IUIBA behave the same. diff --git a/.github/skills/fieldworks-wix6-ui/references/ui-repo-map.md b/.github/skills/fieldworks-wix6-ui/references/ui-repo-map.md new file mode 100644 index 0000000000..40c6a07d31 --- /dev/null +++ b/.github/skills/fieldworks-wix6-ui/references/ui-repo-map.md @@ -0,0 +1,34 @@ +# FieldWorks UI Repo Map + +## Bundle UI + +- `FLExInstaller/wix6/Shared/Base/Bundle.wxs`: online bundle. Uses WixStdBA, custom theme payloads, `SuppressOptionsUI="yes"`, and `bal:DisplayInternalUICondition="WixBundleUILevel >= 4"` on `AppMsiPackage`. +- `FLExInstaller/wix6/Shared/Base/OfflineBundle.wxs`: offline bundle equivalent. Keep UI behavior in sync unless intentionally different. +- `FLExInstaller/wix6/Shared/Base/BundleTheme.xml`: WixStdBA theme layout. +- `FLExInstaller/wix6/Shared/Base/BundleTheme.wxl`: WixStdBA theme strings. +- `FLExInstaller/wix6/FieldWorks.Bundle.wixproj`: stages `BundleTheme.xml`, `.wxl`, `fw-logo.png`, background assets, and `License.htm` into the culture output folder by flat filename. + +## MSI UI + +- `FLExInstaller/wix6/Shared/Base/Framework.wxs`: package root, properties, custom actions, `WIXUI_INSTALLDIR`, `WIXUI_PROJECTSDIR`, UI refs, app/data folder registry searches. +- `FLExInstaller/wix6/Shared/Base/WixUI_DialogFlow.wxs`: custom dialog navigation. +- `FLExInstaller/wix6/Shared/Base/GIWelcomeDlg.wxs`: welcome/update entry. +- `FLExInstaller/wix6/Shared/Base/GISetupTypeDlg.wxs`: typical/custom/complete route. +- `FLExInstaller/wix6/Shared/Base/GIInstallDirDlg.wxs`: app folder and project data folder controls. +- `FLExInstaller/wix6/Shared/Base/GICustomizeDlg.wxs`: feature selection tree. +- `FLExInstaller/wix6/Shared/Base/GIProgressDlg.wxs`: progress and patch/update text. +- `FLExInstaller/wix6/Shared/Base/WixUI_en-us.wxl`: MSI UI strings. + +## Feature Tree Inputs + +- `FLExInstaller/wix6/Shared/Common/CustomFeatures.wxi`: features and feature levels. +- `FLExInstaller/wix6/Shared/Common/CustomComponents.wxi`: component groups, shortcuts, environment variables, URL protocol registration. +- Harvested component groups from the WiX 6 build must match feature refs. + +## High-Value Checks + +- `Bundle.wxs` should hide the MSI in ARP with `Visible="no"` unless intentionally changing ARP behavior. +- `LogPathVariable="WixBundleLog_AppMsiPackage"` should stay available for MSI log discovery. +- `WixStdBASuppressOptionsUI`/`SuppressOptionsUI` keeps bundle Options UI from competing with the MSI directory UI. +- `GIInstallDirDlg` uses indirect PathEdit controls. Make sure the properties point to directory IDs and are initialized before the dialog opens. +- The data folder should lock on upgrade only when the registry/search state proves an existing data folder. diff --git a/.github/skills/fieldworks-wix6-upgrade-patching/SKILL.md b/.github/skills/fieldworks-wix6-upgrade-patching/SKILL.md new file mode 100644 index 0000000000..216b441ed6 --- /dev/null +++ b/.github/skills/fieldworks-wix6-upgrade-patching/SKILL.md @@ -0,0 +1,99 @@ +--- +name: fieldworks-wix6-upgrade-patching +description: Use this skill for FieldWorks installer upgrade and patch work: installing WiX 6 over prior WiX 3 builds, single-instance guarantees, MajorUpgrade behavior, ARP duplicate entries, uninstall/repair after upgrade, BuildPatch, base build artifacts, MSP creation, PatchBaseline, .wixpdb baselines, component GUID/file ID stability, and replacing PatchableInstaller patch infrastructure. Trigger whenever the user mentions upgrade, patch, MSP, base build, ARP, repair, uninstall compatibility, ProductCode, UpgradeCode, or Burn provider keys. +--- + +# FieldWorks WiX 6 Upgrade And Patching + +This skill handles the part of the migration most likely to hurt later: compatibility with existing installs and the discipline needed for future patches. + +## Load References When Needed + +- Read `references/upgrade-patch-checklist.md` for FieldWorks-specific gates. +- Read `references/official-external-upgrade-notes.md` for WiX/Windows Installer guidance and external lessons. + +## First Moves + +1. Read `specs/001-wix-v6-migration/REMAINING_WIX6_ISSUES.md`, `verification-matrix.md`, `golden-install-checklist.md`, and `parity-check.md`. +2. Inspect `Framework.wxs` for `MajorUpgrade`, `UpgradeCode`, path registry searches, `WIX_UPGRADE_DETECTED`, and `AllowSameVersionUpgrades`. +3. Inspect `Bundle.wxs`/`OfflineBundle.wxs` for bundle identity, `RelatedBundle`, `MsiPackage`, `Visible`, `ProviderKey`, and any `Provides` children. +4. Inspect `Build/InstallerBuild.proj`, `Build/Installer.targets`, and `Build/Installer.Wix3.targets` before assuming patch targets exist for WiX 6. +5. For actual failures, use the diagnostics skill to collect bundle/MSI logs and ARP/registry snapshots first. + +## Hard Requirements + +- FieldWorks must be single-instance. WiX 6 must not allow side-by-side FieldWorks installs from any previous WiX 3 or WiX 6 generation. +- Upgrade must preserve user data and settings while replacing the old install. +- WiX 3 remains the current default build during transition, but the migration target is WiX 6-first. Do not break the fallback while building WiX 6 upgrade behavior, and do not treat the current default as the desired final state. +- Major upgrades are the safer default for broad installer changes. MSP patches require stricter component-rule discipline. + +## WiX 3 To WiX 6 Upgrade Matrix + +Validate these scenarios before declaring upgrade support complete: + +- Clean WiX 6 bundle install. +- Prior WiX 3 MSI -> WiX 6 MSI. +- Prior WiX 3 bundle -> WiX 6 bundle. +- WiX 6 repair after WiX 3-to-WiX 6 upgrade. +- WiX 6 uninstall after WiX 3-to-WiX 6 upgrade. +- Downgrade attempt from newer install. +- Interrupted upgrade rollback. +- Same-version dev build replacement if `AllowSameVersionUpgrades` is in play. + +Capture bundle logs, MSI logs, ARP snapshots, path registry exports, and before/after install/data folder listings. + +## Burn Provider Compatibility + +Public WiX issue history shows v3 and v4+ bundles can disagree about package dependency/provider keys. For upgrades from a WiX 3 bundle to WiX 6: + +- Review whether FieldWorks needs explicit provider compatibility. +- Consider whether a package-level `` matching the old MSI provider/product identity is needed. +- Test repair/uninstall after adding any provider workaround; it can fix upgrade but expose repair behavior. +- Do not guess ProductCode/ProviderKey values. Extract them from the baseline artifact or registry evidence. + +## ARP Duplication Workflow + +When Programs and Features shows duplicate or strange FieldWorks entries: + +1. Inventory bundle entries and MSI entries separately. +2. Check `Bundle` identity/name/version/upgrade provider behavior. +3. Check `MsiPackage Visible`, MSI `ARPSYSTEMCOMPONENT`, `ARPNOMODIFY`, `ARPNOREMOVE`, and bundle `DisableModify`/`DisableRemove`. +4. Use Windows Installer product inventory and registry snapshots to identify which product/package registered each ARP row. +5. Verify uninstall from ARP does not hang and removes the expected entry only. + +## WinApp MCP For Upgrade/Uninstall Runs + +Use WinApp MCP to observe and drive UI during upgrade, repair, and uninstall evidence runs: + +- Attach to the visible bundle/MSI/ARP window and capture the title, PID, focused element, and any prompt text before clicking. +- For ARP uninstall hangs, use WinApp to identify whether focus is on a hidden prompt, MSI dialog, bundle confirmation, or UAC-adjacent window before changing authoring. +- During WiX 3 to WiX 6 upgrade tests, use WinApp screenshots/focus traces for the bundle welcome/license page, MSI destination folders page, feature tree, progress, completion, and any maintenance prompt. +- Do not use WinApp to bypass prompts silently. The point is to document and reproduce the user-visible path while bundle/MSI logs and snapshots capture the underlying state. +- Run the WinApp automation host elevated for full manual upgrade or uninstall UI once Burn hands off to MSI internal UI. A non-elevated host can observe the elevated MSI dialog but may not be able to click `Next`, `Repair`, `Uninstall`, or ARP prompts. + +## MSP Patch Gate + +Before creating a WiX 6 MSP path, classify the change: + +- Safe-ish for patch: changed file contents with stable component GUIDs/file IDs and unchanged component ownership. +- Risky for patch: removed files, renamed files, moved directories, changed component GUIDs, changed key paths, feature tree reshaping, or harvested fragment identity churn. +- Prefer a major upgrade when component rules are uncertain. + +Patch infrastructure must preserve: + +- Target and update `.msi` files. +- Target and update `.wixpdb` files. +- Source payload directories or enough `.msi` information for extraction. +- Bind paths for target/update payloads. +- Generated fragments and ID maps. +- Build logs and installer version metadata. + +## Output Expectations + +For upgrade/patch work, report: + +- Baseline artifact and update artifact identities. +- UpgradeCode, ProductCode, package identity, and first-three-field version relationship. +- Whether the result is a major upgrade, repair, uninstall, or MSP patch path. +- Evidence captured and any matrix rows proven. +- Residual risk around Burn provider compatibility or component-rule stability. diff --git a/.github/skills/fieldworks-wix6-upgrade-patching/references/official-external-upgrade-notes.md b/.github/skills/fieldworks-wix6-upgrade-patching/references/official-external-upgrade-notes.md new file mode 100644 index 0000000000..607e4a5e13 --- /dev/null +++ b/.github/skills/fieldworks-wix6-upgrade-patching/references/official-external-upgrade-notes.md @@ -0,0 +1,34 @@ +# Official And External Upgrade/Patch Notes + +Use these for WiX Toolset and Windows Installer only. + +## Official Sources + +- Patches: https://docs.firegiant.com/wix/tools/patches/ +- Patch schema: https://docs.firegiant.com/wix/schema/wxs/patch/ +- PatchBaseline schema: https://docs.firegiant.com/wix/schema/wxs/patchbaseline/ +- MajorUpgrade schema: https://docs.firegiant.com/wix/schema/wxs/majorupgrade/ +- MsiPackage schema: https://docs.firegiant.com/wix/schema/wxs/msipackage/ +- Bundle schema: https://docs.firegiant.com/wix/schema/wxs/bundle/ +- Windows Installer patching: https://learn.microsoft.com/windows/win32/msi/patching + +## Key Facts + +- Major upgrades are simpler and safer than MSPs for broad product changes. +- MSI version comparison ignores the fourth field. Do not depend on fourth-field changes for upgrade detection. +- `AllowSameVersionUpgrades` can be useful for dev builds but can also mask versioning mistakes. +- WiX 4+ patch authoring can use `.wixpdb` or `.msi` baselines with `PatchBaseline`. +- `.wixpdb` baselines need correct bind paths to target/update payload files. +- `.msi` baselines can be easier to use but require extraction and can be slower. + +## External Lessons Worth Testing + +- WiX issue #7778: v3 and v4+ Burn package dependency keys can be incompatible. A package-level `` can preserve compatibility in some scenarios, but repair/uninstall must be tested afterward. +- WiX 4+ bundles respect `-arch`; old WiX 3 x86-Burn assumptions can break x64 registry searches, BA payload loading, and dependency detection. +- Stable component GUIDs, stable file IDs, and stable generated fragment identity matter for MSPs. +- Custom action failures should be diagnosed from MSI logs, not from UI symptoms. + +## Treat With Caution + +- v4/v5 guidance usually applies conceptually to v6, but verify exact package names and schema support against the WiX version pinned in the repo. +- Stack Overflow/blog advice is useful for symptoms, not authoritative design. Convert it into a FieldWorks repro and validation checklist. diff --git a/.github/skills/fieldworks-wix6-upgrade-patching/references/upgrade-patch-checklist.md b/.github/skills/fieldworks-wix6-upgrade-patching/references/upgrade-patch-checklist.md new file mode 100644 index 0000000000..b21b340ba9 --- /dev/null +++ b/.github/skills/fieldworks-wix6-upgrade-patching/references/upgrade-patch-checklist.md @@ -0,0 +1,41 @@ +# FieldWorks Upgrade And Patch Checklist + +## FieldWorks Files To Inspect + +- `specs/001-wix-v6-migration/REMAINING_WIX6_ISSUES.md`: active upgrade, ARP, and uninstall risks. +- `specs/001-wix-v6-migration/verification-matrix.md`: rows for upgrade, registry, uninstall, offline, custom actions, signing. +- `specs/001-wix-v6-migration/golden-install-checklist.md`: VM scenarios A-F. +- `specs/001-wix-v6-migration/parity-check.md`: existing WiX 3 vs WiX 6 evidence. +- `FLExInstaller/wix6/Shared/Base/Framework.wxs`: MSI major upgrade, properties, registry searches, custom action sequencing. +- `FLExInstaller/wix6/Shared/Base/Bundle.wxs`: bundle identity, related bundle upgrade, app MSI package, ARP visibility. +- `Build/Installer.targets`: WiX 6 build targets. Verify patch target presence before using `-BuildPatch -InstallerToolset Wix6`. +- `Build/Installer.legacy.targets`: old patch process and warnings about patch fragility. + +## Evidence To Capture + +- Bundle log for install/upgrade/uninstall. +- Package MSI log via `WixBundleLog_AppMsiPackage` or explicit `msiexec /l*v`. +- ARP screenshots before and after. +- Registry exports for FieldWorks keys and Windows uninstall keys. +- File listings for app folder and data folder. +- Product inventory showing ProductCode, UpgradeCode, version, and package source/cache. +- `.msi`, `.wixpdb`, and SHA256 hashes for baseline and update artifacts. + +## Upgrade Pass Criteria + +- Existing WiX 3 install is detected. +- WiX 6 install does not create side-by-side FieldWorks instances. +- Old install is removed/replaced. +- App folder defaults or preserved paths match expected behavior. +- Data folder remains in place and is not duplicated. +- User settings and projects survive. +- ARP shows one sensible FieldWorks entry. +- Repair and uninstall work after upgrade. + +## Patch Pass Criteria + +- Target and update baselines are archived with `.wixpdb` and payloads. +- Component GUIDs and file IDs are stable across target/update where required. +- Patch authoring uses `Patch`/`PatchBaseline` and produces a valid `.msp`. +- Patch install, repair, uninstall, rollback, and supersedence behavior are tested. +- Build logs prove which target/update artifacts were used. diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index c578ee748d..9245febef6 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -5,6 +5,15 @@ on: pull_request: branches: ["release/**", "main", "feature/PubSub"] workflow_dispatch: + inputs: + run_wix6_installer_check: + description: 'Run the opt-in WiX 6 quiet install/uninstall verification on the GitHub runner' + required: false + default: 'false' + type: choice + options: + - 'false' + - 'true' permissions: contents: read @@ -61,6 +70,110 @@ jobs: ./Output/**/*.log ./Output/**/*.log.stderr + wix6_installer_build: + name: Build WiX 6 installer artifacts + runs-on: windows-latest + env: + CROWDIN_API_KEY: ${{ secrets.FLEX_CROWDIN_API }} + LcmRootDir: ${{ github.workspace }}/Localizations/LCM + steps: + - name: Checkout Files + uses: actions/checkout@v6 + id: checkout + + - name: Checkout Helps + uses: actions/checkout@v6 + with: + repository: 'sillsdev/FwHelps' + ref: 'develop' + fetch-depth: 0 + path: 'DistFiles/Helps' + + - name: Checkout Localizations + uses: actions/checkout@v6 + with: + repository: 'sillsdev/FwLocalizations' + ref: 'develop' + fetch-depth: 0 + path: 'Localizations' + + - name: Checkout liblcm + uses: actions/checkout@v6 + with: + repository: 'sillsdev/liblcm' + ref: 'master' + fetch-depth: 0 + path: 'Localizations/LCM' + + - name: Setup dotnet + uses: actions/setup-dotnet@v5 + with: + dotnet-version: | + 8.0.x + + - name: Validate installer prerequisites + shell: powershell + run: | + .\Build\Agent\Setup-InstallerBuild.ps1 -ValidateOnly + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + + - name: Build WiX 6 installer + shell: powershell + run: | + $buildLog = Join-Path $pwd 'wix6-installer-build.log' + $exitCode = 0 + Start-Transcript -Path $buildLog -Force + try { + .\build.ps1 -BuildInstaller -InstallerToolset Wix6 -Configuration Release -Verbosity detailed -MsBuildArgs @('/bl:wix6-installer-build.binlog','/p:FastBundleBuild=0') + $exitCode = $LASTEXITCODE + } + catch { + Write-Error $_ + $exitCode = 1 + } + finally { + Stop-Transcript + } + if ($exitCode -ne 0) { exit $exitCode } + + - name: Audit WiX 6 installer evidence + shell: powershell + run: | + .\Build\Agent\Test-Wix6InstallerBuildEvidence.ps1 -Configuration Release -Platform x64 -BuildLogPath .\wix6-installer-build.log -ReportPath .\Output\InstallerEvidence\wix6-ci-build-audit.txt -RequireNoWix3ToolsOnPath + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + + - name: Run opt-in WiX 6 installer verification + if: ${{ github.event_name == 'workflow_dispatch' && inputs.run_wix6_installer_check == 'true' }} + shell: powershell + run: | + .\scripts\Agent\Invoke-InstallerCheck.ps1 -InstallerType Bundle -InstallerToolset Wix6 -Configuration Release -Platform x64 -RunId wix6-ci-install-check -InstallArguments @('/quiet','/norestart') -InstallTimeoutSeconds 3600 -RunUninstall -UninstallArguments @('/uninstall','/quiet','/norestart') -UninstallTimeoutSeconds 1800 + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + + - name: Upload WiX 6 installer artifacts + uses: actions/upload-artifact@v7 + if: ${{ !cancelled() }} + with: + name: wix6-installer-artifacts + if-no-files-found: error + path: | + FLExInstaller/wix6/bin/x64/Release/FieldWorksBundle.exe + FLExInstaller/wix6/bin/x64/Release/FieldWorksBundle.wixpdb + FLExInstaller/wix6/bin/x64/Release/FieldWorksOfflineBundle.exe + FLExInstaller/wix6/bin/x64/Release/FieldWorksOfflineBundle.wixpdb + FLExInstaller/wix6/bin/x64/Release/en-US/FieldWorks.msi + FLExInstaller/wix6/bin/x64/Release/en-US/FieldWorks.wixpdb + + - name: Upload WiX 6 build evidence + uses: actions/upload-artifact@v7 + if: ${{ always() }} + with: + name: wix6-installer-build-evidence + if-no-files-found: warn + path: | + wix6-installer-build.log + wix6-installer-build.binlog + Output/InstallerEvidence/** + publish_test_results: name: Publish Test Results if: ${{ !cancelled() }} diff --git a/.github/workflows/base-installer-cd.yml b/.github/workflows/base-installer-cd.yml index dc85a8d83a..feb37369fc 100644 --- a/.github/workflows/base-installer-cd.yml +++ b/.github/workflows/base-installer-cd.yml @@ -1,6 +1,8 @@ -name: Base Installer +name: Legacy Base Installer (WiX 3) # Builds the FieldWorks Base Installers (On- and Offline) (x64). +# Transitional WiX 3 release lane: this workflow still checks out PatchableInstaller/genericinstaller. +# The WiX 6 migration lane lives in CI.yml and must not depend on PatchableInstaller. # If `make_release` is true: # - Uploads installers to https://flex-updates.s3.amazonaws.com/?prefix=jobs/FieldWorks-Win-all-Release-Base. # - Tags the repository. @@ -23,7 +25,7 @@ on: required: false default: 'develop' installer_ref: - description: 'Commit-ish for PatchableInstaller repository' + description: 'Commit-ish for PatchableInstaller repository (legacy WiX 3 lane only)' required: false default: 'master' localizations_ref: @@ -40,7 +42,7 @@ on: default: 'false' concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + group: ${{ github.workflow }}-${{ github.ref || github.run_id }} cancel-in-progress: true jobs: @@ -49,7 +51,7 @@ jobs: CROWDIN_API_KEY: ${{ secrets.FLEX_CROWDIN_API }} LcmRootDir: ${{ github.workspace }}/Localizations/LCM FILESTOSIGNLATER: ./signExternally - name: Build Debug and run Tests + name: Build legacy WiX 3 base installer and run tests runs-on: windows-latest steps: - name: Compute build number @@ -60,6 +62,7 @@ jobs: $combined = $lastJenkins + $githubRun echo "Calculated build number: $combined" echo "FW_BUILD_NUMBER=$combined" >> $env:GITHUB_ENV + echo "fw_build_number=$combined" >> $env:GITHUB_OUTPUT - name: Checkout Files uses: actions/checkout@v6 @@ -77,7 +80,7 @@ jobs: fetch-depth: 0 path: 'DistFiles/Helps' - - name: Checkout PatchableInstaller + - name: Checkout PatchableInstaller (legacy WiX 3 only) uses: actions/checkout@v6 id: installer-checkout with: @@ -136,7 +139,7 @@ jobs: - name: Setup dotnet uses: actions/setup-dotnet@v5 with: - dotnet-version: | + dotnet-version: | 3.1.x 5.0.x @@ -169,11 +172,11 @@ jobs: $results = Select-String -Path "build.log" -Pattern "^\s*[1-9][0-9]* Error\(s\)" if ($results) { foreach ($result in $results) { - Write-Host "Found errors in build.log $($result.LineNumber): $($result.Line)" -ForegroundColor red + Write-Host "Found errors in build.log $($result.LineNumber): $($result.Line)" -ForegroundColor red } exit 1 } else { - Write-Host "No errors found" -ForegroundColor green + Write-Host "No errors found" -ForegroundColor green exit 0 } @@ -248,24 +251,24 @@ jobs: run: | $offlineExe = "${{ steps.stage_installers.outputs.offline_exe }}" $onlineExe = "${{ steps.stage_installers.outputs.online_exe }}" - + if (-not (Test-Path $offlineExe)) { throw "Offline installer not found at $offlineExe" } if (-not (Test-Path $onlineExe)) { throw "Online installer not found at $onlineExe" } - + $offlineFile = Split-Path $offlineExe -Leaf $onlineFile = Split-Path $onlineExe -Leaf - + $s3BasePath = "jobs/FieldWorks-Win-all-Release-Base/$($env:FW_BUILD_NUMBER)" $offlineS3Key = "$s3BasePath/$offlineFile" $onlineS3Key = "$s3BasePath/$onlineFile" - + aws s3 cp $offlineExe "s3://flex-updates/$offlineS3Key" Write-Host "Uploaded offline installer to s3://flex-updates/$offlineS3Key" - + aws s3 cp $onlineExe "s3://flex-updates/$onlineS3Key" Write-Host "Uploaded online installer to s3://flex-updates/$onlineS3Key" @@ -295,8 +298,8 @@ jobs: uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe with: target_commitish: ${{ github.event.inputs.fw_ref || github.ref }} - tag_name: build-${{ env.FW_BUILD_NUMBER }} - name: "FieldWorks Base Build #${{ env.FW_BUILD_NUMBER }}" + tag_name: build-${{ steps.build_number.outputs.fw_build_number }} + name: "FieldWorks Base Build #${{ steps.build_number.outputs.fw_build_number }}" draft: false prerelease: true fail_on_unmatched_files: true diff --git a/.github/workflows/patch-installer-cd.yml b/.github/workflows/patch-installer-cd.yml index 4233836bc1..0666cbea69 100644 --- a/.github/workflows/patch-installer-cd.yml +++ b/.github/workflows/patch-installer-cd.yml @@ -1,6 +1,8 @@ -name: Patch Installer +name: Legacy Patch Installer (WiX 3) # Builds the FieldWorks Patch Installer on the specified `base_release` +# Transitional WiX 3 patch lane: WiX 6 patch/MSP support is deferred and not provided by this workflow. +# This workflow still checks out PatchableInstaller/genericinstaller for the legacy patch process. # If `make_release` is true, uploads installers to https://flex-updates.s3.amazonaws.com/?prefix=jobs/FieldWorks-Win-all-Release-Patch. # Saves the build log as an artifact of the workflow run. # Note: FW_BUILD_NUMBER is higher than GITHUB_RUN_NUMBER because it needs to be higher than the build number on artifacts from our previous system. @@ -23,7 +25,7 @@ on: required: false default: 'develop' installer_ref: - description: 'Commit-ish for PatchableInstaller repository' + description: 'Commit-ish for PatchableInstaller repository (legacy WiX 3 lane only)' required: false default: 'master' localizations_ref: @@ -50,7 +52,7 @@ on: default: 'true' concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + group: ${{ github.workflow }}-${{ github.ref || github.run_id }} cancel-in-progress: true jobs: @@ -63,7 +65,7 @@ jobs: FILESTOSIGNLATER: ./signExternally GH_TOKEN: ${{ github.token }} BASE_BUILD_NUMBER: ${{ inputs.base_build_number || '1416' }} - name: Build Debug and run Tests + name: Build legacy WiX 3 patch installer and run tests runs-on: windows-latest steps: - name: Compute build number for archival @@ -92,7 +94,7 @@ jobs: fetch-depth: 0 path: 'DistFiles/Helps' - - name: Checkout PatchableInstaller + - name: Checkout PatchableInstaller (legacy WiX 3 only) uses: actions/checkout@v6 id: installer-checkout with: @@ -151,7 +153,7 @@ jobs: - name: Setup dotnet uses: actions/setup-dotnet@v5 with: - dotnet-version: | + dotnet-version: | 3.1.x 5.0.x @@ -166,7 +168,7 @@ jobs: choco install wixtoolset --version 3.11.2 --allow-downgrade --force echo "C:\Program Files (x86)\WiX Toolset v3.11\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append if: github.event_name != 'pull_request' - + - name: Import Base Build Artifacts shell: pwsh run: | @@ -211,7 +213,7 @@ jobs: ) $valueName = "Switch.System.DisableTempFileCollectionDirectoryFeature" $expectedValue = "true" - + foreach ($path in $regPaths) { Write-Host "Adding or updating registry value in $path..." if (-not (Test-Path $path)) { @@ -238,11 +240,11 @@ jobs: $results = Select-String -Path "build.log" -Pattern "^\s*[1-9][0-9]* Error\(s\)" if ($results) { foreach ($result in $results) { - Write-Host "Found errors in build.log $($result.LineNumber): $($result.Line)" -ForegroundColor red + Write-Host "Found errors in build.log $($result.LineNumber): $($result.Line)" -ForegroundColor red } exit 1 } else { - Write-Host "No errors found" -ForegroundColor green + Write-Host "No errors found" -ForegroundColor green exit 0 } diff --git a/.github/workflows/wix6-installer-cd.yml b/.github/workflows/wix6-installer-cd.yml new file mode 100644 index 0000000000..fd7edd2e90 --- /dev/null +++ b/.github/workflows/wix6-installer-cd.yml @@ -0,0 +1,294 @@ +name: WiX 6 Base Installer + +# Builds the FieldWorks WiX 6 Base Installers (Online + Offline, x64). +# This is the WiX 6 release lane. It does NOT require PatchableInstaller/genericinstaller. +# If `make_release` is true: +# - Uploads installers to https://flex-updates.s3.amazonaws.com/?prefix=jobs/FieldWorks-Win-all-Release-Base-Wix6. +# - Creates a GitHub Release of the signed artifacts. +# Saves the build log and evidence as workflow artifacts. +# Note: FW_BUILD_NUMBER is higher than GITHUB_RUN_NUMBER because it needs to be higher than the +# build number on artifacts from our previous system. + +on: + schedule: + # Runs every Monday at 02:45 UTC (offset from WiX 3 lane at 02:30 to avoid runner contention) + - cron: "45 2 * * 1" + workflow_dispatch: + inputs: + fw_ref: + description: 'Commit-ish (branch, tag, SHA) to checkout for the main repository' + required: false + default: '' + helps_ref: + description: 'Commit-ish for helps repository' + required: false + default: 'develop' + localizations_ref: + description: 'Commit-ish for localization repository' + required: false + default: 'develop' + lcm_ref: + description: 'Commit-ish for liblcm repository' + required: false + default: 'master' + make_release: + description: 'Should the build archive a release, false by default, should be set to true on a release build.' + required: false + default: 'false' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref || github.run_id }} + cancel-in-progress: true + +jobs: + build_and_sign: + permissions: + contents: write # required for creating GitHub Releases + env: + CROWDIN_API_KEY: ${{ secrets.FLEX_CROWDIN_API }} + LcmRootDir: ${{ github.workspace }}/Localizations/LCM + # FILESTOSIGNLATER causes signingProxy.bat to defer signing (append paths to this file) + # rather than calling signtool directly. The trusted-signing-action step reads this file. + FILESTOSIGNLATER: ./signExternally + name: Build WiX 6 base installer and sign + runs-on: windows-latest + steps: + - name: Compute build number + id: build_number + run: | + $lastBase = 1000 # Align with WiX 3 lane's lastJenkins offset + $githubRun = $env:GITHUB_RUN_NUMBER + $combined = $lastBase + $githubRun + Write-Host "Calculated build number: $combined" + "FW_BUILD_NUMBER=$combined" >> $env:GITHUB_ENV + "fw_build_number=$combined" >> $env:GITHUB_OUTPUT + + - name: Checkout Files + uses: actions/checkout@v6 + id: checkout + with: + ref: ${{ github.event.inputs.fw_ref || github.ref }} + fetch-depth: 0 + + - name: Checkout Helps + uses: actions/checkout@v6 + with: + repository: 'sillsdev/FwHelps' + ref: ${{ github.event.inputs.helps_ref || 'develop' }} + fetch-depth: 0 + path: 'DistFiles/Helps' + + - name: Checkout Localizations + uses: actions/checkout@v6 + with: + repository: 'sillsdev/FwLocalizations' + ref: ${{ github.event.inputs.localizations_ref || 'develop' }} + fetch-depth: 0 + path: 'Localizations' + + - name: Checkout liblcm + uses: actions/checkout@v6 + with: + repository: 'sillsdev/liblcm' + ref: ${{ github.event.inputs.lcm_ref || 'master' }} + fetch-depth: 0 + path: 'Localizations/LCM' + + - name: Setup dotnet + uses: actions/setup-dotnet@v5 + with: + dotnet-version: | + 8.0.x + + - name: Validate installer prerequisites + shell: powershell + run: | + .\Build\Agent\Setup-InstallerBuild.ps1 -ValidateOnly + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + + - name: Build WiX 6 installer + id: build + shell: powershell + run: | + $buildLog = Join-Path $pwd 'wix6-installer-build.log' + $exitCode = 0 + Start-Transcript -Path $buildLog -Force + try { + # SignOutput=true triggers signingProxy.bat; FILESTOSIGNLATER env var defers actual signing + .\build.ps1 -BuildInstaller -InstallerToolset Wix6 -Configuration Release ` + -Verbosity detailed ` + -MsBuildArgs @('/bl:wix6-installer-build.binlog', '/p:FastBundleBuild=0', '/p:SignOutput=true') + $exitCode = $LASTEXITCODE + } + catch { + Write-Error $_ + $exitCode = 1 + } + finally { + Stop-Transcript + } + if ($exitCode -ne 0) { exit $exitCode } + + - name: Audit WiX 6 installer evidence + shell: powershell + run: | + .\Build\Agent\Test-Wix6InstallerBuildEvidence.ps1 ` + -Configuration Release -Platform x64 ` + -BuildLogPath .\wix6-installer-build.log ` + -ReportPath .\Output\InstallerEvidence\wix6-cd-build-audit.txt ` + -RequireNoWix3ToolsOnPath + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + + - name: Stage WiX 6 bundles for signing + id: stage + shell: pwsh + run: | + $wixOut = 'FLExInstaller\wix6\bin\x64\Release' + $staging = Join-Path $pwd 'Installers' + New-Item -ItemType Directory -Path $staging -Force | Out-Null + + $onlineExe = Join-Path $wixOut 'FieldWorksBundle.exe' + $offlineExe = Join-Path $wixOut 'FieldWorksOfflineBundle.exe' + foreach ($f in @($onlineExe, $offlineExe)) { + if (-not (Test-Path $f)) { throw "Expected bundle not found: $f" } + } + + Copy-Item $onlineExe (Join-Path $staging 'FieldWorksBundle.exe') + Copy-Item $offlineExe (Join-Path $staging 'FieldWorksOfflineBundle.exe') + + "online_exe=$(Join-Path $staging 'FieldWorksBundle.exe')" >> $env:GITHUB_OUTPUT + "offline_exe=$(Join-Path $staging 'FieldWorksOfflineBundle.exe')" >> $env:GITHUB_OUTPUT + "staging=$staging" >> $env:GITHUB_OUTPUT + + - name: Detach Burn engines from bundles + # WiX 6 syntax: wix burn detach original.exe -engine engine.exe + # Replaces WiX 3's: insignia -ib bundle.exe -o engine.exe + shell: pwsh + run: | + $wix = 'packages\wixtoolset.sdk\6.0.2\tools\net472\x64\wix.exe' + if (-not (Test-Path $wix)) { + # Fall back to locating via MSBuild NuGet cache + $wix = (Get-ChildItem packages -Recurse -Filter 'wix.exe' | + Where-Object { $_.FullName -like '*\x64\wix.exe' } | + Select-Object -First 1).FullName + if (-not $wix) { throw 'Could not locate wix.exe in NuGet packages' } + } + + & $wix burn detach '${{ steps.stage.outputs.online_exe }}' -engine 'Installers\online-engine.exe' + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + & $wix burn detach '${{ steps.stage.outputs.offline_exe }}' -engine 'Installers\offline-engine.exe' + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + + - name: Sign Burn engines + if: github.event_name != 'pull_request' + uses: sillsdev/codesign/trusted-signing-action@v3 + with: + credentials: ${{ secrets.TRUSTED_SIGNING_CREDENTIALS }} + files-folder: Installers + files-folder-filter: '*-engine.exe' + description: 'FieldWorks Installer' + description-url: 'https://software.sil.org/fieldworks/' + + - name: Reattach signed Burn engines + if: github.event_name != 'pull_request' + # WiX 6 syntax: wix burn reattach original.exe -engine signed.exe -o final.exe + # Replaces WiX 3's: insignia -ab engine.exe bundle.exe -o bundle.exe + shell: pwsh + run: | + $wix = (Get-ChildItem packages -Recurse -Filter 'wix.exe' | + Where-Object { $_.FullName -like '*\x64\wix.exe' } | + Select-Object -First 1).FullName + if (-not $wix) { throw 'Could not locate wix.exe in NuGet packages' } + + & $wix burn reattach '${{ steps.stage.outputs.online_exe }}' -engine 'Installers\online-engine.exe' -o '${{ steps.stage.outputs.online_exe }}' + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + & $wix burn reattach '${{ steps.stage.outputs.offline_exe }}' -engine 'Installers\offline-engine.exe' -o '${{ steps.stage.outputs.offline_exe }}' + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + + - name: Sign bundles + if: github.event_name != 'pull_request' + uses: sillsdev/codesign/trusted-signing-action@v3 + with: + credentials: ${{ secrets.TRUSTED_SIGNING_CREDENTIALS }} + files-folder: Installers + files-folder-filter: 'FieldWorks*.exe' + description: 'FieldWorks Installer' + description-url: 'https://software.sil.org/fieldworks/' + + - name: Configure AWS credentials + if: ${{ inputs.make_release == 'true' }} + uses: aws-actions/configure-aws-credentials@v6 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: us-east-1 + + - name: Upload Installers to S3 + if: ${{ inputs.make_release == 'true' }} + shell: pwsh + run: | + $online = '${{ steps.stage.outputs.online_exe }}' + $offline = '${{ steps.stage.outputs.offline_exe }}' + $s3Base = "jobs/FieldWorks-Win-all-Release-Base-Wix6/$($env:FW_BUILD_NUMBER)" + + aws s3 cp $online "s3://flex-updates/$s3Base/FieldWorksBundle.exe" + Write-Host "Uploaded online bundle to s3://flex-updates/$s3Base/FieldWorksBundle.exe" + aws s3 cp $offline "s3://flex-updates/$s3Base/FieldWorksOfflineBundle.exe" + Write-Host "Uploaded offline bundle to s3://flex-updates/$s3Base/FieldWorksOfflineBundle.exe" + + - name: Compress WiX 6 artifacts for release + id: compress_artifacts + if: ${{ inputs.make_release == 'true' }} + shell: pwsh + run: | + # Archive .wixpdb files needed as baselines for future WiX 6 patch builds (T101/T102) + $wixOut = 'FLExInstaller\wix6\bin\x64\Release' + $zipPath = Join-Path $pwd 'Wix6BuildArtifacts.zip' + Compress-Archive -Path @( + "$wixOut\FieldWorksBundle.wixpdb", + "$wixOut\FieldWorksOfflineBundle.wixpdb", + "$wixOut\en-US\FieldWorks.msi", + "$wixOut\en-US\FieldWorks.wixpdb" + ) -DestinationPath $zipPath -Force + "artifacts_zip=$zipPath" >> $env:GITHUB_OUTPUT + + - name: Tag, Create Release, and Upload artifacts + if: ${{ inputs.make_release == 'true' }} + uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe + with: + target_commitish: ${{ github.event.inputs.fw_ref || github.ref }} + tag_name: wix6-build-${{ steps.build_number.outputs.fw_build_number }} + name: "FieldWorks WiX 6 Base Build #${{ steps.build_number.outputs.fw_build_number }}" + draft: false + prerelease: true + fail_on_unmatched_files: true + files: | + Installers/FieldWorksBundle.exe + Installers/FieldWorksOfflineBundle.exe + ${{ steps.compress_artifacts.outputs.artifacts_zip }} + + - name: Upload WiX 6 installer artifacts + uses: actions/upload-artifact@v7 + if: ${{ !cancelled() }} + with: + name: wix6-cd-installer-artifacts + if-no-files-found: error + path: | + Installers/FieldWorksBundle.exe + Installers/FieldWorksOfflineBundle.exe + FLExInstaller/wix6/bin/x64/Release/FieldWorksBundle.wixpdb + FLExInstaller/wix6/bin/x64/Release/FieldWorksOfflineBundle.wixpdb + FLExInstaller/wix6/bin/x64/Release/en-US/FieldWorks.msi + FLExInstaller/wix6/bin/x64/Release/en-US/FieldWorks.wixpdb + + - name: Upload WiX 6 build evidence + uses: actions/upload-artifact@v7 + if: ${{ always() }} + with: + name: wix6-cd-build-evidence + if-no-files-found: warn + path: | + wix6-installer-build.log + wix6-installer-build.binlog + signExternally + Output/InstallerEvidence/** diff --git a/.serena/project.yml b/.serena/project.yml index 63d7787207..2d85369457 100644 --- a/.serena/project.yml +++ b/.serena/project.yml @@ -1,27 +1,29 @@ + + # list of languages for which language servers are started; choose from: -# al bash clojure cpp csharp csharp_omnisharp -# dart elixir elm erlang fortran go -# haskell java julia kotlin lua markdown -# nix perl php python python_jedi r -# rego ruby ruby_solargraph rust scala swift -# terraform typescript typescript_vts zig +# al bash clojure cpp csharp +# csharp_omnisharp dart elixir elm erlang +# fortran fsharp go groovy haskell +# haxe java julia kotlin lua +# markdown +# matlab nix pascal perl php +# php_phpactor powershell python python_jedi r +# rego ruby ruby_solargraph rust scala +# swift terraform toml typescript typescript_vts +# vue yaml zig +# (This list may be outdated. For the current list, see values of Language enum here: +# https://github.com/oraios/serena/blob/main/src/solidlsp/ls_config.py +# For some languages, there are alternative language servers, e.g. csharp_omnisharp, ruby_solargraph.) # Note: # - For C, use cpp # - For JavaScript, use typescript -# Language Server Details (Serena AUTO-DOWNLOADS these on first startup): -# - csharp: Uses Microsoft.CodeAnalysis.LanguageServer (Roslyn) - same engine as VS Code's C# extension -# Downloads from Azure NuGet feed; also downloads .NET 9 runtime if needed -# ⚠️ KNOWN ISSUE: Fails on T-Mobile Home Internet due to IPv6 routing issues to Azure blob storage -# - csharp_omnisharp: Uses OmniSharp (alternative) - also auto-downloads -# - cpp: Uses clangd (auto-downloads v19.1.2 on Windows/Mac; Linux needs `apt install clangd`) +# - For Free Pascal/Lazarus, use pascal # Special requirements: -# - csharp: Requires the presence of a .sln file in the project folder. +# Some languages require additional setup/installations. +# See here for details: https://oraios.github.io/serena/01-about/020_programming-languages.html#language-servers # When using multiple languages, the first language server that supports a given file will be used for that file. # The first language is the default language and the respective language server will be used as a fallback. # Note that when using the JetBrains backend, language servers are not used and this list is correspondingly ignored. -# -# T-Mobile IPv6 workaround: Use csharp_omnisharp instead of csharp if you see Azure NuGet download errors. -# The issue is that Python urllib prefers IPv6, and T-Mobile's IPv6 routing to Azure blob storage is broken. languages: - csharp_omnisharp - cpp @@ -30,14 +32,12 @@ languages: # For a list of possible encodings, see https://docs.python.org/3.11/library/codecs.html#standard-encodings encoding: "utf-8" -# whether to use the project's gitignore file to ignore files -# Added on 2025-04-07 +# whether to use project's .gitignore files to ignore files ignore_all_files_in_gitignore: true -# list of additional paths to ignore -# same syntax as gitignore, so you can use * and ** -# Was previously called `ignored_dirs`, please update your config if you are using that. -# Added (renamed) on 2025-04-07 +# list of additional paths to ignore in this project. +# Same syntax as gitignore, so you can use * and **. +# Note: global ignored_paths from serena_config.yml are also applied additively. ignored_paths: - Output/** - Obj/** @@ -52,54 +52,63 @@ ignored_paths: # Added on 2025-04-18 read_only: false -# list of tool names to exclude. We recommend not excluding any tools, see the readme for more details. +# initial prompt for the project. It will always be given to the LLM upon activating the project +# (contrary to the memories, which are loaded on demand). +initial_prompt: | + FieldWorks (FLEx) is a Windows-first linguistics suite by SIL International. + Key build facts: + - Uses MSBuild Traversal SDK via FieldWorks.proj (21 ordered phases, 110+ projects) + - Native C++ (Phase 2) must build before managed code (Phases 3+) + - Build command: .\build.ps1 or msbuild FieldWorks.proj /p:Configuration=Debug /p:Platform=x64 /m + - Check .github/instructions/*.instructions.md for coding guidelines (managed, native, testing, etc.) + - Per-folder AGENTS.md files describe component contracts and dependencies + +# list of tool names to exclude. +# This extends the existing exclusions (e.g. from the global configuration) +# # Below is the complete list of tools for convenience. -# To make sure you have the latest list of tools, and to view their descriptions, +# To make sure you have the latest list of tools, and to view their descriptions, # execute `uv run scripts/print_tool_overview.py`. # -# * `activate_project`: Activates a project by name. +# * `activate_project`: Activates a project based on the project name or path. # * `check_onboarding_performed`: Checks whether project onboarding was already performed. # * `create_text_file`: Creates/overwrites a file in the project directory. -# * `delete_lines`: Deletes a range of lines within a file. -# * `delete_memory`: Deletes a memory from Serena's project-specific memory store. +# * `delete_memory`: Delete a memory file. Should only happen if a user asks for it explicitly, +# for example by saying that the information retrieved from a memory file is no longer correct +# or no longer relevant for the project. +# * `edit_memory`: Replaces content matching a regular expression in a memory. # * `execute_shell_command`: Executes a shell command. -# * `find_referencing_code_snippets`: Finds code snippets in which the symbol at the given location is referenced. -# * `find_referencing_symbols`: Finds symbols that reference the symbol at the given location (optionally filtered by type). -# * `find_symbol`: Performs a global (or local) search for symbols with/containing a given name/substring (optionally filtered by type). +# * `find_file`: Finds files in the given relative paths +# * `find_referencing_symbols`: Finds symbols that reference the given symbol using the language server backend +# * `find_symbol`: Performs a global (or local) search using the language server backend. # * `get_current_config`: Prints the current configuration of the agent, including the active and available projects, tools, contexts, and modes. # * `get_symbols_overview`: Gets an overview of the top-level symbols defined in a given file. -# * `initial_instructions`: Gets the initial instructions for the current project. -# Should only be used in settings where the system prompt cannot be set, -# e.g. in clients you have no control over, like Claude Desktop. +# * `initial_instructions`: Provides instructions Serena usage (i.e. the 'Serena Instructions Manual') +# for clients that do not read the initial instructions when the MCP server is connected. # * `insert_after_symbol`: Inserts content after the end of the definition of a given symbol. -# * `insert_at_line`: Inserts content at a given line in a file. # * `insert_before_symbol`: Inserts content before the beginning of the definition of a given symbol. # * `list_dir`: Lists files and directories in the given directory (optionally with recursion). -# * `list_memories`: Lists memories in Serena's project-specific memory store. +# * `list_memories`: List available memories. Any memory can be read using the `read_memory` tool. # * `onboarding`: Performs onboarding (identifying the project structure and essential tasks, e.g. for testing or building). -# * `prepare_for_new_conversation`: Provides instructions for preparing for a new conversation (in order to continue with the necessary context). # * `read_file`: Reads a file within the project directory. -# * `read_memory`: Reads the memory with the given name from Serena's project-specific memory store. -# * `remove_project`: Removes a project from the Serena configuration. -# * `replace_lines`: Replaces a range of lines within a file with new content. -# * `replace_symbol_body`: Replaces the full definition of a symbol. -# * `restart_language_server`: Restarts the language server, may be necessary when edits not through Serena happen. +# * `read_memory`: Read the content of a memory file. This tool should only be used if the information +# is relevant to the current task. You can infer whether the information +# is relevant from the memory file name. +# You should not read the same memory file multiple times in the same conversation. +# * `rename_memory`: Renames or moves a memory. Moving between project and global scope is supported +# (e.g., renaming "global/foo" to "bar" moves it from global to project scope). +# * `rename_symbol`: Renames a symbol throughout the codebase using language server refactoring capabilities. +# For JB, we use a separate tool. +# * `replace_content`: Replaces content in a file (optionally using regular expressions). +# * `replace_symbol_body`: Replaces the full definition of a symbol using the language server backend. +# * `safe_delete_symbol`: # * `search_for_pattern`: Performs a search for a pattern in the project. -# * `summarize_changes`: Provides instructions for summarizing the changes made to the codebase. -# initial prompt for the project. It will always be given to the LLM upon activating the project -# (contrary to the memories, which are loaded on demand). -initial_prompt: | - FieldWorks (FLEx) is a Windows-first linguistics suite by SIL International. - Key build facts: - - Uses MSBuild Traversal SDK via FieldWorks.proj (21 ordered phases, 110+ projects) - - Native C++ (Phase 2) must build before managed code (Phases 3+) - - Build command: .\build.ps1 or msbuild FieldWorks.proj /p:Configuration=Debug /p:Platform=x64 /m - - Check .github/instructions/*.instructions.md for coding guidelines (managed, native, testing, etc.) - - Per-folder AGENTS.md files describe component contracts and dependencies -# project_name: Intentionally left out so that the folder name will be used and worktrees will not conflict +# * `write_memory`: Write some information (utf-8-encoded) about this project that can be useful for future tasks to a memory in md format. +# The memory name should be meaningful. excluded_tools: [] -# list of tools to include that would otherwise be disabled (particularly optional tools that are disabled by default) +# list of tools to include that would otherwise be disabled (particularly optional tools that are disabled by default). +# This extends the existing inclusions (e.g. from the global configuration). included_optional_tools: [] # list of mode names to that are always to be included in the set of active modes @@ -123,7 +132,6 @@ fixed_tools: [] # the name by which the project can be referenced within Serena project_name: FieldWorks - # time budget (seconds) per tool call for the retrieval of additional symbol information # such as docstrings or parameter information. # This overrides the corresponding setting in the global configuration; see the documentation there. @@ -136,3 +144,26 @@ symbol_info_budget: # Note: the backend is fixed at startup. If a project with a different backend # is activated post-init, an error will be returned. language_backend: + +# line ending convention to use when writing source files. +# Possible values: unset (use global setting), "lf", "crlf", or "native" (platform default) +# This does not affect Serena's own files (e.g. memories and configuration files), which always use native line endings. +line_ending: + +# list of regex patterns which, when matched, mark a memory entry as read‑only. +# Extends the list from the global configuration, merging the two lists. +read_only_memory_patterns: [] + +# list of regex patterns for memories to completely ignore. +# Matching memories will not appear in list_memories or activate_project output +# and cannot be accessed via read_memory or write_memory. +# To access ignored memory files, use the read_file tool on the raw file path. +# Extends the list from the global configuration, merging the two lists. +# Example: ["_archive/.*", "_episodes/.*"] +ignored_memory_patterns: [] + +# advanced configuration option allowing to configure language server-specific options. +# Maps the language key to the options. +# Have a look at the docstring of the constructors of the LS implementations within solidlsp (e.g., for C# or PHP) to see which options are available. +# No documentation on options means no options are available. +ls_specific_settings: {} diff --git a/.vscode/tasks.json b/.vscode/tasks.json index e83806466c..37651de523 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -496,6 +496,32 @@ }, "problemMatcher": [] }, + { + "label": "Installer Evidence: WiX6 UI screenshots (Debug, x64)", + "type": "shell", + "command": "./scripts/Agent/Invoke-Installer.ps1 -InstallerType Bundle -InstallerToolset Wix6 -Configuration Debug -Platform x64 -CaptureScreenshots -ScreenshotDurationSeconds 20 -ScreenshotIntervalSeconds 2 -StopAfterScreenshots -NoWait -IncludeTempLogs", + "detail": "Launch WiX 6 bundle UI, capture screenshots under Output/InstallerEvidence//screenshots, then stop before install", + "options": { + "shell": { + "executable": "powershell.exe", + "args": ["-NoProfile", "-ExecutionPolicy", "Bypass", "-Command"] + } + }, + "problemMatcher": [] + }, + { + "label": "Installer Check: WiX6 Bundle (Debug, x64)", + "type": "shell", + "command": "./scripts/Agent/Invoke-InstallerCheck.ps1 -InstallerType Bundle -InstallerToolset Wix6 -Configuration Debug -Platform x64", + "detail": "Run snapshot -> install WiX 6 bundle -> snapshot -> diff (writes evidence under Output/InstallerEvidence/)", + "options": { + "shell": { + "executable": "powershell.exe", + "args": ["-NoProfile", "-ExecutionPolicy", "Bypass", "-Command"] + } + }, + "problemMatcher": [] + }, // ==================== Git ==================== { diff --git a/Build/Agent/Test-Wix6InstallerBuildEvidence.ps1 b/Build/Agent/Test-Wix6InstallerBuildEvidence.ps1 new file mode 100644 index 0000000000..b127433ff0 --- /dev/null +++ b/Build/Agent/Test-Wix6InstallerBuildEvidence.ps1 @@ -0,0 +1,307 @@ +[CmdletBinding()] +param( + [Parameter(Mandatory = $false)] + [string]$RepoRoot, + + [Parameter(Mandatory = $false)] + [ValidateSet('Debug', 'Release')] + [string]$Configuration = 'Release', + + [Parameter(Mandatory = $false)] + [ValidateSet('x64')] + [string]$Platform = 'x64', + + [Parameter(Mandatory = $false)] + [string]$BuildLogPath, + + [Parameter(Mandatory = $false)] + [string]$ReportPath, + + [Parameter(Mandatory = $false)] + [string]$WixVersion = '6.0.2', + + [Parameter(Mandatory = $false)] + [long]$MinimumOfflineBundleBytes = 50000000, + + [Parameter(Mandatory = $false)] + [switch]$SkipBuildLogAudit, + + [Parameter(Mandatory = $false)] + [switch]$RequireNoWix3ToolsOnPath, + + [Parameter(Mandatory = $false)] + [switch]$AllowPatchableInstallerDirectory +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +function Resolve-DefaultRepoRoot { + $repoRootPath = Resolve-Path -LiteralPath (Join-Path $PSScriptRoot '..\..') + return $repoRootPath.Path +} + +function New-DirectoryIfMissing { + param([Parameter(Mandatory = $true)][string]$Path) + + if (!(Test-Path -LiteralPath $Path)) { + $null = New-Item -ItemType Directory -Path $Path -Force + } +} + +function Convert-ToRepoRelativePath { + param( + [Parameter(Mandatory = $true)] + [string]$Path, + [Parameter(Mandatory = $true)] + [string]$Root + ) + + $fullPath = [System.IO.Path]::GetFullPath($Path) + $fullRoot = [System.IO.Path]::GetFullPath($Root).TrimEnd('\', '/') + if ($fullPath.StartsWith($fullRoot, [System.StringComparison]::OrdinalIgnoreCase)) { + return $fullPath.Substring($fullRoot.Length).TrimStart('\', '/') + } + + return $fullPath +} + +function Add-CheckResult { + param( + [Parameter(Mandatory = $true)] + [bool]$Passed, + [Parameter(Mandatory = $true)] + [string]$Message + ) + + if ($Passed) { + $script:ReportLines.Add(('[OK] {0}' -f $Message)) + return + } + + $script:ReportLines.Add(('[FAIL] {0}' -f $Message)) + $script:Failures.Add($Message) +} + +function Add-WarningResult { + param([Parameter(Mandatory = $true)][string]$Message) + + $script:ReportLines.Add(('[WARN] {0}' -f $Message)) + $script:Warnings.Add($Message) +} + +function Add-FileEvidence { + param( + [Parameter(Mandatory = $true)] + [string]$Label, + [Parameter(Mandatory = $true)] + [string]$Path, + [Parameter(Mandatory = $true)] + [string]$Root, + [Parameter(Mandatory = $false)] + [switch]$Hash + ) + + if (!(Test-Path -LiteralPath $Path -PathType Leaf)) { + Add-CheckResult -Passed $false -Message ("Missing {0}: {1}" -f $Label, (Convert-ToRepoRelativePath -Path $Path -Root $Root)) + return + } + + $item = Get-Item -LiteralPath $Path + $relativePath = Convert-ToRepoRelativePath -Path $item.FullName -Root $Root + Add-CheckResult -Passed $true -Message ("{0}: {1}" -f $Label, $relativePath) + $script:EvidenceLines.Add(("{0}`t{1}`t{2}" -f $Label, $relativePath, $item.Length)) + + if ($Hash) { + $fileHash = Get-FileHash -LiteralPath $item.FullName -Algorithm SHA256 + $script:EvidenceLines.Add(("{0} SHA256`t{1}`t{2}" -f $Label, $relativePath, $fileHash.Hash)) + } +} + +function Test-BuildLogForForbiddenReferences { + param( + [Parameter(Mandatory = $true)] + [string]$Path, + [Parameter(Mandatory = $true)] + [string]$ExpectedWixVersion + ) + + if (!(Test-Path -LiteralPath $Path -PathType Leaf)) { + Add-CheckResult -Passed $false -Message ("Build log not found: {0}" -f $Path) + return + } + + Add-CheckResult -Passed $true -Message ("Build log found: {0}" -f (Convert-ToRepoRelativePath -Path $Path -Root $RepoRoot)) + + $lines = Get-Content -LiteralPath $Path + $legacyToolPattern = [regex]'(?i)(^|[\\/\s"''])(candle|light|insignia)\.exe(["''\s]|$)' + $legacyPathPattern = [regex]'(?i)\b(PatchableInstaller|genericinstaller)\b' + $heatPattern = [regex]'(?i)\bheat\.exe\b' + $heatDiagnosticPattern = [regex]'(?i)^\s*heat\.exe\s*:\s*(warning|error)\s+HEAT\d+:' + $expectedHeatPatternText = ('(?i)(packages|\.nuget[\\/]packages)[\\/]wixtoolset\.heat[\\/]{0}[\\/]tools[\\/]net472[\\/]x64[\\/]heat\.exe' -f [regex]::Escape($ExpectedWixVersion)) + $expectedHeatPattern = [regex]$expectedHeatPatternText + $heatLines = New-Object 'System.Collections.Generic.List[string]' + + for ($i = 0; $i -lt $lines.Count; $i++) { + $line = $lines[$i] + $lineNumber = $i + 1 + + if ($legacyPathPattern.IsMatch($line)) { + Add-CheckResult -Passed $false -Message ("Build log references legacy generic installer path at line {0}: {1}" -f $lineNumber, $line.Trim()) + } + + if ($legacyToolPattern.IsMatch($line)) { + Add-CheckResult -Passed $false -Message ("Build log references legacy WiX 3 tool at line {0}: {1}" -f $lineNumber, $line.Trim()) + } + + if ($heatPattern.IsMatch($line)) { + if ($heatDiagnosticPattern.IsMatch($line)) { + continue + } + + $heatLines.Add($line.Trim()) + if (-not $expectedHeatPattern.IsMatch($line)) { + Add-CheckResult -Passed $false -Message ("Heat invocation is not from WixToolset.Heat {0} at line {1}: {2}" -f $ExpectedWixVersion, $lineNumber, $line.Trim()) + } + } + } + + if ($heatLines.Count -eq 0) { + Add-WarningResult -Message 'No heat.exe invocation was found in the build log. This can happen on incremental builds, but clean CI should show WixToolset.Heat usage.' + } else { + Add-CheckResult -Passed $true -Message ("Build log contains {0} WixToolset.Heat invocation/reference line(s)." -f $heatLines.Count) + } +} + +function Test-Wix3ToolsAbsentFromPath { + $toolNames = @('candle.exe', 'light.exe', 'insignia.exe') + $foundTools = New-Object 'System.Collections.Generic.List[string]' + + foreach ($toolName in $toolNames) { + $commands = Get-Command -Name $toolName -CommandType Application -ErrorAction SilentlyContinue + foreach ($command in $commands) { + $foundTools.Add(('{0}: {1}' -f $toolName, $command.Source)) + } + } + + if ($foundTools.Count -eq 0) { + Add-CheckResult -Passed $true -Message 'No WiX 3 command-line tools are available on PATH.' + return + } + + foreach ($foundTool in $foundTools) { + Add-CheckResult -Passed $false -Message ('WiX 3 command-line tool is available on PATH: {0}' -f $foundTool) + } +} + +if ([string]::IsNullOrWhiteSpace($RepoRoot)) { + $RepoRoot = Resolve-DefaultRepoRoot +} + +$RepoRoot = (Resolve-Path -LiteralPath $RepoRoot).Path + +if ([string]::IsNullOrWhiteSpace($ReportPath)) { + $ReportPath = Join-Path $RepoRoot 'Output\InstallerEvidence\wix6-installer-build-evidence.txt' +} + +$reportDir = Split-Path -Parent $ReportPath +New-DirectoryIfMissing -Path $reportDir + +$script:Failures = New-Object 'System.Collections.Generic.List[string]' +$script:Warnings = New-Object 'System.Collections.Generic.List[string]' +$script:ReportLines = New-Object 'System.Collections.Generic.List[string]' +$script:EvidenceLines = New-Object 'System.Collections.Generic.List[string]' + +$script:ReportLines.Add('WiX 6 installer build evidence') +$script:ReportLines.Add(('Started: {0:o}' -f (Get-Date))) +$script:ReportLines.Add(('RepoRoot: {0}' -f $RepoRoot)) +$script:ReportLines.Add(('Configuration: {0}' -f $Configuration)) +$script:ReportLines.Add(('Platform: {0}' -f $Platform)) +$script:ReportLines.Add(('Expected WixToolset version: {0}' -f $WixVersion)) +$script:ReportLines.Add(('Require no WiX 3 tools on PATH: {0}' -f [bool]$RequireNoWix3ToolsOnPath)) +$script:ReportLines.Add('') + +$patchableInstallerPath = Join-Path $RepoRoot 'PatchableInstaller' +if ($AllowPatchableInstallerDirectory) { + Add-WarningResult -Message 'PatchableInstaller directory check is disabled for this run.' +} else { + Add-CheckResult -Passed (!(Test-Path -LiteralPath $patchableInstallerPath)) -Message 'PatchableInstaller directory is absent from the WiX 6 build worktree.' +} + +if ($RequireNoWix3ToolsOnPath) { + Test-Wix3ToolsAbsentFromPath +} + +$wixOutputDir = Join-Path $RepoRoot ('FLExInstaller\wix6\bin\{0}\{1}' -f $Platform, $Configuration) +$wixCultureDir = Join-Path $wixOutputDir 'en-US' + +$expectedArtifacts = @( + @{ Label = 'MSI'; Path = Join-Path $wixCultureDir 'FieldWorks.msi' }, + @{ Label = 'MSI wixpdb'; Path = Join-Path $wixCultureDir 'FieldWorks.wixpdb' }, + @{ Label = 'Online bundle'; Path = Join-Path $wixOutputDir 'FieldWorksBundle.exe' }, + @{ Label = 'Online bundle wixpdb'; Path = Join-Path $wixOutputDir 'FieldWorksBundle.wixpdb' }, + @{ Label = 'Offline bundle'; Path = Join-Path $wixOutputDir 'FieldWorksOfflineBundle.exe' }, + @{ Label = 'Offline bundle wixpdb'; Path = Join-Path $wixOutputDir 'FieldWorksOfflineBundle.wixpdb' } +) + +foreach ($artifact in $expectedArtifacts) { + Add-FileEvidence -Label $artifact.Label -Path $artifact.Path -Root $RepoRoot -Hash +} + +$offlineBundlePath = Join-Path $wixOutputDir 'FieldWorksOfflineBundle.exe' +if ((Test-Path -LiteralPath $offlineBundlePath -PathType Leaf) -and $MinimumOfflineBundleBytes -gt 0) { + $offlineBundle = Get-Item -LiteralPath $offlineBundlePath + Add-CheckResult -Passed ($offlineBundle.Length -ge $MinimumOfflineBundleBytes) -Message ("Offline bundle is at least {0} bytes (actual: {1})." -f $MinimumOfflineBundleBytes, $offlineBundle.Length) +} + +$offlineSourceDir = Join-Path $wixCultureDir 'SourceDir' +$offlinePayloads = @( + 'ndp48-x86-x64-allos-enu.exe', + ('vcredist_2008_{0}.exe' -f $Platform), + ('vcredist_2010_{0}.exe' -f $Platform), + ('vcredist_2012_{0}.exe' -f $Platform), + ('vcredist_2013_{0}.exe' -f $Platform), + ('vcredist_2015-19_{0}.exe' -f $Platform) +) + +foreach ($payload in $offlinePayloads) { + Add-FileEvidence -Label 'Offline prerequisite payload' -Path (Join-Path $offlineSourceDir $payload) -Root $RepoRoot +} + +Add-FileEvidence -Label 'Offline FLEx Bridge payload' -Path (Join-Path $RepoRoot 'FLExInstaller\wix6\libs\FLExBridge_Offline.exe') -Root $RepoRoot + +if (-not $SkipBuildLogAudit) { + if ([string]::IsNullOrWhiteSpace($BuildLogPath)) { + Add-WarningResult -Message 'No build log was supplied; skipping legacy tool/path log audit.' + } else { + $resolvedBuildLog = $BuildLogPath + if (-not [System.IO.Path]::IsPathRooted($resolvedBuildLog)) { + $resolvedBuildLog = Join-Path $RepoRoot $resolvedBuildLog + } + + Test-BuildLogForForbiddenReferences -Path $resolvedBuildLog -ExpectedWixVersion $WixVersion + } +} + +$script:ReportLines.Add('') +$script:ReportLines.Add('File evidence') +$script:ReportLines.Add("Label`tPath`tValue") +$script:ReportLines.AddRange($script:EvidenceLines) +$script:ReportLines.Add('') +$script:ReportLines.Add(('Warnings: {0}' -f $script:Warnings.Count)) +$script:ReportLines.Add(('Failures: {0}' -f $script:Failures.Count)) +$script:ReportLines.Add(('Finished: {0:o}' -f (Get-Date))) + +$utf8NoBom = New-Object System.Text.UTF8Encoding($false) +[System.IO.File]::WriteAllLines($ReportPath, $script:ReportLines, $utf8NoBom) + +Write-Output "WiX 6 build evidence report: $ReportPath" +foreach ($line in $script:ReportLines) { + Write-Output $line +} + +if ($script:Failures.Count -gt 0) { + exit 1 +} + +exit 0 \ No newline at end of file diff --git a/FLExInstaller/wix6/FieldWorks.Bundle.wixproj b/FLExInstaller/wix6/FieldWorks.Bundle.wixproj index a9928157c9..efe8051b97 100644 --- a/FLExInstaller/wix6/FieldWorks.Bundle.wixproj +++ b/FLExInstaller/wix6/FieldWorks.Bundle.wixproj @@ -8,6 +8,7 @@ {RawFileName};{HintPathFromItem};$(ReferencePaths) + wixext6 {RawFileName};{HintPathFromItem};$(ReferencePaths) + wixext6 {RawFileName};{HintPathFromItem};$(ReferencePaths) + wixext6