From 85348ea63e48cdd0589c968126c8d22ce8d8bcb5 Mon Sep 17 00:00:00 2001 From: Hasso Date: Tue, 24 Feb 2026 16:20:50 -0600 Subject: [PATCH 1/7] Update CD workflows to use build.ps1 * Pass MSBuild arguments through to installer builds * Add -BuildPatch option for building patch installers * Remove WriteNonLocalDevelopmentPropertiesFile; this has long been unnecessary. Also * Force all Installer builds to be Release builds * Remove unused Target BuildInstallerWix3 --- .github/workflows/base-installer-cd.yml | 11 +- .github/workflows/patch-installer-cd.yml | 12 +- .gitignore | 2 +- Build/Installer.Wix3.targets | 2 +- ReadMe.md | 12 +- build.ps1 | 919 ++++++++++++----------- 6 files changed, 491 insertions(+), 467 deletions(-) diff --git a/.github/workflows/base-installer-cd.yml b/.github/workflows/base-installer-cd.yml index aab44b5abf..f2633291f8 100644 --- a/.github/workflows/base-installer-cd.yml +++ b/.github/workflows/base-installer-cd.yml @@ -134,20 +134,11 @@ jobs: 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: Prepare for build - shell: cmd - working-directory: Build - run: build64.bat /t:WriteNonlocalDevelopmentPropertiesFile - - name: Build and run tests id: build_installer shell: powershell run: | - cd Build - .\build64.bat /t:BuildBaseInstaller "/property:config=release;action=test;desktopNotAvailable=true" /v:d /bl ^| tee-object -FilePath build.log - cd .. - cd BuildDir - md5sum *.exe > md5.txt + .\build.ps1 -BuildInstaller -Configuration Release -MsBuildArgs @("/p:action=test;desktopNotAvailable=true","/v:d","/bl") | Tee-Object -FilePath build.log - name: Scan Build Output shell: powershell diff --git a/.github/workflows/patch-installer-cd.yml b/.github/workflows/patch-installer-cd.yml index 0b89bc051a..12e7b29901 100644 --- a/.github/workflows/patch-installer-cd.yml +++ b/.github/workflows/patch-installer-cd.yml @@ -8,7 +8,7 @@ name: Patch Installer on: push: - branches: ["main"] + branches: ["main", "release/**"] schedule: # Runs every Monday at 03:30 UTC (which is 8:30pm MST/PDT Sunday evening) - cron: "30 3 * * 1" @@ -183,8 +183,7 @@ jobs: Expand-Archive -Path "base-artifacts/ProcRunner.zip" -DestinationPath $procTarget -Force Write-Host "Expanded ProcRunner.zip -> $procTarget" - # Write out the properties file (a first build on a system requires a prompt response otherwise) - # and set an OS feature in the registry that will allow Wix v3 to use temporary files without error + # Set an OS feature in the registry that will allow Wix v3 to use temporary files without error - name: Prepare for build working-directory: Build run: | @@ -203,18 +202,13 @@ jobs: } New-ItemProperty -Path $path -Name $valueName -Value $expectedValue -Type String -Force } - - .\build64.bat /t:WriteNonlocalDevelopmentPropertiesFile - name: Build Debug and run tests id: build_installer shell: powershell run: | Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\.NETFramework\AppContext" - cd Build - .\build64.bat /t:BuildPatchInstaller "/property:config=release;action=test;desktopNotAvailable=true" /v:d /bl ^| tee-object -FilePath build.log - cd .. - cd BuildDir + .\build.ps1 -BuildPatch -Configuration Release -MsBuildArgs @("/p:action=test;desktopNotAvailable=true","/v:d","/bl") | Tee-Object -FilePath build.log - name: Scan Debug Build Output shell: powershell diff --git a/.gitignore b/.gitignore index 1d10128593..6bf8a3485d 100644 --- a/.gitignore +++ b/.gitignore @@ -54,7 +54,6 @@ x64 *.user *.ncrunchsolution *.ncb -FW.sln *.suo *.o *_ReSharper* @@ -89,6 +88,7 @@ buildi686/ buildx86_64/ installi686/ installx86_64/ +msbuild.binlog test-results/ trash/ UpgradeLog*.XML diff --git a/Build/Installer.Wix3.targets b/Build/Installer.Wix3.targets index 521783ee7a..0ef1f36cdb 100644 --- a/Build/Installer.Wix3.targets +++ b/Build/Installer.Wix3.targets @@ -28,5 +28,5 @@ - + diff --git a/ReadMe.md b/ReadMe.md index 637a1b0997..e054e079c9 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -55,24 +55,18 @@ Installer builds include the additional utilities (UnicodeCharEditor, LCMBrowser To skip them, pass `-BuildAdditionalApps:$false`. ```powershell -# Build the installer (Debug, WiX 3 default) +# Build the installer (WiX 3 default) .\build.ps1 -BuildInstaller -# Build the installer (Debug, WiX 6) +# Build the installer (WiX 6) .\build.ps1 -BuildInstaller -InstallerToolset Wix6 - -# Build the installer (Release, WiX 3 default) -.\build.ps1 -BuildInstaller -Configuration Release - -# Build the installer (Release, WiX 6) -.\build.ps1 -BuildInstaller -Configuration Release -InstallerToolset Wix6 ``` WiX 3 artifacts are produced under `FLExInstaller/bin/x64//` (MSI under `en-US/`). WiX 6 artifacts are produced under `FLExInstaller/wix6/bin/x64//` (MSI under `en-US/`). -For more details, see [specs/001-wix-v6-migration/quickstart.md](specs/001-wix-v6-migration/quickstart.md). +For more details, see [specs/001-wix-v6-migration/quickstart.md (TODO: DEAD LINK)](specs/001-wix-v6-migration/quickstart.md). ### Code signing for local installer builds diff --git a/build.ps1 b/build.ps1 index 21bb7845d2..8a6ab2df48 100644 --- a/build.ps1 +++ b/build.ps1 @@ -1,158 +1,164 @@ -<# +<# .SYNOPSIS - Builds the FieldWorks repository using the MSBuild Traversal SDK. + Builds the FieldWorks repository using the MSBuild Traversal SDK. .DESCRIPTION - This script orchestrates the build process for FieldWorks. It handles: - 1. Initializing the Visual Studio Developer Environment (if needed). - 2. Bootstrapping build tasks (FwBuildTasks). - 3. Restoring NuGet packages. - 4. Building the solution via FieldWorks.proj using MSBuild Traversal. + This script orchestrates the build process for FieldWorks. It handles: + 1. Initializing the Visual Studio Developer Environment (if needed). + 2. Bootstrapping build tasks (FwBuildTasks). + 3. Restoring NuGet packages. + 4. Building the solution via FieldWorks.proj using MSBuild Traversal. .PARAMETER Configuration - The build configuration (Debug or Release). Default is Debug. + The build configuration (Debug or Release). Default is Debug. .PARAMETER Platform - The target platform. Only x64 is supported. Default is x64. + The target platform. Only x64 is supported. Default is x64. .PARAMETER Serial - If set, disables parallel build execution (/m). Default is false (parallel enabled). + If set, disables parallel build execution (/m). Default is false (parallel enabled). .PARAMETER BuildTests - If set, includes test projects in the build. Default is false. + If set, includes test projects in the build. Default is false. .PARAMETER RunTests - If set, runs tests after building. Implies -BuildTests. Uses VSTest via test.ps1. - Also builds native test prerequisites (Unit++/native test libs). + If set, runs tests after building. Implies -BuildTests. Uses VSTest via test.ps1. + Also builds native test prerequisites (Unit++/native test libs). .PARAMETER TestFilter - Optional VSTest filter expression (e.g., "TestCategory!=Slow"). Only used with -RunTests. + Optional VSTest filter expression (e.g., "TestCategory!=Slow"). Only used with -RunTests. .PARAMETER BuildAdditionalApps - If set, includes optional utility applications (e.g. MigrateSqlDbs, LCMBrowser, UnicodeCharEditor) in the build. - Default is false unless -BuildInstaller is specified, which enables it automatically. + If set, includes optional utility applications (e.g. MigrateSqlDbs, LCMBrowser, UnicodeCharEditor) in the build. + Default is false unless -BuildInstaller is specified, which enables it automatically. .PARAMETER Verbosity - Specifies the amount of information to display in the build log. - Values: q[uiet], m[inimal], n[ormal], d[etailed], diag[nostic]. - Default is 'minimal'. + Specifies the amount of information to display in the build log. + Values: q[uiet], m[inimal], n[ormal], d[etailed], diag[nostic]. + Default is 'minimal'. .PARAMETER TraceCrashes - If set, enables the dev diagnostics config (FieldWorks.Diagnostics.dev.config) so trace logging - is written next to the built executable. Useful for crash investigation. + If set, enables the dev diagnostics config (FieldWorks.Diagnostics.dev.config) so trace logging + is written next to the built executable. Useful for crash investigation. .PARAMETER NodeReuse - Enables or disables MSBuild node reuse (/nr). Default is true. + Enables or disables MSBuild node reuse (/nr). Default is true. .PARAMETER MsBuildArgs - Additional arguments to pass directly to MSBuild. + Additional arguments to pass directly to MSBuild. .PARAMETER BuildInstaller - If set, builds the installer via Build/InstallerBuild.proj after the main build. - This automatically enables -BuildAdditionalApps unless explicitly disabled. + If set, builds the installer via Build/InstallerBuild.proj after the main build. + This automatically enables -BuildAdditionalApps unless explicitly disabled. .PARAMETER InstallerToolset - Selects the installer toolset to build (Wix3 or Wix6). Default is Wix3. + Selects the installer toolset to build (Wix3 or Wix6). Default is Wix3. .PARAMETER InstallerOnly - Only used with -BuildInstaller. Skips rebuilding FieldWorks and only builds the WiX installer/bundles, - reusing existing binaries under Output/. For safety, this requires a build stamp from a - prior full build in the same configuration. + Only used with -BuildInstaller. Skips rebuilding FieldWorks and only builds the WiX installer/bundles, + reusing existing binaries under Output/. For safety, this requires a build stamp from a + prior full build in the same configuration. .PARAMETER SignInstaller - Only used with -BuildInstaller. Enables local signing when signing tools are available. - By default, local installer builds capture files to sign later instead of signing. + Only used with -BuildInstaller. Enables local signing when signing tools are available. + By default, local installer builds capture files to sign later instead of signing. .PARAMETER ForceInstallerOnly - Only used with -InstallerOnly. Forces installer-only builds even when the git HEAD or dirty state - differs from the last full-build stamp, or when there are uncommitted changes outside FLExInstaller/. - Use only when you are sure the current Output/ binaries are still what you want to package. + Only used with -InstallerOnly. Forces installer-only builds even when the git HEAD or dirty state + differs from the last full-build stamp, or when there are uncommitted changes outside FLExInstaller/. + Use only when you are sure the current Output/ binaries are still what you want to package. .PARAMETER UseLocalLcm - If set, builds liblcm from a local checkout (default: ../liblcm) after the FieldWorks build - and copies the resulting DLLs into the output directory, overwriting the NuGet package versions. - Use this to test local liblcm fixes without publishing a NuGet package. + If set, builds liblcm from a local checkout (default: ../liblcm) after the FieldWorks build + and copies the resulting DLLs into the output directory, overwriting the NuGet package versions. + Use this to test local liblcm fixes without publishing a NuGet package. .PARAMETER LocalLcmPath - Path to the local liblcm repository. Defaults to ../liblcm relative to the FieldWorks repo root. - Only used when -UseLocalLcm is specified. + Path to the local liblcm repository. Defaults to ../liblcm relative to the FieldWorks repo root. + Only used when -UseLocalLcm is specified. .PARAMETER LogFile - Path to a file where the build output should be logged. + Path to a file where the build output should be logged. .PARAMETER TailLines - If specified, only displays the last N lines of output after the build completes. - Useful for CI/agent scenarios where you want to see recent output without piping. - The full output is still written to LogFile if specified. + If specified, only displays the last N lines of output after the build completes. + Useful for CI/agent scenarios where you want to see recent output without piping. + The full output is still written to LogFile if specified. .EXAMPLE - .\build.ps1 - Builds Debug x64 in parallel with minimal logging. + .\build.ps1 + Builds Debug x64 in parallel with minimal logging. .EXAMPLE - .\build.ps1 -Configuration Release -BuildTests - Builds Release x64 including test projects. + .\build.ps1 -Configuration Release -BuildTests + Builds Release x64 including test projects. .EXAMPLE - .\build.ps1 -RunTests - Builds Debug x64 including test projects and runs all tests. + .\build.ps1 -RunTests + Builds Debug x64 including test projects and runs all tests. .EXAMPLE - .\build.ps1 -Serial -Verbosity detailed - Builds Debug x64 serially with detailed logging. + .\build.ps1 -Serial -Verbosity detailed + Builds Debug x64 serially with detailed logging. .EXAMPLE - .\build.ps1 -UseLocalLcm - Builds FieldWorks, then builds liblcm from ../liblcm and copies DLLs into Output. + .\build.ps1 -UseLocalLcm + Builds FieldWorks, then builds liblcm from ../liblcm and copies DLLs into Output. .NOTES - FieldWorks is x64-only. The x86 platform is no longer supported. + FieldWorks is x64-only. The x86 platform is no longer supported. #> [CmdletBinding()] param( - [string]$Configuration = "Debug", - [ValidateSet('x64')] - [string]$Platform = "x64", - [switch]$Serial, - [switch]$BuildTests, - [switch]$RunTests, - [string]$TestFilter, - [switch]$BuildAdditionalApps, - [string]$Project = "FieldWorks.proj", - [string]$Verbosity = "minimal", - [bool]$NodeReuse = $true, - [string[]]$MsBuildArgs = @(), - [string]$LogFile, - [int]$TailLines, - [switch]$SkipRestore, - [switch]$SkipNative, - [switch]$BuildInstaller, - [ValidateSet('Wix3', 'Wix6')] - [string]$InstallerToolset = "Wix3", - [switch]$InstallerOnly, - [switch]$ForceInstallerOnly, - [switch]$SignInstaller, - [switch]$TraceCrashes, - [switch]$UseLocalLcm, - [string]$LocalLcmPath + [string]$Configuration = "Debug", + [ValidateSet('x64')] + [string]$Platform = "x64", + [switch]$Serial, + [switch]$BuildTests, + [switch]$RunTests, + [string]$TestFilter, + [switch]$BuildAdditionalApps, + [string]$Project = "FieldWorks.proj", + [switch]$BuildPatch, + [string]$Verbosity = "minimal", + [bool]$NodeReuse = $true, + [string[]]$MsBuildArgs = @(), + [string]$LogFile, + [int]$TailLines, + [switch]$SkipRestore, + [switch]$SkipNative, + [switch]$BuildInstaller, + [ValidateSet('Wix3', 'Wix6')] + [string]$InstallerToolset = "Wix3", + [switch]$InstallerOnly, + [switch]$ForceInstallerOnly, + [switch]$SignInstaller, + [switch]$TraceCrashes, + [switch]$UseLocalLcm, + [string]$LocalLcmPath ) $ErrorActionPreference = "Stop" if ($Configuration -like "--*") { - if ($Configuration -eq "--TraceCrashes" -and -not $TraceCrashes) { - $TraceCrashes = $true - $Configuration = "Debug" - Write-Output "[WARN] Detected '--TraceCrashes' passed without PowerShell switch parsing. Using -TraceCrashes and defaulting Configuration to Debug." - } - else { - throw "Invalid Configuration value '$Configuration'. Use -TraceCrashes (single dash) for the trace option." - } + if ($Configuration -eq "--TraceCrashes" -and -not $TraceCrashes) { + $TraceCrashes = $true + $Configuration = "Debug" + Write-Output "[WARN] Detected '--TraceCrashes' passed without PowerShell switch parsing. Using -TraceCrashes and defaulting Configuration to Debug." + } + else { + throw "Invalid Configuration value '$Configuration'. Use -TraceCrashes (single dash) for the trace option." + } } if ($BuildInstaller -and -not $BuildAdditionalApps) { - $BuildAdditionalApps = $true - Write-Host "BuildInstaller enabled: including additional apps (use -BuildAdditionalApps:$false to skip)." -ForegroundColor Yellow + $BuildAdditionalApps = $true + Write-Host "BuildInstaller enabled: including additional apps (use -BuildAdditionalApps:$false to skip)." -ForegroundColor Yellow +} + +if (($BuildInstaller -or $BuildPatch) -and -not ($Configuration -eq "Release")) { + $Configuration = "Release" + Write-Host "Installer builds must be Release builds; changing Configuration to Release" -ForegroundColor Yellow } # For local Release builds, use a stable daily build number so native artifacts can be reused. @@ -160,24 +166,24 @@ if ($BuildInstaller -and -not $BuildAdditionalApps) { # CI (GitHub Actions) should continue to provide its own build number. $isGitHubActions = ($env:GITHUB_ACTIONS -eq 'true') if ($Configuration -eq 'Release' -and -not $isGitHubActions) { - $buildNumberOk = $false - if (-not [string]::IsNullOrWhiteSpace($env:FW_BUILD_NUMBER)) { - $parsedBuildNumber = 0 - if ([int]::TryParse($env:FW_BUILD_NUMBER, [ref]$parsedBuildNumber) -and $parsedBuildNumber -ge 0 -and $parsedBuildNumber -le 65535) { - $buildNumberOk = $true - } - } - - if (-not $buildNumberOk) { - $utcNow = (Get-Date).ToUniversalTime() - $env:FW_BUILD_NUMBER = "{0}{1:000}" -f $utcNow.ToString('yy'), $utcNow.DayOfYear - Write-Host "Using local numeric build number: $($env:FW_BUILD_NUMBER)" -ForegroundColor Yellow - } - - if ([string]::IsNullOrWhiteSpace($env:FW_BUILD_LABEL)) { - $env:FW_BUILD_LABEL = "local_{0}" -f (Get-Date).ToUniversalTime().ToString('yyyyMMdd') - Write-Host "Using local build label: $($env:FW_BUILD_LABEL)" -ForegroundColor Yellow - } + $buildNumberOk = $false + if (-not [string]::IsNullOrWhiteSpace($env:FW_BUILD_NUMBER)) { + $parsedBuildNumber = 0 + if ([int]::TryParse($env:FW_BUILD_NUMBER, [ref]$parsedBuildNumber) -and $parsedBuildNumber -ge 0 -and $parsedBuildNumber -le 65535) { + $buildNumberOk = $true + } + } + + if (-not $buildNumberOk) { + $utcNow = (Get-Date).ToUniversalTime() + $env:FW_BUILD_NUMBER = "{0}{1:000}" -f $utcNow.ToString('yy'), $utcNow.DayOfYear + Write-Host "Using local numeric build number: $($env:FW_BUILD_NUMBER)" -ForegroundColor Yellow + } + + if ([string]::IsNullOrWhiteSpace($env:FW_BUILD_LABEL)) { + $env:FW_BUILD_LABEL = "local_{0}" -f (Get-Date).ToUniversalTime().ToString('yyyyMMdd') + Write-Host "Using local build label: $($env:FW_BUILD_LABEL)" -ForegroundColor Yellow + } } # ===========ed Module @@ -185,8 +191,8 @@ if ($Configuration -eq 'Release' -and -not $isGitHubActions) { $helpersPath = Join-Path $PSScriptRoot "Build/Agent/FwBuildHelpers.psm1" if (-not (Test-Path $helpersPath)) { - Write-Host "[ERROR] FwBuildHelpers.psm1 not found at $helpersPath" -ForegroundColor Red - exit 1 + Write-Host "[ERROR] FwBuildHelpers.psm1 not found at $helpersPath" -ForegroundColor Red + exit 1 } Import-Module $helpersPath -Force @@ -200,348 +206,387 @@ $fwTasksDropPath = Join-Path $PSScriptRoot "BuildTools/FwBuildTasks/$Configurati # ============================================================================= $cleanupArgs = @{ - IncludeOmniSharp = $true - RepoRoot = $PSScriptRoot + IncludeOmniSharp = $true + RepoRoot = $PSScriptRoot } $testExitCode = 0 function Get-RepoStamp { - $gitHead = & git rev-parse HEAD - if ($LASTEXITCODE -ne 0) { - throw "Failed to determine git HEAD (git rev-parse HEAD)." - } - $gitHead = ($gitHead | Out-String).Trim() - - $gitStatus = & git status --porcelain - if ($LASTEXITCODE -ne 0) { - throw "Failed to determine git dirty state (git status --porcelain)." - } - $gitStatusText = ($gitStatus | Out-String) - $isDirty = -not [string]::IsNullOrWhiteSpace($gitStatusText) - - # For installer-only iteration, we want to allow local edits under FLExInstaller/** - # while still blocking when *product* inputs have changed (e.g., Src/**). - $isDirtyOutsideInstaller = $false - if ($isDirty) { - $lines = @() - foreach ($line in ($gitStatusText -split "`r?`n")) { - $trimmed = $line.TrimEnd() - if ([string]::IsNullOrWhiteSpace($trimmed)) { - continue - } - $lines += $trimmed - } - - foreach ($statusLine in $lines) { - if ($statusLine.Length -lt 4) { - continue - } - - # Porcelain format: XYPATH (or XYOLD -> NEW) - $pathPart = $statusLine.Substring(3).Trim() - if ($pathPart -like "* -> *") { - $pathPart = ($pathPart.Split(@(" -> "), 2, [System.StringSplitOptions]::None)[1]).Trim() - } - - if (-not ($pathPart -like "FLExInstaller/*")) { - $isDirtyOutsideInstaller = $true - break - } - } - } - - return [pscustomobject]@{ - GitHead = $gitHead - IsDirty = $isDirty - IsDirtyOutsideInstaller = $isDirtyOutsideInstaller - } + $gitHead = & git rev-parse HEAD + if ($LASTEXITCODE -ne 0) { + throw "Failed to determine git HEAD (git rev-parse HEAD)." + } + $gitHead = ($gitHead | Out-String).Trim() + + $gitStatus = & git status --porcelain + if ($LASTEXITCODE -ne 0) { + throw "Failed to determine git dirty state (git status --porcelain)." + } + $gitStatusText = ($gitStatus | Out-String) + $isDirty = -not [string]::IsNullOrWhiteSpace($gitStatusText) + + # For installer-only iteration, we want to allow local edits under FLExInstaller/** + # while still blocking when *product* inputs have changed (e.g., Src/**). + $isDirtyOutsideInstaller = $false + if ($isDirty) { + $lines = @() + foreach ($line in ($gitStatusText -split "`r?`n")) { + $trimmed = $line.TrimEnd() + if ([string]::IsNullOrWhiteSpace($trimmed)) { + continue + } + $lines += $trimmed + } + + foreach ($statusLine in $lines) { + if ($statusLine.Length -lt 4) { + continue + } + + # Porcelain format: XYPATH (or XYOLD -> NEW) + $pathPart = $statusLine.Substring(3).Trim() + if ($pathPart -like "* -> *") { + $pathPart = ($pathPart.Split(@(" -> "), 2, [System.StringSplitOptions]::None)[1]).Trim() + } + + if (-not ($pathPart -like "FLExInstaller/*")) { + $isDirtyOutsideInstaller = $true + break + } + } + } + + return [pscustomobject]@{ + GitHead = $gitHead + IsDirty = $isDirty + IsDirtyOutsideInstaller = $isDirtyOutsideInstaller + } } function Get-BuildStampPath { - param( - [Parameter(Mandatory = $true)][string]$RepoRoot, - [Parameter(Mandatory = $true)][string]$ConfigurationName - ) - $outputDir = Join-Path $RepoRoot ("Output\\{0}" -f $ConfigurationName) - return Join-Path $outputDir "BuildStamp.json" + param( + [Parameter(Mandatory = $true)][string]$RepoRoot, + [Parameter(Mandatory = $true)][string]$ConfigurationName + ) + $outputDir = Join-Path $RepoRoot ("Output\\{0}" -f $ConfigurationName) + return Join-Path $outputDir "BuildStamp.json" } try { - Invoke-WithFileLockRetry -Context "FieldWorks build" -IncludeOmniSharp -Action { - # Initialize Visual Studio Developer environment - Initialize-VsDevEnvironment - Test-CvtresCompatibility - - # Set architecture environment variable (x64-only) - $env:arch = 'x64' - Write-Host "Set arch environment variable to: $env:arch" -ForegroundColor Green - - # Stop conflicting processes before the build - Stop-ConflictingProcesses @cleanupArgs - - $projectPath = $Project - $rootedProjectPath = Join-Path $PSScriptRoot $Project - if (-not (Test-Path $projectPath) -and (Test-Path $rootedProjectPath)) { - $projectPath = $rootedProjectPath - } - if (-not (Test-Path $projectPath)) { - throw "Project path '$Project' was not found. Pass a path relative to the repo root or an absolute path." - } - - # Clean stale per-project obj/ folders - Remove-StaleObjFolders -RepoRoot $PSScriptRoot - - # LT-22382: Remove stale first-party DLLs from the output directory before building. - # Uses a whitelist of assembly names from Src/**/*.csproj to identify FW assemblies - # and checks their major version against FWMAJOR. See Build\Agent\Remove-StaleDlls.ps1. - $staleDllScript = Join-Path $PSScriptRoot "Build\Agent\Remove-StaleDlls.ps1" - if (Test-Path $staleDllScript) { - $outputDir = "Output\$Configuration" - & $staleDllScript -OutputDir $outputDir -RepoRoot $PSScriptRoot -Verbose:$VerbosePreference - } - - # ============================================================================= - # Build Configuration - # ============================================================================= - - # Determine logical core count for CL_MPCount - if ($env:CL_MPCount) { - $mpCount = $env:CL_MPCount - } - else { - $mpCount = 8 - if ($env:NUMBER_OF_PROCESSORS) { - $procCount = [int]$env:NUMBER_OF_PROCESSORS - if ($procCount -lt 8) { $mpCount = $procCount } - } - } - - # Construct MSBuild arguments - $finalMsBuildArgs = @() - - # Parallelism - if (-not $Serial) { - $finalMsBuildArgs += "/m" - } - - # Verbosity & Logging - $finalMsBuildArgs += "/v:$Verbosity" - $finalMsBuildArgs += "/nologo" - $finalMsBuildArgs += "/consoleloggerparameters:Summary" - - # Node Reuse - $finalMsBuildArgs += "/nr:$($NodeReuse.ToString().ToLower())" - - # Properties - $finalMsBuildArgs += "/p:Configuration=$Configuration" - $finalMsBuildArgs += "/p:Platform=$Platform" - if ($SkipNative) { - $finalMsBuildArgs += "/p:SkipNative=true" - } - if ($TraceCrashes) { - $finalMsBuildArgs += "/p:UseDevTraceConfig=true" - } - $finalMsBuildArgs += "/p:CL_MPCount=$mpCount" - if ($env:FW_TRACE_LOG) { - $finalMsBuildArgs += "/p:FW_TRACE_LOG=`"$($env:FW_TRACE_LOG)`"" - } - - if ($BuildTests -or $RunTests) { - $finalMsBuildArgs += "/p:BuildTests=true" - $finalMsBuildArgs += "/p:BuildNativeTests=true" - } - - if ($BuildAdditionalApps) { - $finalMsBuildArgs += "/p:BuildAdditionalApps=true" - } - - # Add user-supplied args - $finalMsBuildArgs += $MsBuildArgs - - # ============================================================================= - # Build Execution - # ============================================================================= - - Write-Host "" - Write-Host "Building FieldWorks..." -ForegroundColor Cyan - Write-Host "Project: $projectPath" -ForegroundColor Cyan - Write-Host "Configuration: $Configuration | Platform: $Platform | Parallel: $(-not $Serial) | Tests: $($BuildTests -or $RunTests)" -ForegroundColor Cyan - - if ($BuildAdditionalApps) { - Write-Host "Including optional FieldWorks executables" -ForegroundColor Yellow - } - - # Bootstrap: Build FwBuildTasks first (required by SetupInclude.targets) - $fwBuildTasksOutputDir = Join-Path $PSScriptRoot "BuildTools/FwBuildTasks/$Configuration/" - Invoke-MSBuild ` - -Arguments @('Build/Src/FwBuildTasks/FwBuildTasks.csproj', '/t:Restore;Build', "/p:Configuration=$Configuration", "/p:Platform=$Platform", "/p:FwBuildTasksOutputPath=$fwBuildTasksOutputDir", "/p:SkipFwBuildTasksAssemblyCheck=true", "/p:SkipFwBuildTasksUsingTask=true", "/p:SkipGenerateFwTargets=true", "/p:SkipSetupTargets=true", "/v:quiet", "/nologo") ` - -Description 'FwBuildTasks (Bootstrap)' - - if (-not (Test-Path $fwTasksSourcePath)) { - throw "Failed to build FwBuildTasks. Expected $fwTasksSourcePath to exist." - } - - if ($fwTasksSourcePath -ne $fwTasksDropPath) { - $dropDir = Split-Path $fwTasksDropPath -Parent - if (-not (Test-Path $dropDir)) { - New-Item -Path $dropDir -ItemType Directory -Force | Out-Null - } - Copy-Item -Path $fwTasksSourcePath -Destination $fwTasksDropPath -Force - } - - # Restore packages - if (-not $SkipRestore) { - Write-Host "Restoring NuGet packages..." -ForegroundColor Cyan - $packagesDir = Join-Path $PSScriptRoot "packages" - if (-not (Test-Path $packagesDir)) { - New-Item -Path $packagesDir -ItemType Directory -Force | Out-Null - } - & dotnet restore "$PSScriptRoot\FieldWorks.sln" /p:NoWarn=NU1903 /p:DisableWarnForInvalidRestoreProjects=true "/p:Configuration=$Configuration" "/p:Platform=$Platform" --verbosity quiet - if ($LASTEXITCODE -ne 0) { - throw "NuGet package restore failed for FieldWorks.sln" - } - Write-Host "Package restore complete." -ForegroundColor Green - } else { - Write-Host "Skipping package restore (-SkipRestore)" -ForegroundColor Yellow - } - - if ($InstallerOnly) { - if (-not $BuildInstaller) { - throw "-InstallerOnly requires -BuildInstaller." - } - - $stampPath = Get-BuildStampPath -RepoRoot $PSScriptRoot -ConfigurationName $Configuration - if (-not (Test-Path $stampPath)) { - throw "-InstallerOnly requested but no build stamp was found at '$stampPath'. Run a full build once (without -InstallerOnly) to create it." - } - - $stamp = Get-Content -LiteralPath $stampPath -Raw | ConvertFrom-Json - $current = Get-RepoStamp - - $stampConfig = $stamp.Configuration - $stampPlatform = $stamp.Platform - if (($stampConfig -ne $Configuration) -or ($stampPlatform -ne $Platform)) { - throw "-InstallerOnly stamp mismatch: stamp is Configuration='$stampConfig' Platform='$stampPlatform' but this run is Configuration='$Configuration' Platform='$Platform'. Run a full build in this configuration/platform." - } - - $headChanged = ($stamp.GitHead -ne $current.GitHead) - if ((-not $ForceInstallerOnly) -and ($headChanged -or $current.IsDirtyOutsideInstaller)) { - throw "-InstallerOnly refused: product inputs may have changed since the last full build stamp. Run a full build, or use -ForceInstallerOnly if you are sure Output\\$Configuration is still correct." - } - - Write-Host "" - Write-Host "Skipping product build (-InstallerOnly). Reusing Output\\$Configuration." -ForegroundColor Yellow - } - else { - # Build using traversal project - Invoke-MSBuild ` - -Arguments (@($projectPath) + $finalMsBuildArgs) ` - -Description "FieldWorks Solution" ` - -LogPath $LogFile ` - -TailLines $TailLines - - $stampDir = Join-Path $PSScriptRoot ("Output\\{0}" -f $Configuration) - if (-not (Test-Path $stampDir)) { - New-Item -Path $stampDir -ItemType Directory -Force | Out-Null - } - - $repoStamp = Get-RepoStamp - $stampObject = [pscustomobject]@{ - Configuration = $Configuration - Platform = $Platform - GitHead = $repoStamp.GitHead - IsDirty = $repoStamp.IsDirty - IsDirtyOutsideInstaller = $repoStamp.IsDirtyOutsideInstaller - TimestampUtc = (Get-Date).ToUniversalTime().ToString('o') - } - - $stampPath = Get-BuildStampPath -RepoRoot $PSScriptRoot -ConfigurationName $Configuration - $stampObject | ConvertTo-Json -Depth 4 | Set-Content -LiteralPath $stampPath -Encoding UTF8 - - Write-Host "" - Write-Host "[OK] Build complete!" -ForegroundColor Green - Write-Host "Output: Output\$Configuration" -ForegroundColor Cyan - } - - # Copy local LCM assemblies if requested - if ($UseLocalLcm) { - Write-Host "" - Write-Host "Applying local LCM assemblies..." -ForegroundColor Cyan - - $lcmCopyScript = Join-Path $PSScriptRoot "scripts\Agent\Copy-LocalLcm.ps1" - $lcmArgs = @{ - Configuration = $Configuration - BuildLcm = $true - SkipConfirm = $true - } - if ($LocalLcmPath) { - $lcmArgs['LcmRoot'] = $LocalLcmPath - } - - & $lcmCopyScript @lcmArgs - if ($LASTEXITCODE -ne 0) { - throw "Failed to copy local LCM assemblies." - } - } - - if ($BuildInstaller) { - Write-Host "" - Write-Host "Building Installer..." -ForegroundColor Cyan - - if (-not $isGitHubActions) { - if ($SignInstaller) { - Write-Host "Signing enabled for local installer build." -ForegroundColor Yellow - $env:FILESTOSIGNLATER = $null - } - else { - $defaultSignList = Join-Path $PSScriptRoot "Output\files-to-sign.txt" - if ([string]::IsNullOrWhiteSpace($env:FILESTOSIGNLATER)) { - $env:FILESTOSIGNLATER = $defaultSignList - } - Write-Host "Signing disabled for local build; capturing files to $env:FILESTOSIGNLATER" -ForegroundColor Yellow - } - } - - $installerCleanArg = "/p:InstallerCleanProductOutputs=false" - if ($isGitHubActions) { - $installerCleanArg = "/p:InstallerCleanProductOutputs=true" - } - - Invoke-MSBuild ` - -Arguments @('Build/InstallerBuild.proj', '/t:BuildInstaller', "/p:Configuration=$Configuration", "/p:Platform=$Platform", '/p:config=release', "/p:InstallerToolset=$InstallerToolset", $installerCleanArg) ` - -Description 'Installer Build' - - Write-Host "[OK] Installer build complete!" -ForegroundColor Green - } - } - - # ============================================================================= - # Test Execution (Optional) - # ============================================================================= - - if ($RunTests) { - Write-Host "" - Write-Host "Running tests..." -ForegroundColor Cyan - - $testArgs = @("-Configuration", $Configuration, "-NoBuild") - if ($TestFilter) { - $testArgs += @("-TestFilter", $TestFilter) - } - - Stop-ConflictingProcesses @cleanupArgs - & "$PSScriptRoot\test.ps1" @testArgs - $testExitCode = $LASTEXITCODE - if ($testExitCode -ne 0) { - Write-Warning "Some tests failed. Check output above for details." - } - } + Invoke-WithFileLockRetry -Context "FieldWorks build" -IncludeOmniSharp -Action { + # Initialize Visual Studio Developer environment + Initialize-VsDevEnvironment + Test-CvtresCompatibility + + # Set architecture environment variable (x64-only) + $env:arch = 'x64' + Write-Host "Set arch environment variable to: $env:arch" -ForegroundColor Green + + # Stop conflicting processes before the build + Stop-ConflictingProcesses @cleanupArgs + + $projectPath = $Project + $rootedProjectPath = Join-Path $PSScriptRoot $Project + if (-not (Test-Path $projectPath) -and (Test-Path $rootedProjectPath)) { + $projectPath = $rootedProjectPath + } + if (-not (Test-Path $projectPath)) { + throw "Project path '$Project' was not found. Pass a path relative to the repo root or an absolute path." + } + + # Clean stale per-project obj/ folders + Remove-StaleObjFolders -RepoRoot $PSScriptRoot + + # LT-22382: Remove stale first-party DLLs from the output directory before building. + # Uses a whitelist of assembly names from Src/**/*.csproj to identify FW assemblies + # and checks their major version against FWMAJOR. See Build\Agent\Remove-StaleDlls.ps1. + $staleDllScript = Join-Path $PSScriptRoot "Build\Agent\Remove-StaleDlls.ps1" + if (Test-Path $staleDllScript) { + $outputDir = "Output\$Configuration" + & $staleDllScript -OutputDir $outputDir -RepoRoot $PSScriptRoot -Verbose:$VerbosePreference + } + + # ============================================================================= + # Build Configuration + # ============================================================================= + + # Determine logical core count for CL_MPCount + if ($env:CL_MPCount) { + $mpCount = $env:CL_MPCount + } + else { + $mpCount = 8 + if ($env:NUMBER_OF_PROCESSORS) { + $procCount = [int]$env:NUMBER_OF_PROCESSORS + if ($procCount -lt 8) { $mpCount = $procCount } + } + } + + # Construct MSBuild arguments + $finalMsBuildArgs = @() + + # Parallelism + if (-not $Serial) { + $finalMsBuildArgs += "/m" + } + + # Verbosity & Logging + $finalMsBuildArgs += "/v:$Verbosity" + $finalMsBuildArgs += "/nologo" + $finalMsBuildArgs += "/consoleloggerparameters:Summary" + + # Node Reuse + $finalMsBuildArgs += "/nr:$($NodeReuse.ToString().ToLower())" + + # Properties + $finalMsBuildArgs += "/p:Configuration=$Configuration" + $finalMsBuildArgs += "/p:Platform=$Platform" + if ($SkipNative) { + $finalMsBuildArgs += "/p:SkipNative=true" + } + if ($TraceCrashes) { + $finalMsBuildArgs += "/p:UseDevTraceConfig=true" + } + $finalMsBuildArgs += "/p:CL_MPCount=$mpCount" + if ($env:FW_TRACE_LOG) { + $finalMsBuildArgs += "/p:FW_TRACE_LOG=`"$($env:FW_TRACE_LOG)`"" + } + + if ($BuildTests -or $RunTests) { + $finalMsBuildArgs += "/p:BuildTests=true" + $finalMsBuildArgs += "/p:BuildNativeTests=true" + } + + if ($BuildAdditionalApps) { + $finalMsBuildArgs += "/p:BuildAdditionalApps=true" + } + + # Add user-supplied args + $finalMsBuildArgs += $MsBuildArgs + + # ============================================================================= + # Build Execution + # ============================================================================= + + Write-Host "" + Write-Host "Building FieldWorks..." -ForegroundColor Cyan + Write-Host "Project: $projectPath" -ForegroundColor Cyan + Write-Host "Configuration: $Configuration | Platform: $Platform | Parallel: $(-not $Serial) | Tests: $($BuildTests -or $RunTests)" -ForegroundColor Cyan + + if ($BuildAdditionalApps) { + Write-Host "Including optional FieldWorks executables" -ForegroundColor Yellow + } + + # Bootstrap: Build FwBuildTasks first (required by SetupInclude.targets) + $fwBuildTasksOutputDir = Join-Path $PSScriptRoot "BuildTools/FwBuildTasks/$Configuration/" + Invoke-MSBuild ` + -Arguments @('Build/Src/FwBuildTasks/FwBuildTasks.csproj', '/t:Restore;Build', "/p:Configuration=$Configuration", "/p:Platform=$Platform", ` + "/p:FwBuildTasksOutputPath=$fwBuildTasksOutputDir", "/p:SkipFwBuildTasksAssemblyCheck=true", "/p:SkipFwBuildTasksUsingTask=true", "/p:SkipGenerateFwTargets=true", ` + "/p:SkipSetupTargets=true", "/v:quiet", "/nologo") ` + -Description 'FwBuildTasks (Bootstrap)' + + if (-not (Test-Path $fwTasksSourcePath)) { + throw "Failed to build FwBuildTasks. Expected $fwTasksSourcePath to exist." + } + + if ($fwTasksSourcePath -ne $fwTasksDropPath) { + $dropDir = Split-Path $fwTasksDropPath -Parent + if (-not (Test-Path $dropDir)) { + New-Item -Path $dropDir -ItemType Directory -Force | Out-Null + } + Copy-Item -Path $fwTasksSourcePath -Destination $fwTasksDropPath -Force + } + + # Restore packages + if (-not $SkipRestore) { + Write-Host "Restoring NuGet packages..." -ForegroundColor Cyan + $packagesDir = Join-Path $PSScriptRoot "packages" + if (-not (Test-Path $packagesDir)) { + New-Item -Path $packagesDir -ItemType Directory -Force | Out-Null + } + & dotnet restore "$PSScriptRoot\FieldWorks.sln" /p:NoWarn=NU1903 /p:DisableWarnForInvalidRestoreProjects=true "/p:Configuration=$Configuration" "/p:Platform=$Platform" --verbosity quiet + if ($LASTEXITCODE -ne 0) { + throw "NuGet package restore failed for FieldWorks.sln" + } + Write-Host "Package restore complete." -ForegroundColor Green + } else { + Write-Host "Skipping package restore (-SkipRestore)" -ForegroundColor Yellow + } + + if ($InstallerOnly) { + if (-not $BuildInstaller -and -not $BuildPatch) { + throw "-InstallerOnly requires -BuildInstaller or -BuildPatch." + } + + $stampPath = Get-BuildStampPath -RepoRoot $PSScriptRoot -ConfigurationName $Configuration + if (-not (Test-Path $stampPath)) { + throw "-InstallerOnly requested but no build stamp was found at '$stampPath'. Run a full build once (without -InstallerOnly) to create it." + } + + $stamp = Get-Content -LiteralPath $stampPath -Raw | ConvertFrom-Json + $current = Get-RepoStamp + + $stampConfig = $stamp.Configuration + $stampPlatform = $stamp.Platform + if (($stampConfig -ne $Configuration) -or ($stampPlatform -ne $Platform)) { + throw "-InstallerOnly stamp mismatch: stamp is Configuration='$stampConfig' Platform='$stampPlatform' but this run is Configuration='$Configuration' Platform='$Platform'. Run a full build in this configuration/platform." + } + + $headChanged = ($stamp.GitHead -ne $current.GitHead) + if ((-not $ForceInstallerOnly) -and ($headChanged -or $current.IsDirtyOutsideInstaller)) { + throw "-InstallerOnly refused: product inputs may have changed since the last full build stamp. Run a full build, or use -ForceInstallerOnly if you are sure Output\\$Configuration is still correct." + } + + Write-Host "" + Write-Host "Skipping product build (-InstallerOnly). Reusing Output\\$Configuration." -ForegroundColor Yellow + } + else { + # Build using traversal project + Invoke-MSBuild ` + -Arguments (@($projectPath) + $finalMsBuildArgs) ` + -Description "FieldWorks Solution" ` + -LogPath $LogFile ` + -TailLines $TailLines + + $stampDir = Join-Path $PSScriptRoot ("Output\\{0}" -f $Configuration) + if (-not (Test-Path $stampDir)) { + New-Item -Path $stampDir -ItemType Directory -Force | Out-Null + } + + $repoStamp = Get-RepoStamp + $stampObject = [pscustomobject]@{ + Configuration = $Configuration + Platform = $Platform + GitHead = $repoStamp.GitHead + IsDirty = $repoStamp.IsDirty + IsDirtyOutsideInstaller = $repoStamp.IsDirtyOutsideInstaller + TimestampUtc = (Get-Date).ToUniversalTime().ToString('o') + } + + $stampPath = Get-BuildStampPath -RepoRoot $PSScriptRoot -ConfigurationName $Configuration + $stampObject | ConvertTo-Json -Depth 4 | Set-Content -LiteralPath $stampPath -Encoding UTF8 + + Write-Host "" + Write-Host "[OK] Build complete!" -ForegroundColor Green + Write-Host "Output: Output\$Configuration" -ForegroundColor Cyan + } + + # Copy local LCM assemblies if requested + if ($UseLocalLcm) { + Write-Host "" + Write-Host "Applying local LCM assemblies..." -ForegroundColor Cyan + + $lcmCopyScript = Join-Path $PSScriptRoot "scripts\Agent\Copy-LocalLcm.ps1" + $lcmArgs = @{ + Configuration = $Configuration + BuildLcm = $true + SkipConfirm = $true + } + if ($LocalLcmPath) { + $lcmArgs['LcmRoot'] = $LocalLcmPath + } + + & $lcmCopyScript @lcmArgs + if ($LASTEXITCODE -ne 0) { + throw "Failed to copy local LCM assemblies." + } + } + + if ($BuildInstaller -or $BuildPatch) { + if ($BuildPatch) { + $BaseOrPatch = "Patch" + } + else { + $BaseOrPatch = "Installer" + } + Write-Host "" + Write-Host "Building $BaseOrPatch..." -ForegroundColor Cyan + + if (-not $isGitHubActions) { + if ($SignInstaller) { + Write-Host "Signing enabled for local installer build." -ForegroundColor Yellow + $env:FILESTOSIGNLATER = $null + } + else { + $defaultSignList = Join-Path $PSScriptRoot "Output\files-to-sign.txt" + if ([string]::IsNullOrWhiteSpace($env:FILESTOSIGNLATER)) { + $env:FILESTOSIGNLATER = $defaultSignList + } + Write-Host "Signing disabled for local build; capturing files to $env:FILESTOSIGNLATER" -ForegroundColor Yellow + } + } + + $installerCleanArg = "/p:InstallerCleanProductOutputs=false" + if ($isGitHubActions) { + $installerCleanArg = "/p:InstallerCleanProductOutputs=true" + } + + Invoke-MSBuild ` + -Arguments (@('Build/InstallerBuild.proj', '/t:BuildInstaller', "/p:Configuration=$Configuration", "/p:Platform=$Platform", '/p:config=release', ` + "/p:InstallerToolset=$InstallerToolset", $installerCleanArg) + $MsBuildArgs) ` + -Description '$BaseOrPatch Build' + + Write-Host "[OK] $BaseOrPatch build complete!" -ForegroundColor Green + } +# else { if ($BuildPatch) { +# Write-Host "" +# Write-Host "Building Patch Installer..." -ForegroundColor Cyan +# +# if (-not $isGitHubActions) { +# if ($SignInstaller) { +# Write-Host "Signing enabled for local installer build." -ForegroundColor Yellow +# $env:FILESTOSIGNLATER = $null +# } +# else { +# $defaultSignList = Join-Path $PSScriptRoot "Output\files-to-sign.txt" +# if ([string]::IsNullOrWhiteSpace($env:FILESTOSIGNLATER)) { +# $env:FILESTOSIGNLATER = $defaultSignList +# } +# Write-Host "Signing disabled for local build; capturing files to $env:FILESTOSIGNLATER" -ForegroundColor Yellow +# } +# } +# +# $installerCleanArg = "/p:InstallerCleanProductOutputs=false" +# if ($isGitHubActions) { +# $installerCleanArg = "/p:InstallerCleanProductOutputs=true" +# } +# +# Invoke-MSBuild ` +# -Arguments @('Build/InstallerBuild.proj', '/t:BuildPatch', "/p:Configuration=$Configuration", "/p:Platform=$Platform", '/p:config=release', +# "/p:InstallerToolset=$InstallerToolset", $installerCleanArg) ` +# -Description 'Patch Installer Build' +# +# Write-Host "[OK] Patch Installer build complete!" -ForegroundColor Green +# }} + } + + # ============================================================================= + # Test Execution (Optional) + # ============================================================================= + + if ($RunTests) { + Write-Host "" + Write-Host "Running tests..." -ForegroundColor Cyan + + $testArgs = @("-Configuration", $Configuration, "-NoBuild") + if ($TestFilter) { + $testArgs += @("-TestFilter", $TestFilter) + } + + Stop-ConflictingProcesses @cleanupArgs + & "$PSScriptRoot\test.ps1" @testArgs + $testExitCode = $LASTEXITCODE + if ($testExitCode -ne 0) { + Write-Warning "Some tests failed. Check output above for details." + } + } } finally { - # Kill any lingering build processes that might hold file locks - Stop-ConflictingProcesses @cleanupArgs + # Kill any lingering build processes that might hold file locks + Stop-ConflictingProcesses @cleanupArgs } if ($testExitCode -ne 0) { - exit $testExitCode + exit $testExitCode } From 5d869ede3b61042f623ca8e1f3fea0f770d6764d Mon Sep 17 00:00:00 2001 From: Hasso Date: Wed, 25 Feb 2026 15:14:04 -0600 Subject: [PATCH 2/7] fix dead link in readme --- Build/FieldWorks.targets | 3289 +++++++++++++++++ ReadMe.md | 2 +- specs/001-wix-v6-migration/BUNDLE_UI.md | 268 ++ .../HYPERV_INSTALLER_TESTING.md | 184 + .../HYPERV_INSTRUCTIONS.md | 176 + .../REMAINING_WIX6_ISSUES.md | 53 + .../WIX3_TO_WIX6_COMPARISON.md | 181 + .../WIX_MIGRATION_STATUS.md | 28 + .../checklists/requirements.md | 34 + .../contracts/hyperv-runner.sample.json | 26 + specs/001-wix-v6-migration/data-model.md | 46 + .../golden-install-checklist.md | 142 + specs/001-wix-v6-migration/parity-check.md | 267 ++ specs/001-wix-v6-migration/plan.md | 116 + specs/001-wix-v6-migration/quickstart.md | 84 + specs/001-wix-v6-migration/research.md | 72 + specs/001-wix-v6-migration/spec.md | 162 + specs/001-wix-v6-migration/tasks.md | 195 + .../verification-matrix.md | 55 + .../wix3-to-wix6-audit.md | 145 + 20 files changed, 5524 insertions(+), 1 deletion(-) create mode 100644 Build/FieldWorks.targets create mode 100644 specs/001-wix-v6-migration/BUNDLE_UI.md create mode 100644 specs/001-wix-v6-migration/HYPERV_INSTALLER_TESTING.md create mode 100644 specs/001-wix-v6-migration/HYPERV_INSTRUCTIONS.md create mode 100644 specs/001-wix-v6-migration/REMAINING_WIX6_ISSUES.md create mode 100644 specs/001-wix-v6-migration/WIX3_TO_WIX6_COMPARISON.md create mode 100644 specs/001-wix-v6-migration/WIX_MIGRATION_STATUS.md create mode 100644 specs/001-wix-v6-migration/checklists/requirements.md create mode 100644 specs/001-wix-v6-migration/contracts/hyperv-runner.sample.json create mode 100644 specs/001-wix-v6-migration/data-model.md create mode 100644 specs/001-wix-v6-migration/golden-install-checklist.md create mode 100644 specs/001-wix-v6-migration/parity-check.md create mode 100644 specs/001-wix-v6-migration/plan.md create mode 100644 specs/001-wix-v6-migration/quickstart.md create mode 100644 specs/001-wix-v6-migration/research.md create mode 100644 specs/001-wix-v6-migration/spec.md create mode 100644 specs/001-wix-v6-migration/tasks.md create mode 100644 specs/001-wix-v6-migration/verification-matrix.md create mode 100644 specs/001-wix-v6-migration/wix3-to-wix6-audit.md diff --git a/Build/FieldWorks.targets b/Build/FieldWorks.targets new file mode 100644 index 0000000000..a8b474e2df --- /dev/null +++ b/Build/FieldWorks.targets @@ -0,0 +1,3289 @@ + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + + + + + + + DEBUG CODE_ANALYSIS + + + + + DEBUG CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG CODE_ANALYSIS + + + + + DEBUG CODE_ANALYSIS + + + + + + + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + + + + + + + DEBUG CODE_ANALYSIS + + + + + DEBUG CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG CODE_ANALYSIS + + + + + DEBUG CODE_ANALYSIS + + + + + + + + + + + + + + + + + + + + DEBUG CODE_ANALYSIS + + + + + DEBUG CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG CODE_ANALYSIS + + + + + DEBUG CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG CODE_ANALYSIS + + + + + DEBUG CODE_ANALYSIS + + + + + + + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG CODE_ANALYSIS + + + + + + + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + + + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + TRACE CODE_ANALYSIS + + + + + DEBUG TRACE CODE_ANALYSIS + + + + + + + + + + + + + + diff --git a/ReadMe.md b/ReadMe.md index e054e079c9..37c9382a5e 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -66,7 +66,7 @@ WiX 3 artifacts are produced under `FLExInstaller/bin/x64//` (MSI under WiX 6 artifacts are produced under `FLExInstaller/wix6/bin/x64//` (MSI under `en-US/`). -For more details, see [specs/001-wix-v6-migration/quickstart.md (TODO: DEAD LINK)](specs/001-wix-v6-migration/quickstart.md). +For more details, see [specs/001-wix-v6-migration/quickstart.md](specs/001-wix-v6-migration/quickstart.md). ### Code signing for local installer builds diff --git a/specs/001-wix-v6-migration/BUNDLE_UI.md b/specs/001-wix-v6-migration/BUNDLE_UI.md new file mode 100644 index 0000000000..f2d735fab3 --- /dev/null +++ b/specs/001-wix-v6-migration/BUNDLE_UI.md @@ -0,0 +1,268 @@ +# Bundle UI (WiX v6) — FieldWorks + +This document describes the desired UI flow for `FieldWorksBundle.exe` and provides a checklist of changes needed to implement that flow starting from the current bundle UI state in this worktree. + +## Scope + +- Applies to the **online** bundle authored in [FLExInstaller/Shared/Base/Bundle.wxs](../../FLExInstaller/Shared/Base/Bundle.wxs). +- Uses **WixStdBA (WixStandardBootstrapperApplication)** with a custom theme authored in [FLExInstaller/Shared/Base/BundleTheme.xml](../../FLExInstaller/Shared/Base/BundleTheme.xml) + [FLExInstaller/Shared/Base/BundleTheme.wxl](../../FLExInstaller/Shared/Base/BundleTheme.wxl). +- Goal: **mirror the MSI UI** as closely as practical while still letting Burn handle prerequisites, detection, logging, and restart. + +## Reference screenshots (expected UX) + +These screenshots reflect the desired end-user experience and should be used as the visual reference when implementing/adjusting the theme and bundle metadata. + +### Bundle (first screen) — Welcome + +- Window title: “FieldWorks Language Explorer ” +- Main text: “To install FieldWorks Language Explorer , agree to the license terms, and then click Install.” +- Includes: + - **license terms** hyperlink + - **I agree to the license terms** checkbox (gates Install) +- Buttons: + - **Install** (shows elevation shield when required) + - **Close** + +### MSI (Destination Folders) + +The MSI UI shows two destination folders. The expected defaults are: + +- Program folder: `C:\Program Files\SIL\FieldWorks 9\` +- Projects folder: `C:\ProgramData\SIL\FieldWorks\Projects\` + +### Bundle (last screen) — Completed + +- “Operation Completed” +- **Close** button + +## Current state (baseline) + +### What the bundle shows today + +- The bundle uses WixStdBA `Theme="hyperlinkLicense"` with a custom `ThemeFile` + `LocalizationFile`. +- The theme includes the standard WixStdBA pages: + - `Loading`, `Install` (with EULA hyperlink + checkbox), `Options` (install folder), `Progress`, `Modify`, plus `Success`/`Failure` pages (later in the file). +- The window/background is built as a template `BundleThemeTemplate.wxi`: + - `logo.png`. + +### What the chain does today + +- The chain includes prerequisites and the MSI: + - `.NET 4.8 web` package group + - `vcredists` package group + - `AppMsiPackage` (FieldWorks MSI) +- In [FLExInstaller/Shared/Base/Bundle.wxs](../../FLExInstaller/Shared/Base/Bundle.wxs), the `AppMsiPackage` currently has: + - `Visible="no"` + - MSI internal UI is enabled for **full UI** runs (see note below) + - MSI logging captured via `LogPathVariable="WixBundleLog_AppMsiPackage"`. + +- Bundle-level Options UI is suppressed via `WixStdBASuppressOptionsUI=1` so the MSI is the single source of truth for directory/feature selection. + +**Note:** Attempting to author `DisplayInternalUI="yes"` on `MsiPackage` fails with WiX v6 in this repo (error `WIX0004`: unexpected attribute). This remains true even after upgrading the installer projects to WiX v6.0.2. + +The WiX v6 mechanism for showing MSI internal UI from a bundle is `bal:DisplayInternalUICondition` on `MsiPackage`. + +- Current bundle authoring uses: `bal:DisplayInternalUICondition="WixBundleUILevel >= 4"` + - This turns on MSI internal UI only for **full UI** runs. + - It avoids MSI dialogs in `/passive` and `/quiet` runs (runtime verification still required). + +Reference docs: +- https://docs.firegiant.com/wix/schema/wxs/msipackage/ (see `DisplayInternalUICondition`) +- https://docs.firegiant.com/wix/tools/burn/builtin-variables/ (see `WixBundleUILevel`) + +Attempting to force `REBOOT=ReallySuppress` via `MsiProperty` also fails (error `WIX0365`: `REBOOT` is bootstrapper-controlled). Enabling true MSI dialog UI from the bundle therefore requires either: + +- upgrading the WiX toolset packages to a version where `DisplayInternalUI` is supported in this project, or +- switching to the “bundle-only UI styled to match MSI” approach (Option B). + +### Payload staging constraint (important) + +WixStdBA binds theme resources by *filename* at runtime. This worktree stages flat-named copies of the theme and its assets into the culture output folder via [FLExInstaller/wix6/FieldWorks.Bundle.wixproj](../../FLExInstaller/wix6/FieldWorks.Bundle.wixproj) target `StageBundlePayloads`. + +That staging approach is required for stability of any custom theme/asset work. + +## Target UI approach + +There are two viable approaches. The recommended one is **Option A**. + +### Option A (recommended): Burn handles prereqs + MSI shows internal UI + +In this approach: + +- Burn/WixStdBA shows a **minimal shell UI** (welcome/prep, prereq progress, completion). +- When it is time to install/repair/uninstall FieldWorks, Burn launches the MSI with **internal UI enabled**, so the user sees the real MSI dialogs (directory selection, features, etc.). + +This provides the closest parity to the MSI UI with the least custom theming work. + +#### Important behavior note + +With WixStdBA, MSI internal UI is shown in the **Windows Installer UI window** (msiexec) rather than being embedded in the bootstrapper window. Expect “two windows” during the handoff. + +### Option B: Keep bundle-only UI and only style it to look like MSI + +This is the current direction of the custom theme: MSI-like background, sizes, and copy. This can be kept even if Option A is implemented, because Option A still needs a small bootstrapper shell. + +## Recommended interactive UI flow (Option A) + +This section is the concrete “optimal flow” for **Full UI** runs (the normal interactive case). + +### 1) Startup / detection + +- Bundle starts and detects: + - whether prerequisites are already present + - whether FieldWorks is installed (to decide between `Install` vs `Modify` pages) + +UI: +- Show `Loading` briefly (or a quiet welcome page if you want to avoid a “blank” feel). + +### 2) Install (first-time install) + +UI: +- Show the Welcome bundle screen (per the reference screenshot): + - prerequisites may be installed first + - then the FieldWorks MSI installer UI will open + +License: +- Keep the **license terms** hyperlink and the **I agree to the license terms** checkbox. +- The Install action should be gated until the checkbox is checked. + +Buttons: +- `Install` +- `Close` + +### 3) Prerequisites phase + +UI: +- Show the bundle `Progress` page while Burn installs: + - .NET 4.8 (if needed) + - VC++ redists (if needed) + +Behavior: +- If prerequisites require a reboot, the bundle may need to resume (depending on package behaviors). + +### 4) FieldWorks install phase (MSI internal UI) + +At the point Burn reaches `AppMsiPackage`: + +- MSI internal UI opens (MSI welcome + directory dialogs + feature selection + progress). +- **Bundle window behavior:** keep the bundle UI open and visible directly behind the MSI UI window while MSI internal UI is running. + - The bundle should not minimize itself or jump in front of the MSI UI. + +MSI destination folders (defaults shown in the MSI UI): + +- Program folder: `C:\Program Files\SIL\FieldWorks 9\` +- Projects folder: `C:\ProgramData\SIL\FieldWorks\Projects\` + +### 5) Completion + +UI: +- On success, show `Success` page. +- On failure, show `Failure` page with a link to the bundle log and/or the MSI log. + +Restart messaging: +- Only show restart text/buttons when `WixStdBARestartRequired` is true. +- Do not gate on “RebootPending” alone. + +### 6) Maintenance runs (Repair / Uninstall) + +If FieldWorks is already installed: + +UI: +- Show the bundle `Modify` page with: + - `Repair` (optional) + - `Uninstall` + +Behavior: +- Clicking `Repair` or `Uninstall` launches MSI internal UI in maintenance mode. + +## Non-interactive modes + +These must continue to work: + +- `/quiet`: no UI +- `/passive`: minimal UI + +Key requirement: +- MSI internal UI must not appear in `/quiet` and should not appear in `/passive`. + +(Implementation note: the bundle currently gates `bal:DisplayInternalUICondition` on `WixBundleUILevel >= 4` (intended to mean full UI only). This still needs explicit verification.) + +## Implementation checklist (from current state) + +### Branding and identity requirements + +- [ ] **Elevation prompt branding:** when Windows prompts for elevation (UAC), the prompt should display program name **“FieldWorks Installer”** and show the cube logo. +- [ ] **ARP display name:** after install, Add/Remove Programs should list **“FieldWorks Language Explorer ”**. +- [ ] **Window title:** the Welcome bundle window title should display **“FieldWorks Language Explorer ”**. + +### A) Bundle authoring changes (Bundle.wxs) + +- [x] Enable MSI internal UI for the FieldWorks MSI package (WiX v6 approach). + - Implemented using `bal:DisplayInternalUICondition` on `MsiPackage`. + - Currently set to `WixBundleUILevel >= 4` (intended to be full UI only). +- [ ] Decide whether to suppress MSI-initiated reboots. + - `REBOOT` cannot be authored via `MsiProperty` (`WIX0365`). + - If MSI internal UI becomes possible via toolset upgrade, verify reboot prompting behavior via `/norestart` handling. +- [x] Keep MSI logging. + - Retain `LogPathVariable="WixBundleLog_AppMsiPackage"`. + - (Still verify) the failure page hyperlink points at a useful log (bundle log + MSI log, if available). + +- [x] Hide bundle-level Options UI to avoid conflicting UX. + - Set `WixStdBASuppressOptionsUI=1`. + +### B) Theme changes (BundleTheme.xml / BundleTheme.wxl) + +- [ ] Align the Welcome bundle screen with the reference screenshot: + - Keep the license hyperlink and acceptance checkbox. + - Welcome copy should explain prerequisites → then MSI UI. +- [x] Make the Install page button label/copy reflect the handoff. + - Suggested: keep button text “Install”, but change the page text to mention the MSI installer will open. +- [x] Keep restart UI gated on `WixStdBARestartRequired`. + - Restart-related controls in `Success`/`Failure` use `VisibleCondition="WixStdBARestartRequired"`. +- [x] Options page decision: hide it. + - MSI internal UI owns install paths and feature selection; bundle Options is suppressed. + +### C) Build/payload staging validation + +- [x] Confirm [FLExInstaller/wix6/FieldWorks.Bundle.wixproj](../../FLExInstaller/wix6/FieldWorks.Bundle.wixproj) `StageBundlePayloads` continues to stage all theme assets needed by the theme: + - `BundleTheme.xml`, `BundleTheme.wxl`, `logo.png`, `License.htm`, and any bitmap assets referenced by the theme. +- [x] Confirm installer build uses the intended WiX v6.0.2 toolset and extensions. + - Recent `build.ps1 -BuildInstaller` output shows `wix.exe` and all `-ext` paths coming from `packages\\wixtoolset.*\\6.0.2\\...`. +- [ ] Ensure any newly referenced images in the theme are staged by filename into the culture output directory. + +### D) Test checklist + +**Important:** per project practice, remove older FieldWorks installs before each test run. + +- [ ] Fresh machine test (no FieldWorks installed): + - Run bundle normally. + - Verify prereqs install (or are detected). + - Verify MSI UI opens and completes install. + - Verify MSI destination defaults: + - `C:\Program Files\SIL\FieldWorks 9\` + - `C:\ProgramData\SIL\FieldWorks\Projects\` + - Verify the bundle window stays open behind the MSI UI. + - Verify Add/Remove Programs entry is “FieldWorks Language Explorer ”. + - Verify bundle completion page shows and exit code is success. +- [ ] Upgrade test (older FieldWorks installed): + - Verify the expected upgrade/uninstall behavior. + - Ensure UI copy remains accurate (no misleading “clean install” messaging). +- [ ] Repair/uninstall test: + - Run bundle again and use `Repair` and `Uninstall`. + - Verify MSI UI opens in maintenance mode. +- [ ] Restart behavior test: + - Use a scenario that triggers a reboot requirement. + - Verify restart UI appears only when `WixStdBARestartRequired` is set. +- [ ] Quiet/passive tests: + - Run `FieldWorksBundle.exe /quiet` and confirm no UI (including no MSI UI) and correct exit codes. + - Run `FieldWorksBundle.exe /passive` and confirm no MSI internal UI appears. + +## Notes / known gaps + +- The offline bundle in this repo ([FLExInstaller/Shared/Base/OfflineBundle.wxs](../../FLExInstaller/Shared/Base/OfflineBundle.wxs)) is not currently wired into the WiX v6 build outputs. +- `DisplayInternalUI` (legacy attribute) is not supported by WiX v6 schema here; use `bal:DisplayInternalUICondition` instead. +- Runtime behavior still needs verification for: + - normal interactive install + - `/quiet` (no UI) + - `/passive` (minimal UI, no MSI dialogs) +- This document describes the desired UI flow; it does not decide branding/art layout beyond what is already in the current theme. diff --git a/specs/001-wix-v6-migration/HYPERV_INSTALLER_TESTING.md b/specs/001-wix-v6-migration/HYPERV_INSTALLER_TESTING.md new file mode 100644 index 0000000000..862b9e7459 --- /dev/null +++ b/specs/001-wix-v6-migration/HYPERV_INSTALLER_TESTING.md @@ -0,0 +1,184 @@ +# Hyper-V Installer Parity Testing (Deterministic) + +This document describes the deterministic clean-machine verification lane for comparing WiX3 vs WiX6 installer behavior using a Hyper-V VM restored from a checkpoint. + +## Prerequisites + +- Windows Pro/Enterprise with Hyper-V enabled. +- A local Hyper-V VM (Windows 10/11 Pro/Enterprise or Server) that supports PowerShell Direct. +- A checkpoint created for a clean baseline state (no FieldWorks installed). +- Local admin credentials inside the VM (PowerShell Direct). + +Host permissions: + +- Your Windows user should be in the local `Hyper-V Administrators` group to manage VMs without running everything elevated. +- Some host-side disk diagnostics (e.g., `Mount-VHD` while debugging a broken VM boot) require an elevated PowerShell. + +To set up the recommended host permissions (idempotent): + +```powershell +./scripts/Agent/Setup-HyperVHostPermissions.ps1 +``` + +If it adds you to a group, log off/on (or reboot) before rerunning parity, otherwise existing processes may not see the new membership. + +## One-time VM setup + +If you do not already have a Hyper-V VM, you can create one from a Windows ISO. + +Note: there is no built-in way to obtain a "windows-latest" image locally; you must provide a Windows 10/11 ISO (or an evaluation ISO) and complete Windows setup in the VM. + +### Optional: download + cache an ISO + +If you already have a direct ISO URL (for example, from a Microsoft download flow), you can cache it locally: + +```powershell +./scripts/Agent/Get-WindowsIso.ps1 -IsoUrl "https://example.invalid/Windows.iso" +``` + +If you use Fido to obtain the download URL, do not vendor it into this repo (Fido is GPLv3). Instead, download it separately and point the helper at your local copy: + +```powershell +./scripts/Agent/Get-WindowsIso.ps1 -FidoPath "C:\Tools\Fido.ps1" -Win "Windows 11" -Rel Latest -Lang "English" -Arch x64 +``` + +Alternatively, you can let the helper auto-download Fido into a local cache folder (not committed). This requires an explicit GPL acknowledgement: + +```powershell +./scripts/Agent/Get-WindowsIso.ps1 -AutoDownloadFido -AllowGplFido -Win "Windows 11" -Rel Latest -Lang "English" -Arch x64 +``` + +Tip: run Fido with `-Lang List` to see valid language values. + +By default, ISOs are cached under `%ProgramData%\FieldWorks\HyperV\ISOs`. + +```powershell +# Create the VM from an ISO (Generation 2) +./scripts/Agent/New-HyperVTestVm.ps1 -VMName FWInstallerTest -IsoPath "C:\Path\To\Windows.iso" -VhdSizeGB 80 -MemoryStartupBytes 4294967296 -ProcessorCount 2 -StartVm +``` + +Alternatively, if you have a direct ISO URL, you can have the script download into the cache automatically: + +```powershell +./scripts/Agent/New-HyperVTestVm.ps1 -VMName FWInstallerTest -IsoUrl "https://example.invalid/Windows.iso" -StartVm +``` + +If you do not have a direct ISO URL, you can have the VM creation script use Fido to obtain one and download the ISO (Fido is GPLv3; not vendored here). This requires an explicit acknowledgement: + +```powershell +./scripts/Agent/New-HyperVTestVm.ps1 -VMName FWInstallerTest -UseFido -AllowGplFido -StartVm +``` + +Note: do not delete the ISO until Windows installation is complete and the VM no longer needs to boot from DVD. + +1) Create a Windows VM in Hyper-V. +2) Boot it and complete OOBE. +3) Ensure WinRM/PowerShell is usable (PowerShell Direct does not require networking, but the guest must be running Windows). +4) Create a clean checkpoint: + +- Example VM name: `FWInstallerTest` +- Example checkpoint name: `FWInstallerTest_Clean` + +You can create the checkpoint using the helper script (recommended after Windows setup is complete and before installing FieldWorks): + +```powershell +./scripts/Agent/New-HyperVCleanCheckpoint.ps1 -VMName FWInstallerTest -CheckpointName FWInstallerTest_Clean -StopVmFirst +``` + +If the VM boots to the Hyper-V UEFI screen with **"No operating system was loaded"**, your OS disk is not bootable (often it is still RAW/unpartitioned because Windows was never installed). Fix by completing Windows installation inside the VM, then recreate the clean checkpoint. + +### Recreate the clean baseline checkpoint + +If the baseline checkpoint gets "dirty" (e.g., the VM was changed outside of the parity run), recreate it. + +1) Restore the VM to a known-good state (manually, via Hyper-V Manager), and ensure FieldWorks is not installed. +2) Then run: + +```powershell +./scripts/Agent/New-HyperVCleanBaseline.ps1 -VMName FWInstallerTest -CheckpointName FWInstallerTest_Clean -StopVmFirst -Replace -RemoveAutomaticCheckpoints -DisableAutomaticCheckpoints +``` + +Note: `New-HyperVCleanCheckpoint.ps1` / `New-HyperVCleanBaseline.ps1` now perform a host-side OS disk sanity check (and will refuse to checkpoint a RAW/uninitialized disk). If you are not running elevated, they will warn and skip that check. + +If you need to set up host permissions: + +```powershell +./scripts/Agent/Setup-HyperVHostPermissions.ps1 +``` + +Notes: +- This script only creates/removes checkpoints; it cannot automate Windows OOBE or ensure your VM is "clean". +- If you prefer to keep automatic checkpoints, omit `-RemoveAutomaticCheckpoints`. + +## Run parity (WiX3 baseline vs WiX6 candidate) + +If you only need a handful of runs (e.g., ~1–5) and PowerShell Direct authentication is getting in the way, use the manual Guest Service Interface workflow instead: + +- `specs/001-wix-v6-migration/HYPERV_INSTRUCTIONS.md` + +That workflow uses `Copy-VMFile` (Guest Service Interface) to stage installers + scripts into the VM and includes a one-time VM step to set ExecutionPolicy so Shift+Right-click → Run with PowerShell works reliably. + +Create a config file (sample): + +- specs/001-wix-v6-migration/contracts/hyperv-runner.sample.json + +Notes on the **WiX3 baseline**: + +- The parity runner can auto-download the FieldWorks 9.2.11 (WiX3-era) Windows installer from https://software.sil.org/fieldworks/download/fw-92/fw-9211/. +- The sample config sets `baseline.downloadPageUrl` and caches the EXE under `%ProgramData%\FieldWorks\HyperV\Installers\Wix3Baseline`. +- If you already have the installer EXE, you can point `baseline.installerPath` at your local copy and omit the download fields. + +Then run: + +```powershell +# From repo root +# Optional: avoid interactive credential prompts by setting the guest password in an env var. +# The config can reference it via guestCredential.passwordEnvVar. +$env:FW_HYPERV_GUEST_PASSWORD = '' + +./scripts/Agent/Invoke-HyperVInstallerParity.ps1 -ConfigPath specs/001-wix-v6-migration/contracts/hyperv-runner.sample.json -PublishToSpecEvidence -GenerateFixPlan +``` + +Note on elevation: + +- Parity runs should not require starting VS Code as Administrator if your user is in `Hyper-V Administrators`. +- If you need to debug VM boot failures by mounting the VHD on the host (`Mount-VHD`), use an elevated PowerShell / elevated VS Code. + +If the env var is not set, the runner will prompt via `Get-Credential` (interactive). For CI/non-interactive runs, pass `-RequireGuestPasswordEnvVar` to fail fast instead of prompting. + +Credential caching (recommended for local/dev): + +- By default, the runner will cache the guest credential locally (DPAPI-encrypted, CurrentUser scope) under `%LOCALAPPDATA%\FieldWorks\HyperV\Secrets`. +- This avoids hardcoding a password anywhere and avoids storing a plaintext password in a persistent environment variable. +- Disable with `-RememberGuestCredential:$false`. + +One-time setup helper (recommended): + +```powershell +./scripts/Agent/Set-HyperVGuestCredential.ps1 -VMName FWInstallerTest +``` + +Tip: when prompted for the username, use a local account format like `.\` (or `FwInstallerTest\`). The account should be a local administrator in the guest. + +### Validate configuration (no VM changes) + +Use this to confirm the VM/checkpoint and installer paths are correct before doing a real run: + +```powershell +./scripts/Agent/Invoke-HyperVInstallerParity.ps1 -ConfigPath specs/001-wix-v6-migration/contracts/hyperv-runner.sample.json -ValidateOnly +``` + +Note: `-ValidateOnly` does not auto-download the baseline installer unless you also pass `-ForceDownloadWix3Baseline`. + +### Outputs + +- Host evidence: + - `Output/InstallerEvidence/HyperV/Wix3//...` + - `Output/InstallerEvidence/HyperV/Wix6//...` +- Published spec evidence (optional): + - `specs/001-wix-v6-migration/evidence/Wix3//...` + - `specs/001-wix-v6-migration/evidence/Wix6//...` +- Diff report: + - under a `compare/` folder next to the WiX6 evidence directory +- Generated fix plan (optional): + - `specs/001-wix-v6-migration/wix6-parity-fix-plan-.md` diff --git a/specs/001-wix-v6-migration/HYPERV_INSTRUCTIONS.md b/specs/001-wix-v6-migration/HYPERV_INSTRUCTIONS.md new file mode 100644 index 0000000000..1d6f6de616 --- /dev/null +++ b/specs/001-wix-v6-migration/HYPERV_INSTRUCTIONS.md @@ -0,0 +1,176 @@ +# Hyper-V Installer Parity: Manual Staging + Evidence (Guest Service Interface) + +This is the simplest “I only need a few runs” path for comparing the WiX3 baseline installer vs the WiX6 candidate installer. + +Instead of PowerShell Direct (`New-PSSession -VMName ...`), it uses the Hyper-V **Guest Service Interface** to copy files into the guest, and then you run the installer + evidence capture script manually inside the VM. + +## Prerequisites + +- Hyper-V VM: `FwInstallerTest` +- Clean checkpoint: your clean baseline checkpoint (use `Get-VMSnapshot -VMName FwInstallerTest` to list names) +- Hyper-V Integration Service enabled: **Guest Service Interface** +- WiX6 candidate bundle built on the host: + - `FLExInstaller\bin\x64\Release\FieldWorksBundle.exe` +- WiX3 baseline installer present on the host (default expected path): + - `C:\ProgramData\FieldWorks\HyperV\Installers\Wix3Baseline\FieldWorks_9.2.11.1_Online_x64.exe` + +## What the helper scripts do + +- Host-side copier: `scripts\Agent\Copy-HyperVParityPayload.ps1` + - Copies the payload files into the VM: + - WiX3 baseline EXE + - WiX6 candidate EXE + - Guest evidence scripts (single-entry + double-click wrappers) +- Guest evidence scripts: + - Single-entry (CLI-friendly): `scripts\Agent\Guest\Invoke-InstallerParityEvidence.ps1` + - Double-click friendly: + - `scripts\Agent\Guest\Invoke-InstallerParityEvidence-Wix3.ps1` + - `scripts\Agent\Guest\Invoke-InstallerParityEvidence-Wix6.ps1` + - Shared implementation: `scripts\Agent\Guest\InstallerParityEvidence.Common.ps1` + +## Run loop (repeat per attempt) + +Fast path (host helper): + +```powershell +./scripts/Agent/Start-HyperVManualParityRun.ps1 -VMName FwInstallerTest -CheckpointName initial -ForceRestore + +# If your checkpoint is named differently (e.g., "installer"), use that name: +# ./scripts/Agent/Start-HyperVManualParityRun.ps1 -VMName FwInstallerTest -CheckpointName installer -ForceRestore +``` + +This restores the checkpoint, boots the VM, and stages the payload into the guest. + +## One-time VM prep (recommended): allow local PowerShell scripts + +If you see errors like “running scripts is disabled on this system” when you Shift+Right-click → **Run with PowerShell**, that’s Windows PowerShell’s **execution policy**. + +Do this once inside the VM, then take (or update) your clean checkpoint so you don’t have to fight it again. + +Inside the VM (run an elevated PowerShell): + +```powershell +Get-ExecutionPolicy -List + +# Recommended: allow local scripts; still blocks unsigned scripts that came from the internet. +Set-ExecutionPolicy -Scope LocalMachine -ExecutionPolicy RemoteSigned -Force + +# Optional: also set CurrentUser (helps if MachinePolicy is not in play) +Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned -Force + +Get-ExecutionPolicy -List +``` + +Notes: + +- `RemoteSigned` is usually the best tradeoff for a test VM: locally-created/copied scripts run; “downloaded from the internet” scripts still require unblocking/signing. +- If `MachinePolicy` is set, Group Policy is enforcing it; you’ll need to change the policy (or set it to Not Configured) to make `Set-ExecutionPolicy` stick. +- ExecutionPolicy is not a security boundary, but it *does* stop double-click/Run-with-PowerShell flows unless you set it. + +After setting the policy: + +1. Apply your clean checkpoint baseline (or clean the VM) +2. Create/update the clean checkpoint (so the policy change is preserved) + +For your setup, this is typically a checkpoint like `initial` or `installer`. + +### 1) Restore the checkpoint and start the VM + +In Hyper-V Manager: + +1. Select `FwInstallerTest` +2. Apply checkpoint `fresh-install` +3. Start the VM + +Wait until Windows reaches the logon screen. + +### 2) Copy baseline + candidate + script into the guest (host → guest) + +From repo root on the host: + +```powershell +./scripts/Agent/Copy-HyperVParityPayload.ps1 -VMName FwInstallerTest +``` + +This prints a `GuestRunRoot` like: + +- `C:\FWInstallerTest\ParityPayload\` + +and places these files there: + +- `Wix3Baseline.exe` +- `Wix6Candidate.exe` +- `Invoke-InstallerParityEvidence.ps1` +- `Invoke-InstallerParityEvidence-Wix3.ps1` +- `Invoke-InstallerParityEvidence-Wix6.ps1` +- `InstallerParityEvidence.Common.ps1` + +If your installer paths differ, pass overrides: + +```powershell +./scripts/Agent/Copy-HyperVParityPayload.ps1 ` + -VMName FwInstallerTest ` + -Wix3BaselineInstallerPath "C:\Path\To\FieldWorks_9.2.11.1_Online_x64.exe" ` + -Wix6CandidateInstallerPath "FLExInstaller\bin\x64\Release\FieldWorksBundle.exe" +``` + +### 3) Run the evidence script inside the VM + +You have two ways to run inside the VM: + +1) Shift+Right-click a mode-specific script and choose **Run with PowerShell** (it pauses at the end) +2) Run from an elevated PowerShell prompt + +Adjust `` to the folder printed by the copy script: + +Option A (Shift+Right-click → Run with PowerShell): + +- `C:\FWInstallerTest\ParityPayload\\Invoke-InstallerParityEvidence-Wix3.ps1` +- `C:\FWInstallerTest\ParityPayload\\Invoke-InstallerParityEvidence-Wix6.ps1` + +Option B (elevated PowerShell): + +```powershell +$payload = 'C:\FWInstallerTest\ParityPayload\' +Set-ExecutionPolicy -Scope Process Bypass -Force + +& "$payload\Invoke-InstallerParityEvidence-Wix3.ps1" + +# For clean parity, restore the checkpoint again before the Wix6 run. + +& "$payload\Invoke-InstallerParityEvidence-Wix6.ps1" +``` + +Each run prints a zip path like: + +- `EvidenceZip=C:\FWInstallerTest\evidence--.zip` + +### 4) Copy evidence out of the VM + +Hyper-V Guest Services are host→guest only (`Copy-VMFile`), so copy the zip back to the host using one of: + +- Enhanced Session Mode (clipboard + drive redirection) +- Remote Desktop (clipboard + drive redirection) +- SMB share (if you enabled networking) +- A “transfer disk” VHDX mounted in guest then mounted on host + +#### Clipboard / drive sharing prerequisites + +- Use a **Windows Pro** (or Enterprise) guest. Windows Home cannot act as an RDP host, which makes Remote Desktop (and thus easy clipboard/drive redirection) unavailable. +- Enable **Enhanced Session Mode** on the host: + - Hyper-V Manager → Hyper-V Settings… → Enhanced Session Mode Policy → **Allow enhanced session mode** + - Hyper-V Manager → Hyper-V Settings… → Enhanced Session Mode → **Use enhanced session mode** +- Enable **Remote Desktop** in the guest: + - Settings → System → Remote Desktop → **Enable Remote Desktop** + - Ensure the user you log in with is allowed to connect (Administrators are allowed by default) + - If prompted, allow Remote Desktop through Windows Firewall +- Windows Hello Conflict: If using Windows 10/11, go to Settings > Accounts > Sign-in options and turn off the requirement for "Windows Hello sign-in for Microsoft accounts". + +Suggested host destination: + +- `Output\InstallerEvidence\HyperV\Manual\\evidence-*.zip` + +## Notes + +- For true “clean machine” parity, restore `fresh-install` before each run. +- This manual path does not uninstall FieldWorks automatically. diff --git a/specs/001-wix-v6-migration/REMAINING_WIX6_ISSUES.md b/specs/001-wix-v6-migration/REMAINING_WIX6_ISSUES.md new file mode 100644 index 0000000000..5cb4cbb7f5 --- /dev/null +++ b/specs/001-wix-v6-migration/REMAINING_WIX6_ISSUES.md @@ -0,0 +1,53 @@ +# Remaining WiX 6 Issues + +This is a working TODO list of remaining issues for the WiX 6 installer, based on current observed behavior. + +## Upgrade over WiX 3 (keep settings, same install shape) + +Goal: installing the WiX 6 bundle over an existing WiX 3 FieldWorks install should behave like a proper upgrade (same install locations, old install removed, settings preserved). + +**Hard requirement:** FieldWorks must be single-instance on a machine. The WiX 6 installer must not allow side-by-side installs of FieldWorks from any previous WiX 3 or WiX 6 generation. Installing the WiX 6 bundle must remove/replace all prior FieldWorks installs. + +- [ ] Upgrade detects an existing WiX 3 installation reliably (via UpgradeCode / related bundle detection) and enters “upgrade” behavior. +- [ ] Upgrade uses the same default install paths as WiX 3 (APPFOLDER / DATAFOLDER defaults match). +- [ ] Upgrade does not install to a new/different location when upgrading from WiX 3. +- [ ] Upgrade removes/uninstalls the old WiX 3 install (no side-by-side installs, ever). +- [ ] Upgrade keeps all user settings (projects, preferences, registry-based settings, config files, etc.). +- [ ] Upgrade keeps all user data in-place (no duplication or orphaned data folders). +- [ ] Add explicit evidence for the above (bundle log + MSI log + before/after install paths + ARP snapshot). + +## Product icon parity + +Goal: WiX 6 install should use the same icon experience as WiX 3. + +- [x] ARP icon matches WiX 3 (Programs & Features / Settings > Apps). +- [x] Start Menu shortcuts use the same icon as WiX 3. +- [x] Desktop shortcut (if installed) uses the same icon as WiX 3. + +## ARP entries and installed size look wrong (multiple entries) + +Observed: +- ARP shows one “FieldWorks Language Explorer 9.9” around ~900MB. +- ARP shows multiple “FieldWorks Language Explorer 9 Packages” entries (each ~454MB). +- WiX 3 shows a single entry around ~458MB. + +Note: the WiX 6 authoring has been updated to use “FieldWorks” branding and 9.3.x versioning, but ARP behavior still needs to be re-validated in the clean-machine lane. + +Goal: “one install” presentation similar to WiX 3, with sane size reporting. + +- [ ] Only one primary ARP entry is visible for FieldWorks (matching WiX 3’s behavior). +- [ ] “Packages” entry/entries are not visible to the user (or are collapsed into a single consistent representation). +- [ ] Reported installed size is reasonable and comparable to WiX 3 (no duplicate counting of the same payload). +- [x] Prevent side-by-side MSI installs when `VersionNumber` does not change (dev builds): enable same-version major upgrades. +- [x] Remove MSI ARP flags that make entries non-removable (`ARPNOREMOVE` / `NoRemove=1`) so stale installs can be cleaned up. +- [ ] Validate which packages/components are causing duplicate ARP entries and fix authoring so they don’t register as separate products. +- [ ] Confirm the final state with an ARP snapshot and (optionally) Windows Installer product inventory evidence. + +## Uninstall hangs from Add/Remove Programs + +Observed: uninstall from Settings / Add-Remove Programs gets stuck and does not move forward. + +- [ ] Reproduce uninstall hang deterministically and capture evidence (bundle log, MSI log, any temp logs). +- [ ] Identify whether the hang is in Burn (bootstrapper) vs MSI execution vs a custom action. +- [ ] Fix uninstall to complete successfully from ARP without user intervention. +- [ ] Verify post-uninstall cleanup matches expectations (ARP removed, registry keys cleaned up, shortcuts removed, env vars restored if applicable). diff --git a/specs/001-wix-v6-migration/WIX3_TO_WIX6_COMPARISON.md b/specs/001-wix-v6-migration/WIX3_TO_WIX6_COMPARISON.md new file mode 100644 index 0000000000..e3e851183a --- /dev/null +++ b/specs/001-wix-v6-migration/WIX3_TO_WIX6_COMPARISON.md @@ -0,0 +1,181 @@ +# WiX3 → WiX6 Evidence Comparison (temp/wix3 vs temp/wix6) + +This report compares the two provided evidence dumps: +- Baseline: `temp/wix3` +- Candidate: `temp/wix6` + +Comparison criteria: `EVIDENCE_CRITERA.md` (derived from specs/001-wix-v6-migration/verification-matrix.md, specs/001-wix-v6-migration/parity-check.md, specs/001-wix-v6-migration/wix3-to-wix6-audit.md) + +## Evidence inventory + +Both evidence folders contain the required baseline artifacts: +- `command.txt` +- `computerinfo.txt` +- `exitcode.txt` +- `run-info.txt` +- `uninstall-pre.txt` +- `uninstall-post.txt` +- Bundle log: `wix*-bundle.log` +- MSI package log: `wix*-bundle_006_AppMsiPackage.log` +- Supporting temp logs under `temp/` + +## High-level outcome + +- Both runs completed successfully with `ExitCode=0`. +- Both runs executed an 8-package chain (NetFx48 + VC runtimes + FLEx Bridge + FieldWorks MSI). +- The candidate run uses Burn v6 (expected modernization); the baseline uses Burn v3.11. + +## Per-criterion assessment + +### A) Inputs are comparable (environment parity) + +Status: PASS + +- Both runs are on the same VM identity (`CsName` appears as `TEST`) and same OS line: + - Windows 11 Pro, build 26200. +- Small differences exist in captured fields (e.g., reported total physical memory and username presence), but nothing indicates the machine image materially differs. + +Evidence: +- `temp/wix3/computerinfo.txt` +- `temp/wix6/computerinfo.txt` + +### B) Invocation is comparable + +Status: PASS + +Both runs are quiet install runs with logging enabled: +- Baseline: `...Wix3Baseline.exe /quiet /norestart /log ...\wix3-bundle.log` +- Candidate: `...Wix6Candidate.exe /quiet /norestart /log ...\wix6-bundle.log` + +Evidence: +- `temp/wix3/command.txt` +- `temp/wix6/command.txt` + +### C) Bundle-level success + +Status: PASS + +- Both runs report `ExitCode=0`. +- Both bundle logs show apply completion with `result: 0x0`. + +Evidence: +- `temp/wix3/exitcode.txt` +- `temp/wix6/exitcode.txt` +- `temp/wix3/wix3-bundle.log` (contains `i399: Apply complete, result: 0x0`) +- `temp/wix6/wix6-bundle.log` (contains `i399: Apply complete, result: 0x0`) + +### D) Package chain and planning parity + +Status: PASS (with expected modernization differences) + +Both logs show: +- `Detect begin, 8 packages` +- Detected package IDs match (NetFx48Web, vc8, vc10, vc11, vc12, vc14, FBInstaller, AppMsiPackage) +- Planned action: install prereqs and MSI on a clean box + +Notable difference (modernization / logging semantics): +- Baseline Burn v3 shows `cache: Yes/No` with fewer fields. +- Candidate Burn v6 shows `cache: Vital`, plus explicit cache strategy and registration-state fields. + +Evidence: +- `temp/wix3/wix3-bundle.log` (Detect/Plan sections) +- `temp/wix6/wix6-bundle.log` (Detect/Plan sections) + +### E) MSI-level success + +Status: PASS + +Both MSI logs show a first-time install of a cached MSI under `C:\ProgramData\Package Cache\{GUID}v\...`. + +Notable differences: +- MSI filename in the cache differs: + - WiX3 run: `FieldWorks_9.2.11.1.msi` + - WiX6 run: `FieldWorks.msi` + This looks like a packaging/name change, not a functional failure. + +Evidence: +- `temp/wix3/wix3-bundle_006_AppMsiPackage.log` +- `temp/wix6/wix6-bundle_006_AppMsiPackage.log` + +### F) Post-install footprint parity (ARP + prereqs) + +Status: PARTIAL (because the two runs intentionally install different FieldWorks versions) + +What matches: +- `uninstall-pre.txt` is identical in both runs (Edge + WebView2 present). +- Both post snapshots show: + - FieldWorks installed + - FLEx Bridge installed + - VC++ runtimes installed (2008/2010/2012/2013/2019 family entries) + +What differs: +- FieldWorks DisplayVersion differs: + - WiX3: FieldWorks 9.2.11.1 + - WiX6: FieldWorks 9.9.1.1 + +Interpretation: +- If the intent of this lane is “**old release vs new build**”, then the version delta is expected and this criterion should focus on presence/shape of entries. +- If the intent is “**same product bits, different toolchain**”, then this comparison is not strict enough; you’ll want to run baseline and candidate from the same product version so DisplayVersion can be compared directly. + +Evidence: +- `temp/wix3/uninstall-pre.txt` / `temp/wix6/uninstall-pre.txt` +- `temp/wix3/uninstall-post.txt` / `temp/wix6/uninstall-post.txt` + +### G) Known/expected warnings + +Status: PASS + +Both bundle logs contain: +- `w363: Could not create system restore point, error: 0x80070422. Continuing...` + +This appears consistent across runs and did not prevent success. + +Evidence: +- `temp/wix3/wix3-bundle.log` +- `temp/wix6/wix6-bundle.log` + +## Notable modernizations / expected differences + +- Burn engine: + - WiX3 baseline uses Burn v3.11.2. + - WiX6 candidate uses Burn v6.0.0. + This is an expected modernization and will change log format/details. + +- Bundle version variable differs as expected: + - `WixBundleVersion = 9.2.11.1` (WiX3) + - `WixBundleVersion = 9.9.1.1` (WiX6) + +Evidence: +- `temp/wix3/wix3-bundle.log` (variable section) +- `temp/wix6/wix6-bundle.log` (variable section) + +## Evidence gaps (not present in these dumps) + +This comparison is limited to silent install + prereq + MSI completion and ARP snapshots. + +Not present here (so not evaluated): +- Interactive UX evidence (dual-directory UI, feature selection UI, dialog flow screenshots) +- Registry exports for key FieldWorks HKLM/HKCR values +- Shortcut/link verification +- URL protocol verification +- Environment variable/PATH verification +- Upgrade and uninstall scenario evidence + +These are tracked as verification requirements in specs/001-wix-v6-migration/verification-matrix.md and specs/001-wix-v6-migration/parity-check.md. + +## TODOs (follow-ups for unexplained or incomplete items) + +1) Decide the strictness goal of the parity lane: + - If “same product version” parity is required, re-run WiX3 baseline and WiX6 candidate using installers built from the same FieldWorks version so ARP version comparison is meaningful. + +2) Add (or capture) additional evidence required for sign-off: + - Registry exports (install + uninstall) + - Shortcut and protocol verification + - Environment variable/PATH snapshots + - Upgrade run evidence (old → new) + - Uninstall run evidence (and post-uninstall snapshots) + +3) Investigate candidate-only extra logs: + - `temp/wix6/temp/StructuredQuery.log` + - `temp/wix6/temp/FieldWorks_Language_Explorer_9.9_20251229085611.elevated.log` + Confirm whether these are expected additions from the newer version/tooling or indicate new behavior worth documenting. diff --git a/specs/001-wix-v6-migration/WIX_MIGRATION_STATUS.md b/specs/001-wix-v6-migration/WIX_MIGRATION_STATUS.md new file mode 100644 index 0000000000..d4f1b94c81 --- /dev/null +++ b/specs/001-wix-v6-migration/WIX_MIGRATION_STATUS.md @@ -0,0 +1,28 @@ +# WiX v6 Migration Status + +## Overview +The migration of the FieldWorks installer to WiX v6 has been verified and updated. + +## Verified Components + +### Project Files +- **FLExInstaller/wix6/FieldWorks.Installer.wixproj**: Converted to SDK-style, targets WiX v6. +- **FLExInstaller/wix6/FieldWorks.Bundle.wixproj**: Converted to SDK-style, targets WiX v6. +- **CustomActions.csproj**: Converted to SDK-style, uses `WixToolset.Dtf` packages. + +### Source Files +- **Framework.wxs**: Updated to WiX v4 namespace (`http://wixtoolset.org/schemas/v4/wxs`). +- **Bundle.wxs**: Updated to WiX v4 namespace. +- **Dialogs**: All dialog files (`WixUI_DialogFlow.wxs`, `GIInstallDirDlg.wxs`, `GIWelcomeDlg.wxs`, `GIProgressDlg.wxs`, `GICustomizeDlg.wxs`, `GISetupTypeDlg.wxs`) are updated to WiX v4 namespace. +- **Localization**: `WixUI_en-us.wxl` and `BundleTheme.wxl` are updated to WiX v4 localization namespace (`http://wixtoolset.org/schemas/v4/wxl`). +- **Theme**: `BundleTheme.xml` updated to WiX v4 theme namespace (`http://wixtoolset.org/schemas/v4/thmutil`). + +### Custom Actions +- **CustomAction.cs**: Updated to use `WixToolset.Dtf.WindowsInstaller` namespace. + +## Updates Made +- Updated `FLExInstaller/wix6/Shared/Base/BundleTheme.xml` to use the correct WiX v4 theme namespace `http://wixtoolset.org/schemas/v4/thmutil`. + +## Next Steps +- Run a full build to verify the installer generation. +- Test the generated MSI and Bundle. diff --git a/specs/001-wix-v6-migration/checklists/requirements.md b/specs/001-wix-v6-migration/checklists/requirements.md new file mode 100644 index 0000000000..e0d3be0f12 --- /dev/null +++ b/specs/001-wix-v6-migration/checklists/requirements.md @@ -0,0 +1,34 @@ +# Specification Quality Checklist: WiX v6 Migration + +**Purpose**: Validate specification completeness and quality before proceeding to planning +**Created**: 2025-12-11 +**Feature**: [spec.md](../spec.md) + +## Content Quality + +- [x] No implementation details (languages, frameworks, APIs) +- [x] Focused on user value and business needs +- [x] Written for non-technical stakeholders +- [x] All mandatory sections completed + +## Requirement Completeness + +- [x] No [NEEDS CLARIFICATION] markers remain +- [x] Requirements are testable and unambiguous +- [x] Success criteria are measurable +- [x] Success criteria are technology-agnostic (no implementation details) +- [x] All acceptance scenarios are defined +- [x] Edge cases are identified +- [x] Scope is clearly bounded +- [x] Dependencies and assumptions identified + +## Feature Readiness + +- [x] All functional requirements have clear acceptance criteria +- [x] User scenarios cover primary flows +- [x] Feature meets measurable outcomes defined in Success Criteria +- [x] No implementation details leak into specification + +## Notes + +- Items marked incomplete require spec updates before `/speckit.clarify` or `/speckit.plan` diff --git a/specs/001-wix-v6-migration/contracts/hyperv-runner.sample.json b/specs/001-wix-v6-migration/contracts/hyperv-runner.sample.json new file mode 100644 index 0000000000..c81c34c5a4 --- /dev/null +++ b/specs/001-wix-v6-migration/contracts/hyperv-runner.sample.json @@ -0,0 +1,26 @@ +{ + "vmName": "FwInstallerTest", + "checkpointName": "fresh-install", + "guestCredential": { + "username": "FWTestAdmin", + "passwordEnvVar": "FW_HYPERV_GUEST_PASSWORD" + }, + "guestWorkDir": "C:\\FWInstallerTest", + "installerType": "Auto", + "installArguments": ["/quiet", "/norestart"], + "maxFileCount": 20000, + "baseline": { + "mode": "Wix3", + "installerPath": "C:\\ProgramData\\FieldWorks\\HyperV\\Installers\\Wix3Baseline\\FieldWorks_9.2.11.1_Online_x64.exe", + "downloadPageUrl": "https://software.sil.org/fieldworks/download/fw-92/fw-9211/", + "cacheRoot": "C:\\ProgramData\\FieldWorks\\HyperV\\Installers" + }, + "candidate": { + "mode": "Wix6", + "installerPath": "FLExInstaller\\bin\\x64\\Release\\FieldWorksBundle.exe" + }, + "output": { + "hostEvidenceRoot": "Output\\InstallerEvidence\\HyperV", + "publishToSpecEvidence": true + } +} diff --git a/specs/001-wix-v6-migration/data-model.md b/specs/001-wix-v6-migration/data-model.md new file mode 100644 index 0000000000..185b908a7a --- /dev/null +++ b/specs/001-wix-v6-migration/data-model.md @@ -0,0 +1,46 @@ +# Installer Data Model + +## Features + +The installer exposes the following features to the user: + +- **FieldWorks** (Root) + - **Core** (Hidden, Mandatory): Main application binaries. + - **Writing Systems** (Hidden, Mandatory): Icu, Graphite, etc. + - **Keyman** (Optional): Keyman keyboard integration. + - **Movies** (Optional): Sample movies. + - **Samples** (Optional): Sample data. + - **Localization** (Optional): + - **French** + - **Spanish** + - **Portuguese** + - ... (others as defined in `CustomFeatures.wxi`) + +## Directory Structure + +The installer manages two primary directories: + +1. **Application Directory** (`WIXUI_INSTALLDIR`): + - Default: `%ProgramFiles%\SIL\FieldWorks` + - Contains: Binaries, DLLs, Resources. + +2. **Project Data Directory** (`WIXUI_PROJECTSDIR`): + - Default: `%ProgramData%\SIL\FieldWorks\Projects` (or similar, need to verify) + - Contains: User project data, shared resources. + +## Components + +Key components include: + +- **FieldWorks.exe**: Main entry point. +- **FwData.dll**: Core data access. +- **Icu.dll**: ICU support. +- **Redistributables**: + - .NET Framework 4.8 + - VC++ Redistributable (x64/x86) + - WebView2 Runtime (if applicable) + +## Registry Keys + +- `HKLM\Software\SIL\FieldWorks`: Installation paths, version info. +- `HKCU\Software\SIL\FieldWorks`: User preferences. diff --git a/specs/001-wix-v6-migration/golden-install-checklist.md b/specs/001-wix-v6-migration/golden-install-checklist.md new file mode 100644 index 0000000000..191083613b --- /dev/null +++ b/specs/001-wix-v6-migration/golden-install-checklist.md @@ -0,0 +1,142 @@ +# Golden Install Checklist (FieldWorks WiX 6) + +**Purpose**: A repeatable manual validation script for the WiX 6 installer, designed to generate consistent evidence for review and regression tracking. + +**Recommended cadence**: Run this checklist at least once per major installer change and before release candidate tagging. + +## Test environment + +- VM: Windows (clean snapshot recommended) +- Snapshot names (suggested): + - `FW-Clean` + - `FW-HasPrereqs` (VC++ + .NET already installed) + - `FW-HasOldFieldWorks` (a prior released FieldWorks installed) + - `FW-Offline` (no internet access) +- Evidence folder: `C:\Temp\FwInstallerEvidence\YYYY-MM-DD\` + +## Artifacts under test + +- Bundle: `FieldWorks.exe` +- MSI: `FieldWorks.msi` +- Optional: offline layout / offline bundle artifact (if supported) + +Record exact artifact paths + hashes: +- ☐ Bundle path + SHA256: +- ☐ MSI path + SHA256: + +## Logging setup + +Preferred: +- Bundle log: `FieldWorks.exe /log C:\Temp\FwInstallerEvidence\bundle.log` +- MSI log: `msiexec /i FieldWorks.msi /l*v C:\Temp\FwInstallerEvidence\msi-install.log` + +If bundle log switch differs, capture `%TEMP%` logs and record filenames here: +- ☐ Bundle log path(s): + +## Scenario A: Online clean install (primary) + +Run on snapshot: `FW-Clean` (internet enabled) + +1) Launch bundle with logging +- ☐ Bundle UI opens +- ☐ License/branding/theme appears correct + +2) Install directories +- ☐ Choose non-default App directory +- ☐ Choose non-default Data directory +- ☐ Install completes successfully + +3) Feature selection +- ☐ Select a non-default feature set (document what you chose) +- ☐ Verify installed payload matches feature selection + +4) Post-install verification +- ☐ App files exist under chosen App directory +- ☐ Data files exist under chosen Data directory +- ☐ Desktop shortcut exists (if expected) +- ☐ Start menu shortcuts exist (docs/tools/help) +- ☐ `silfw:` protocol registered (verify registry + a simple invocation) +- ☐ Environment variables are set as expected (including PATH modifications) +- ☐ Registry keys/values exist under the expected HKLM location (paths + version) + +Evidence to attach: +- ☐ `bundle.log` +- ☐ `msi-install.log` +- ☐ Screenshots of directory selection + feature selection +- ☐ Registry exports (before/after) for relevant keys + +## Scenario B: Online install with prereqs already present (detection) + +Run on snapshot: `FW-HasPrereqs` + +- ☐ Bundle detects prereqs and skips downloads/installs where appropriate +- ☐ Install succeeds and behaves the same as Scenario A + +Evidence: +- ☐ `bundle.log` showing detection decisions + +## Scenario C: Major upgrade from previous released version + +Run on snapshot: `FW-HasOldFieldWorks` + +1) Confirm baseline +- ☐ Prior FieldWorks version installed and launches +- ☐ Record version number + +2) Run new bundle +- ☐ Upgrade path proceeds without side-by-side installs +- ☐ Any data directory lock/restriction behavior matches expectations + +3) Post-upgrade checks +- ☐ Single ARP entry for FieldWorks as expected +- ☐ App launches and existing projects/data remain intact +- ☐ Registry values updated to new version + +Evidence: +- ☐ Upgrade bundle/MSI logs +- ☐ Screenshot of ARP showing installed version + +## Scenario D: Uninstall + +Run after Scenario A or C + +- ☐ Uninstall succeeds without errors +- ☐ Registry keys removed (as expected) +- ☐ Environment variables removed/restored (as expected) +- ☐ Shortcuts removed + +Evidence: +- ☐ Uninstall logs +- ☐ Registry/env snapshots before/after + +## Scenario E: Offline install + +Run on snapshot: `FW-Offline` (no internet) + +Precondition: +- ☐ Offline artifact/layout is available locally + +- ☐ Install completes without requiring network access +- ☐ Prereqs are available from offline media/layout + +Evidence: +- ☐ Offline install logs +- ☐ Proof network disabled (note VM settings) + +## Scenario F: Localization smoke test (at least one locale) + +- ☐ Build/run installer in one non-English locale +- ☐ Installer UI strings load correctly +- ☐ Install succeeds + +Evidence: +- ☐ Screenshots of localized UI +- ☐ Build artifact listing + +## Results summary + +- Date: +- Tester: +- VM images/snapshots used: +- Pass/fail summary: +- Issues found (link to issues/notes): diff --git a/specs/001-wix-v6-migration/parity-check.md b/specs/001-wix-v6-migration/parity-check.md new file mode 100644 index 0000000000..a61e1f6051 --- /dev/null +++ b/specs/001-wix-v6-migration/parity-check.md @@ -0,0 +1,267 @@ +# WiX 3.x → WiX 6 Parity Check Procedure (FieldWorks) + +**Purpose** + +This document defines a repeatable parity-check process to ensure the WiX 6 (SDK-style) MSI + Burn bundle implementation fully replaces the legacy WiX 3.x behavior (genericinstaller template + FieldWorks includes). + +> Note: this worktree does not currently include a `genericinstaller/` folder, so the default “WiX 3.x baseline” is the in-repo `FLExInstaller/*.wxi` at the chosen baseline ref. + +It complements (does not replace): + +- [wix3-to-wix6-audit.md](wix3-to-wix6-audit.md) (what we *believe* is equivalent) +- [verification-matrix.md](verification-matrix.md) (what we must *prove* with evidence) +- [golden-install-checklist.md](golden-install-checklist.md) (manual install runs and evidence collection) + +Related: + +- [tasks.md](tasks.md) (tracks remaining parity/validation work) + +**Scope** + +Parity check = two kinds of proof: + +1) **Behavioral parity** (install, upgrade, uninstall outcomes match expectations). +2) **Build/authoring parity** (any legacy “implicit” steps are explicitly represented in WiX 6 and can be pointed to in-tree). + +--- + +## Inputs / Baselines + +**WiX 3.x baseline definition** + +- FieldWorks product-specific includes (historical): `FLExInstaller/*.wxi` at baseline ref (e.g., `release/9.3` or commit `6a2d976e`) +- Optional (if available externally): the WiX 3.x generic template authoring from `genericinstaller/BaseInstallerBuild/*` and `genericinstaller/Common/*` + +**WiX 6 implementation under test** + +- MSI: `FLExInstaller/wix6/FieldWorks.Installer.wixproj` + `FLExInstaller/wix6/Shared/Base/*.wxs` + `FLExInstaller/wix6/Shared/Common/*.wxi` +- Bundle: `FLExInstaller/wix6/FieldWorks.Bundle.wixproj` + `FLExInstaller/wix6/Shared/Base/Bundle.wxs` + theme files +- Build orchestration/staging: `Build/Installer.targets` + +--- + +## Evidence conventions + +Use an evidence folder per run: + +- `C:\Temp\FwInstallerEvidence\YYYY-MM-DD\\` + +Capture: + +- Bundle log(s) +- MSI verbose log(s) +- Screenshots for UX claims +- Registry exports for key claims +- A short “diff notes” file listing any gaps found + +Local development PC lane (manual/scripted): + +- Run installer(s) directly on the developer machine. +- Capture logs using the standard helper scripts or manual `msiexec`/bundle `/log` flags. +- Evidence is written under `Output\InstallerEvidence\` and can be published to `specs/001-wix-v6-migration/evidence/`. + +--- + +## Procedure + +### 1) Establish the baseline and the current target + +Decide and record: + +- Baseline git ref: `release/9.3` (default) or `6a2d976e` (if required) +- Target branch: `001-wix-v6-migration` + +### 2) Build artifacts (target branch) + +Use the repo build entrypoint: + +- Build (WiX 6): `./build.ps1 -BuildInstaller -Configuration Release -InstallerToolset Wix6` + +Record: + +- The artifact paths (MSI + bundle) and hashes (SHA256). + +### 3) Build pipeline parity review (source parity) + +This is a review step: the goal is to identify **implicit legacy behavior** and verify it has an explicit equivalent in WiX 6. + +Suggested workflow: + +- Compare the “what drives the build” between baseline and current: + - baseline (in-repo): `FLExInstaller/*.wxi` at the selected baseline ref + - baseline (optional external): `genericinstaller/BaseInstallerBuild/buildMsi.bat`, `buildExe.bat` + - current: `Build/Installer.targets`, `FLExInstaller/*.wixproj` + +Recommended commands (repo-local wrappers preferred): + +- Show a baseline file: + - `./scripts/Agent/Git-Search.ps1 -Action show -Ref origin/release/9.3 -Path FLExInstaller/Fonts.wxi -HeadLines 200` +- Diff a baseline file vs current: + - `./scripts/Agent/Git-Search.ps1 -Action diff -Ref origin/release/9.3 -Path FLExInstaller/Fonts.wxi` + +Output of this step: + +- Populate the **Implicit→Explicit Mapping Table** section below. +- Any suspected gaps become follow-up tasks (and should also be noted in `wix3-to-wix6-audit.md`). + +### 4) Install behavior parity (VM runs) + +Follow `golden-install-checklist.md` for the actual interactive validation runs. + +For parity runs on the local development PC: + +- Run WiX3 baseline and WiX6 candidate sequentially on the same machine, ensuring uninstall/cleanup between runs. +- Capture logs and screenshots (when needed) for both runs and record any deltas in the parity notes. + +Suggested command patterns: + +- Bundle (with logs): + - `FieldWorks.exe /log C:\Temp\FwInstallerEvidence\YYYY-MM-DD\bundle.log` +- MSI (verbose log): + - `msiexec /i FieldWorks.msi /l*v C:\Temp\FwInstallerEvidence\YYYY-MM-DD\msi-install.log` + +Repo helper (preferred when available): + +- `./scripts/Agent/Invoke-Installer.ps1 -InstallerType Bundle -Configuration Release -Arguments @('/passive') -IncludeTempLogs` + +Output of this step: + +- Check off the items in the **Parity Checklist** section below. +- Attach evidence paths. + +### 5) Review and sign-off + +At minimum, review should include: + +- A second person validates the mapping table entries (links/paths correct). +- A second person spot-checks a sample of evidence logs/screenshots for the highest-risk areas: + - custom actions + - upgrade + - env vars and PATH + - offline bundle story (if claimed) + +--- + +## Context7: WiX command reference lookup (how to use it) + +When adding/adjusting steps that use WiX tools (e.g., `wix.exe build`, `wix.exe msi validate`, Burn logging switches), use Context7 to fetch up-to-date official patterns. + +Suggested workflow: + +1) Resolve the library ID: + +- Use the Context7 resolver for “Wix Toolset” / “WixToolset” + +2) Fetch docs focused on the command/topic you need: + +- Topic examples: `wix.exe build`, `wix.exe msi validate`, `Burn logs`, `bootstrapper /log`, `MSI ICE validation` + +3) Update this document with: + +- The command syntax used +- What output/log artifacts prove success + +--- + +## Parity Checklist (fill during verification) + +### Toolchain / Build + +- [ ] Build succeeds without `genericinstaller` checkout present (fresh clone/worktree) +- [ ] Build does not rely on WiX 3 being on PATH +- [x] Build outputs match expected filenames/locations +- [ ] Staging folder contains expected payload roots: + - [x] `FieldWorks` + - [x] `FieldWorks_Data` + - [x] `FieldWorks_L10n` + - [x] `FieldWorks_Font` + +### MSI payload (install result) + +- [ ] APPFOLDER contains app binaries after install +- [ ] DATAFOLDER contains data payload after install +- [ ] Localization payload installed (spot-check at least one locale folder) +- [ ] Fonts behavior matches intent: + - [ ] Fonts installed when missing + - [ ] Existing fonts not overwritten (registry-gated) + - [ ] Uninstall does not remove fonts (Permanent) + +### UX + +- [ ] Dual-directory UI works (custom App + Data paths) +- [ ] Feature selection UI works and affects payload installed +- [ ] Dialog flow navigation behaves correctly + +### Registry / shortcuts / protocol / env + +- [ ] HKLM registry keys/values written (paths + version) +- [ ] Desktop shortcut(s) behave as expected +- [ ] Start menu shortcuts (docs/tools/help) present +- [ ] `silfw:` protocol registered and test invocation works +- [ ] Environment variables set correctly (including PATH modifications) + +### Custom actions + +- [ ] CloseApplications CA behaves correctly (no 1603) +- [ ] No modal assertion UI blocks install (debug/dev) +- [ ] Custom actions logs captured and reviewed + +### Bundle prerequisites + +- [ ] .NET 4.8 prereq behavior correct (download/detect) +- [ ] VC++ prereq behavior correct (download/detect) +- [ ] FLEx Bridge offline prereq behavior correct (detect/install) + +### Upgrade / uninstall + +- [ ] Major upgrade from previous release works (single ARP entry) +- [ ] Data path restriction behavior on upgrade matches expectations +- [ ] Uninstall removes expected registry values and shortcuts +- [ ] Uninstall restores env vars/PATH appropriately + +### Offline story (if claimed) + +- [ ] Offline artifact/layout produced reproducibly +- [ ] Offline install succeeds on a disconnected VM + +--- + +## Implicit → Explicit Mapping Table (fill during audit) + +Populate one row per behavior/step. Keep entries short and point to the most specific file/target possible. + +| Behavior / step (short) | Was implicit before (WiX3 baseline) | How it’s expressed in v6 | Where it lives now (path + identifier) | Status | Notes / evidence | +|---|---|---|---|---|---| +| Example: CA runtime selection | DTF/SfxCA expected `CustomAction.config` present in CA payload (in WiX3-era tooling this was typically ensured by the CA project output) | Explicitly copied to output and packaged | `FLExInstaller/Shared/CustomActions/CustomActions/CustomActions.csproj` (`Content Include="CustomAction.config"` + `CopyToOutputDirectory`) | ☑ | Confirmed in project file; historic example (optional external baseline): `genericinstaller/CustomActions/CustomActions/CustomActions.csproj` copies `CustomAction.config` to output | +| Example: L10n payload inclusion | Legacy pipeline staged + harvested l10n implicitly | Heat harvest + feature reference | `FLExInstaller/wix6/FieldWorks.Installer.wixproj` (`HarvestedL10nFiles`) + `Framework.wxs` feature refs | ☐ | Install file listing | +| Example: Fonts install semantics | Font components existed, relied on legacy schema allowances | Explicit components with registry gating + Permanent | `FLExInstaller/Fonts.wxi` (`ComponentGroup Fonts`) + referenced from `Framework.wxs` | ☐ | Registry + file check | +| Staged payload roots exist | `Build/Installer.targets` defined versioned staging dirs (e.g., `$(SafeApplicationName)_$(MinorVersion)_Build_$(Platform)` + `objects/...` suffixes) | Explicit staging target creates expected roots under `FieldWorks_InstallerInput__\objects\...` | `Build/Installer.targets` (`StageInstallerInputs`) and `BuildDir\FieldWorks_InstallerInput_Release_x64\objects\*` | ☑ | Verified on 2025-12-17 (Release build) | +| Bundle logs captured | Ad-hoc logging varied by invocation | Standardized helper writes log under repo output | `scripts/Agent/Invoke-Installer.ps1` (bundle log path) | ☑ | `Output\InstallerEvidence\20251217-local-bundle-passive\bundle.log` | +| FLEx Bridge offline prereq (bundle) | Bundle included/offered offline bridge prereq | `ExePackage` is defined in the shared prereqs include and referenced from the bundle chain via `PackageGroupRef` | `FLExInstaller/Shared/Common/Redistributables.wxi` (`PackageGroup FlexBridgeInstaller` + `ExePackage FBInstaller`) and `PackageGroup vcredists` (`PackageGroupRef FlexBridgeInstaller`) | ☑ | Evidence: `Output\InstallerEvidence\flexbridge-parity\bundle.log` contains `Detected package: FBInstaller` and `Planned package: FBInstaller` (still needs clean-VM behavior validation) | +| MSI ICE validation | Typically implicit (or run manually) | Explicit `wix.exe msi validate -sice ICE60 -wx` in build | `FLExInstaller/wix6/FieldWorks.Installer.wixproj` (`WindowsInstallerValidation`) | ☑ | Present in Release build output | +| | | | | | | +| | | | | | | +| | | | | | | +| | | | | | | + +--- + +## Results (fill after running) + +- Date: 2025-12-17 +- Baseline ref: `release/9.3` +- Target build artifacts (Release): + - `FLExInstaller\wix6\bin\x64\Release\en-US\FieldWorks.msi` (SHA256 `7AED3302AD3A273DAAA29D97B5829E91E684699C9150803AA26A35E29B415C09`) + - `FLExInstaller\wix6\bin\x64\Release\en-US\FieldWorks.wixpdb` + - `FLExInstaller\wix6\bin\x64\Release\FieldWorksBundle.exe` (SHA256 `365901C88C211C1A7019CBCB8EFB54523D212393976DA518A388E42CF1CC7F62`) + - `FLExInstaller\wix6\bin\x64\Release\FieldWorksBundle.wixpdb` +- Staging root (exists): `BuildDir\FieldWorks_InstallerInput_Release_x64\objects\` +- Evidence folder (local run): `Output\InstallerEvidence\20251217-local-bundle-passive\` + - Bundle log: `Output\InstallerEvidence\20251217-local-bundle-passive\bundle.log` +- Evidence folder (local run, FLEx Bridge chain verification): `Output\InstallerEvidence\flexbridge-parity\` + - Bundle log: `Output\InstallerEvidence\flexbridge-parity\bundle.log` +- Summary: + - Pass: Build artifacts + staging roots present for Release. + - Pass: Bundle runs with `/log` and captures Burn log (local run). + - Gaps: Behavioral parity validation on clean VM not yet recorded (local bundle run shows bundle/MSI already present). + - Follow-ups (task IDs / issues): T067, T049-T057, T063-T065. diff --git a/specs/001-wix-v6-migration/plan.md b/specs/001-wix-v6-migration/plan.md new file mode 100644 index 0000000000..4c6f27848f --- /dev/null +++ b/specs/001-wix-v6-migration/plan.md @@ -0,0 +1,116 @@ +# Implementation Plan: WiX v6 Migration + +**Branch**: 001-wix-v6-migration | **Date**: 2025-12-11 | **Spec**: [specs/001-wix-v6-migration/spec.md](specs/001-wix-v6-migration/spec.md) +**Input**: Feature specification from specs/001-wix-v6-migration/spec.md + +**Note**: This template is filled in by the /speckit.plan command. See .specify/templates/commands/plan.md for the execution workflow. + +## Summary + +Run WiX 3 and WiX 6 installers in parallel during a transition period. WiX 3 remains the **default** installer build in the `FLExInstaller/` root, while WiX 6 is moved under `FLExInstaller/wix6/` as an opt-in path. This preserves the release/9.3-compatible layout while continuing the WiX 6 migration work (SDK-style projects, MSBuild targets, and in-tree shared code). + +To keep the migration maintainable, we also standardize on C# 8 language features for managed code (while keeping nullable reference type analysis disabled initially to avoid warnings-as-errors churn). + +Installer testing will be performed on the **local development PC** only (no Hyper-V or Windows Sandbox lanes). + +## Transition Plan: Parallel WiX 3 + WiX 6 Installers (NEW) + +### Objectives + +- Restore the WiX 3 installer project from `release/9.3` and keep it buildable. +- Add a **toolset selection switch** (default **WiX 3**, opt-in **WiX 6**). +- Keep WiX 3 inputs isolated from WiX 6 schema changes. + +### Known changes that can break WiX 3 (must be reversed or isolated) + +- `FLExInstaller/*.wxi` now contain WiX 4+/v6 namespaces and constructs (breaks WiX 3). +- `Build/Installer.targets` was rewritten for the WiX 6 MSBuild pipeline (WiX 3 batch flow removed). +- Legacy `PatchableInstaller` expectations were removed/quarantined; WiX 3 requires these inputs. +- Custom action wiring switched to WiX 4+ binaries (e.g., `Wix4UtilCA_X64`). + +### Files/folders to pull from `release/9.3` + +Pull these **verbatim** from the `release/9.3` worktree to restore WiX 3 support: + +- `Build/Installer.targets` (legacy WiX 3 orchestration + batch script invocation) +- `FLExInstaller/*.wxi` (WiX 3-compatible includes, preserved in root) +- `PatchableInstaller/` full tree (BaseInstallerBuild, Common, CustomActions, ProcRunner, CreateUpdatePatch, libs, resources, `Directory.Build.props`, README, `.gitignore`, `.gitattributes`) + +### Build + Documentation updates + +- Add `InstallerToolset=Wix3|Wix6` (default **Wix3**) to `build.ps1` and `Build/Orchestrator.proj`. +- Add explicit targets: `BuildInstallerWix3` and `BuildInstallerWix6` (with `BuildInstaller` routing to Wix3 by default). +- Move WiX 6 assets to `FLExInstaller/wix6/` (projects + shared authoring) and keep WiX 3 in root. +- Split WiX 3 vs WiX 6 include paths (root vs `FLExInstaller/wix6/Shared/`) to avoid toolset conflicts. +- Update docs (`ReadMe.md`, `specs/001-wix-v6-migration/quickstart.md`, `FLExInstaller/COPILOT.md`) to describe both build paths. +- Update CI to build Wix3 by default, plus an opt-in Wix6 job or flag. + +## Technical Context + +**Language/Version**: WiX Toolset v6, MSBuild (VS 2022), C# 8 (Custom Actions; nullable analysis disabled initially) +**Primary Dependencies**: WixToolset.Sdk, WixToolset.UI.wixext, WixToolset.Util.wixext, WixToolset.NetFx.wixext, WixToolset.Bal.wixext +**Storage**: N/A (Installer) +**Testing**: Snapshot/compare-based evidence collection on local development PC; WixToolset.Heat for harvesting (if used) +**Target Platform**: Windows (x64/x86) +**Project Type**: Installer (MSI & Bundle) +**Performance Goals**: N/A +**Constraints**: Offline capability, Dual Directory UI (App + Project Data), Major Upgrade support +**Scale/Scope**: ~12 features, multiple locales, ~110 projects + +## Constitution Check + +*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.* + +- **I. Data Integrity**: Installer must perform Major Upgrade to prevent side-by-side version conflicts and potential data corruption. (FR-Upgrade) +- **II. Test and Review Discipline**: Changes to installer require validation. User stories include independent tests for Developer Build, CI Build, Online Install, and Offline Install. +- **III. Internationalization**: Installer must support multiple locales (FR-005). +- **IV. User-Centered Stability**: Offline installation support is critical (FR-004). +- **V. Licensing**: WiX is open source (MS-RL/MIT). +- **VI. Documentation Fidelity**: COPILOT.md in FLExInstaller and PatchableInstaller must be updated. + +## Project Structure + +### Documentation (this feature) + +` ext +specs/001-wix-v6-migration/ + plan.md # This file + research.md # Phase 0 output + data-model.md # Phase 1 output (N/A for installer) + quickstart.md # Phase 1 output + contracts/ # Phase 1 output (N/A for installer) + tasks.md # Phase 2 output +` + +### Source Code (repository root) + +` ext +FLExInstaller/ + *.wxi # WiX 3 includes (root, release/9.3-compatible) + ... + +FLExInstaller/wix6/ + FieldWorks.Installer.wixproj + FieldWorks.Bundle.wixproj + Shared/ + ... + +PatchableInstaller/ # Restored for WiX 3 path (legacy batch pipeline) + BaseInstallerBuild/ + ... + +Build/ + Installer.targets # WiX 6 targets + Installer.Wix3.targets # WiX 3 targets + ... +` + +**Structure Decision**: Keep WiX 3 authoring in `FLExInstaller/` root (minimize release/9.3 diffs) and move WiX 6 authoring under `FLExInstaller/wix6/`. PatchableInstaller is restored in-tree for WiX 3, while WiX 6 continues to use MSBuild + SDK-style projects with in-tree shared code. + +## Complexity Tracking + +N/A - No constitution violations. + +## Installer Verification: Local Development PC Only + +Installer validation will be performed on the **local development PC** without sandboxing or Hyper-V. Evidence capture follows the same log collection conventions (bundle/MSI logs and screenshots where needed) but is executed directly on the developer machine. diff --git a/specs/001-wix-v6-migration/quickstart.md b/specs/001-wix-v6-migration/quickstart.md new file mode 100644 index 0000000000..0d2c79fd28 --- /dev/null +++ b/specs/001-wix-v6-migration/quickstart.md @@ -0,0 +1,84 @@ +# Quickstart: Building the Installer + +## Prerequisites + +1. **Visual Studio 2022** with ".NET Desktop Development" workload. +2. **WiX Toolset v3** plus the **Visual Studio WiX Toolset v3 extension** (required for the WiX 3 default build path). +3. **WiX Toolset v6** (for the WiX 6 path; installed via .NET tool or NuGet, handled by build). +4. **Internet Connection** (for downloading NuGet packages and prerequisites). + +## Building Locally + +To build the installer, run the following command from the repository root: + +```powershell +# Build the installer (Debug configuration, default = WiX 3) +./build.ps1 -BuildInstaller + +# Build the WiX 6 installer (Debug) +./build.ps1 -BuildInstaller -InstallerToolset Wix6 + +# Or run MSBuild directly (WiX 3 default) +msbuild Build/Orchestrator.proj /t:BuildInstaller /p:Configuration=Debug /p:Platform=x64 /p:config=release + +# Or run MSBuild directly (WiX 6) +msbuild Build/Orchestrator.proj /t:BuildInstaller /p:Configuration=Debug /p:Platform=x64 /p:config=release /p:InstallerToolset=Wix6 + +# Build Release version (WiX 3 default) +./build.ps1 -BuildInstaller -Configuration Release + +# Build Release version (WiX 6) +./build.ps1 -BuildInstaller -Configuration Release -InstallerToolset Wix6 + +# Or run MSBuild directly (WiX 3 default) +msbuild Build/Orchestrator.proj /t:BuildInstaller /p:Configuration=Release /p:Platform=x64 /p:config=release + +# Or run MSBuild directly (WiX 6) +msbuild Build/Orchestrator.proj /t:BuildInstaller /p:Configuration=Release /p:Platform=x64 /p:config=release /p:InstallerToolset=Wix6 +``` + +## Artifacts + +WiX 3 build artifacts are produced under `FLExInstaller/bin///` (bundle outputs are culture-specific under `en-US/`). + +WiX 6 build artifacts are produced under `FLExInstaller/wix6/bin///` (bundle outputs are culture-specific under `en-US/`). + +- `FieldWorks.msi`: The main MSI package. +- `FieldWorksBundle.exe`: The bootstrapper bundle (includes prerequisites). +- `*.wixpdb`: Debug symbols for MSI/bundle. + +## Customization via MSBuild properties (FR-008) + +Override installer identity/versioning by passing MSBuild properties. Common properties: + +- **WiX 3 default path** (`FLExInstaller/` + `PatchableInstaller/`): + - `ApplicationName`, `SafeApplicationName`, `Manufacturer`, `SafeManufacturer` + - `UpgradeCodeGuid`, `BuildVersionSegment` + - `InstallersBaseDir`, `AppBuildDir`, `BinDirSuffix`, `DataDirSuffix`, `L10nDirSuffix`, `FontDirSuffix` +- **WiX 6 opt-in path** (`FLExInstaller/wix6/`): + - `ApplicationName`, `SafeApplicationName`, `Manufacturer`, `SafeManufacturer` + - `BundleId`, `UpgradeCode`, `BuildVersionSegment` + - `AppBuildDir`, `BinDirSuffix`, `DataDirSuffix`, `L10nDirSuffix`, `FontDirSuffix`, `IcuVersion` + +Examples: + +```powershell +# WiX 3 default with custom version segment +./build.ps1 -BuildInstaller -MsBuildArgs '/p:BuildVersionSegment=1357' + +# WiX 6 with custom name/version +./build.ps1 -BuildInstaller -InstallerToolset Wix6 -MsBuildArgs '/p:ApplicationName="FieldWorks" /p:BuildVersionSegment=1357' +``` + +### Artifact checklist (x64/Debug, WiX 6) + +- [ ] `FLExInstaller/wix6/bin/x64/Debug/en-US/FieldWorks.msi` +- [ ] `FLExInstaller/wix6/bin/x64/Debug/en-US/FieldWorks.wixpdb` +- [ ] `FLExInstaller/wix6/bin/x64/Debug/FieldWorksBundle.exe` +- [ ] `FLExInstaller/wix6/bin/x64/Debug/FieldWorksBundle.wixpdb` + +## Troubleshooting + +- **Missing Prerequisites**: Ensure you have internet access. The build attempts to download required redistributables. +- **Signing Errors**: If `SIGN_INSTALLER` is set but no certificate is provided, the build will fail. Unset the variable for local testing. +- **WiX Errors**: Check the build log for specific WiX error codes (e.g., `WIX0001`). diff --git a/specs/001-wix-v6-migration/research.md b/specs/001-wix-v6-migration/research.md new file mode 100644 index 0000000000..d3da394dc7 --- /dev/null +++ b/specs/001-wix-v6-migration/research.md @@ -0,0 +1,72 @@ +# Research: WiX v6 Migration + +**Status**: In Progress +**Date**: 2025-12-11 + +## 1. GenericInstaller Migration + +**Goal**: Remove dependency on `genericinstaller` submodule and `PatchableInstaller` folder structure. + +**Findings**: +- `PatchableInstaller` contains `BaseInstallerBuild` (UI, common wxs) and `CustomActions`. +- `FLExInstaller` references these files. +- **Decision**: Move `PatchableInstaller/BaseInstallerBuild` and `PatchableInstaller/CustomActions` into `FLExInstaller/Shared` or similar. +- **Task**: Audit `FLExInstaller` .wxs files to find all references to `PatchableInstaller`. + +## 2. WiX v6 Conversion + +**Goal**: Convert WiX 3.11 source to WiX v6. + +**Findings**: +- WiX v6 uses SDK-style projects (``). +- `wix convert` tool can automate much of the .wxs conversion. +- **Decision**: Create new `.wixproj` files for the installer components. +- **Task**: Run `wix convert` on a sample file to verify changes. + +## 3. UI Porting + +**Goal**: Maintain "Dual Directory" selection and custom feature tree. + +**Findings**: +- `GIInstallDirDlg.wxs` implements the dual directory logic. +- `GICustomizeDlg.wxs` implements the restricted feature tree. +- These are standard WiX UI dialogs with custom XML. +- **Decision**: Copy these .wxs files into the new project and reference them. Ensure `WixToolset.UI.wixext` is referenced. +- **Task**: Verify `WixUI_DialogFlow.wxs` logic is compatible with WiX v6 UI extension points. + +## 4. MSBuild Integration & Harvesting + +**Goal**: Replace batch files with MSBuild targets. + +**Findings**: +- Current build uses `buildBaseInstaller.bat` etc. +- `buildMsi.bat` builds `../ProcRunner/ProcRunner.sln` (C# helper). +- `buildMsi.bat` uses `heat.exe` to harvest `MASTERBUILDDIR` and `MASTERDATADIR`. +- **Decision**: + - Create `Build/Installer.targets` to define the build process. + - Migrate `ProcRunner` to `FLExInstaller/Shared/ProcRunner`. + - Use `` in `.wixproj` to replace `heat.exe` calls. +- **Task**: Define targets for `Restore`, `Build`, `Sign`, `Publish`. +- **Prerequisites**: Use `DownloadFile` task or similar in MSBuild to fetch .NET/C++ redistributables if not present (FR-011). + +## 5. Code Signing + +**Goal**: Sign artifacts using env vars. + +**Findings**: +- `buildExe.bat` uses `insignia` to sign the bundle engine. +- WiX v6 `WixToolset.Bal.wixext` handles engine signing automatically if `SignOutput` is configured. +- **Decision**: Use `WixToolset.Sdk` signing integration. +- **Task**: Configure `SignOutput` to use certificate from environment variables. + +## 6. Unknowns & Risks + +- **Risk**: `PatchableInstaller` might have hidden dependencies on other repos (unlikely given the submodule nature, but possible). +- **Risk**: Custom Actions might depend on old .NET versions. (Need to ensure they target .NET 4.8 or .NET Standard). +- **Unknown**: Exact list of prerequisites to download. (Need to check `Redistributables.wxi`). + +## 7. Alternatives Considered + +- **Keep Submodule**: Rejected (FR-003). +- **Standard UI**: Rejected (FR-010) - need dual directory support. +- **PowerShell Build Script**: Rejected (FR-002) - prefer MSBuild targets for better integration. diff --git a/specs/001-wix-v6-migration/spec.md b/specs/001-wix-v6-migration/spec.md new file mode 100644 index 0000000000..d92392bf16 --- /dev/null +++ b/specs/001-wix-v6-migration/spec.md @@ -0,0 +1,162 @@ +# Feature Specification: WiX v6 Migration + +**Feature Branch**: 001-wix-v6-migration +**Created**: 2025-12-11 +**Status**: Draft +**Input**: User description: "Migrate WiX 3.11 installer to WiX v6, modernize build process, and remove genericinstaller submodule." + +## User Scenarios & Testing *(mandatory)* + +### User Story 1 - Developer Builds Installer (Priority: P1) + +A developer wants to build the installer locally to verify changes or create a release. They should be able to do this using standard MSBuild commands without relying on legacy batch files or external submodules. + +**Why this priority**: This is the core development workflow. Without a working build, no other testing or deployment is possible. + +**Independent Test**: Run the installer build on a clean developer machine (e.g., `./build.ps1 -BuildInstaller`, or `msbuild Build/Orchestrator.proj /t:BuildInstaller /p:Configuration=Debug /p:Platform=x64 /p:config=release`) and verify that a valid .exe or .msi is produced. + +**Acceptance Scenarios**: + +1. **Given** a clean repository checkout, **When** the developer runs the installer build command, **Then** the build completes without errors and produces the installer artifacts. +2. **Given** the genericinstaller submodule is removed, **When** the build runs, **Then** it succeeds using the migrated custom actions and logic. + +--- + +### User Story 2 - CI Builds Installer (Priority: P1) + +The Continuous Integration (CI) system needs to build the installer automatically on code changes. The workflow must be updated to use the new WiX v6 build process. + +**Why this priority**: Automated builds are essential for quality assurance and release management. + +**Independent Test**: Trigger the GitHub Actions workflow and verify that the "Build Installer" step succeeds and artifacts are uploaded. + +**Acceptance Scenarios**: + +1. **Given** a push to the repository, **When** the CI workflow triggers, **Then** the installer build step executes the new MSBuild targets and succeeds. +2. **Given** the build completes, **When** artifacts are inspected, **Then** the installer files are present and valid. + +--- + +### User Story 3 - End User Installation (Online) (Priority: P1) + +An end user downloads the installer and runs it on a machine with internet access. The installer should detect missing prerequisites, download them, and install the product. + +**Why this priority**: This is the primary distribution method for the product. + +**Independent Test**: Run the generated installer on a clean VM with internet access. Verify prerequisites are downloaded and the product installs. + +**Acceptance Scenarios**: + +1. **Given** a machine without prerequisites, **When** the user runs the installer, **Then** it downloads and installs the prerequisites before installing the product. +2. **Given** the installation completes, **When** the user launches the product, **Then** it opens successfully. + +--- + +### User Story 4 - End User Installation (Offline) (Priority: P2) + +An end user (or admin) wants to install the product on a machine without internet access. They use an offline layout or bundled installer that includes all prerequisites. + +**Why this priority**: Critical for deployments in low-connectivity environments. + +**Independent Test**: Create an offline layout (or use the offline bundle), disconnect the VM from the internet, and run the installer. + +**Acceptance Scenarios**: + +1. **Given** an offline machine and the offline installer media, **When** the user runs the installer, **Then** it installs prerequisites from the local source without attempting to download them. +2. **Given** the installation completes, **When** the user checks the installed programs, **Then** all components are present. + +### Edge Cases + +- **Build Environment**: What happens if the build machine lacks the specific .NET SDK version required by WiX v6? (Build should fail with a clear error message). +- **Upgrade**: The installer MUST perform a **Major Upgrade** (Seamless Upgrade), automatically removing the previous version before installing the new one. +- **Localization**: What happens if a specific locale build fails? (The entire build process should report the error). + +## Requirements *(mandatory)* + +### Functional Requirements + +- **FR-001**: The WiX 6 build path MUST use WiX Toolset v6 for generating all WiX 6 installer artifacts. +- **FR-002**: WiX 6 build logic MUST be migrated to MSBuild .targets files. +- **FR-003**: The WiX 6 build path MUST NOT depend on the genericinstaller git submodule; all necessary code/config from it MUST be migrated into the main repository. +- **FR-004**: The installer MUST support both online (downloading) and offline (bundled) installation of prerequisites. +- **FR-005**: The installer MUST include all existing features, components, and localization variants (roughly a dozen features). +- **FR-006**: The GitHub Actions workflow MUST be updated to install WiX v6 and execute the new build targets. +- **FR-007**: Custom actions currently provided by genericinstaller MUST be re-implemented or migrated to be compatible with WiX v6. +- **FR-008**: The build process MUST support passing parameters (overrides) via MSBuild properties to customize the installer, replacing the old batch file parameter mechanism. +- **FR-009**: The build process MUST support code signing of the generated MSI and Bundle artifacts, configurable via environment variables (e.g., for certificate path/password). +- **FR-010**: The installer UI MUST be ported from the existing custom implementation to maintain the "Dual Directory" selection (App + Project Data) and custom feature tree behavior. +- **FR-011**: The build process MUST automatically download required prerequisites (e.g., .NET runtimes, C++ redistributables) during the build phase using MSBuild targets, rather than relying on pre-existing files. + +## Transition Plan: Parallel WiX 3 + WiX 6 Installers (NEW) + +**Goal**: Run WiX 3 and WiX 6 installers in parallel for a transition period, with WiX 3 as the **default** installer build, and WiX 6 as an opt-in path. + +### Transitional Requirements + +- **TR-001**: The build system MUST support building **either WiX 3 or WiX 6** installers via an explicit MSBuild property or build script flag. +- **TR-002**: The **default installer build** (no property/flag) MUST produce the **WiX 3** installer artifacts. +- **TR-003**: WiX 3 build inputs MUST be preserved and isolated from WiX 6 schema changes (no shared `.wxi` files between toolsets). +- **TR-004**: CI MUST build WiX 3 by default, with an additional opt-in path for WiX 6 builds. +- **TR-005**: Documentation MUST clearly describe how to build **WiX 3 (default)** and **WiX 6 (opt-in)** installers, including artifact locations. + +**Note**: During the transition, **FR-001/FR-002/FR-003 apply to the WiX 6 path only**. The WiX 3 path intentionally retains its legacy build flow until the transition ends. + +### Changes in this spec that can break WiX 3 (must be reversed or isolated) + +- **WiX 6 schema changes to `FLExInstaller/*.wxi`** (namespace and element changes) make those files incompatible with WiX 3. +- **`Build/Installer.targets` rewritten for WiX 6** and **WiX 3 batch pipelines removed/quarantined** (see tasks T040–T043, T030–T031). +- **Removal of `PatchableInstaller`/`genericinstaller` assumptions** eliminates WiX 3 build inputs and scripts. +- **Custom action wiring changed to WiX 4+ binaries** (e.g., `Wix4UtilCA_X64`), incompatible with WiX 3. + +### Files/folders to restore from `release/9.3` worktree (WiX 3) + +Pull these **verbatim** from the `release/9.3` worktree to re-introduce the WiX 3 installer project: + +- `Build/Installer.targets` (WiX 3 build orchestration, batch script invocation, and staging rules) +- `FLExInstaller/*.wxi` (WiX 3-compatible includes, kept in root to minimize changes from `release/9.3`) +- `PatchableInstaller/` (full tree) + - `BaseInstallerBuild/` (WiX 3 authoring, dialogs, batch scripts) + - `Common/` (shared includes/templates) + - `CustomActions/` (legacy CA project and build scripts) + - `ProcRunner/` + - `CreateUpdatePatch/` + - `libs/` + - `resources/` + - `Directory.Build.props`, `README.md`, `.gitignore`, `.gitattributes` + +### What else must change (high-level) + +- **Split installer inputs by toolset** with **WiX 3 in `FLExInstaller/` root** and **WiX 6 under `FLExInstaller/wix6/`**, and prevent cross-use. +- **Relocate WiX 6 assets** (projects + shared authoring) under `FLExInstaller/wix6/` to avoid collisions with WiX 3 authoring. +- **Add toolset selection property** (e.g., `InstallerToolset=Wix3|Wix6`) to `build.ps1` and `Build/Orchestrator.proj` with **default = Wix3**. +- **Provide dual build targets** in MSBuild (e.g., `BuildInstallerWix3`, `BuildInstallerWix6`). +- **Update documentation + CI** so WiX 3 is the default build and WiX 6 is opt-in. + +### Success Criteria + +- **Build Success**: WiX 3 (default) and WiX 6 (opt-in) installers both build successfully on developer machines and CI. +- **No Legacy Submodule**: The genericinstaller submodule remains removed, even though `PatchableInstaller/` is restored in-tree for WiX 3. +- **Functional Parity**: Both installer paths provide the same installation options (features, locales, offline/online) as the previous version. +- **Modernization (WiX 6 path)**: WiX 6 build uses MSBuild + WiX 6 tools only, with no legacy batch scripts in the WiX 6 path. + +### Assumptions + +- The existing WiX 3.11 source code is largely compatible with WiX v6 or can be automatically converted/easily patched. +- The genericinstaller submodule content is available for migration. +- WiX v6 supports the specific prerequisites (e.g., .NET Framework versions) required by the application. +- The team accepts the "breaking changes" inherent in moving to WiX v6 (e.g., different CLI tools, different project file format). + +### Key Entities + +- **Installer Bundle**: The executable that manages prerequisites and the MSI installation. +- **MSI Package**: The core product installer containing files and registry keys. +- **Build Target**: The MSBuild definition that orchestrates the WiX build process. + +## Clarifications + +### Session 2025-12-11 +- Q: How should the installer handle upgrades from the previous v3-based version? → A: Seamless Upgrade (MajorUpgrade). +- Q: Should the build process support code signing of the installer artifacts? → A: Yes, via Environment Variables. +- Q: How should installer prerequisites (e.g., .NET runtimes) be handled during the build? → A: Yes, via MSBuild Targets (auto-download). + +- Q: How should the custom installer UI (Dual Directory selection, etc.) be handled in WiX v6? → A: Port Existing Custom UI. \ No newline at end of file diff --git a/specs/001-wix-v6-migration/tasks.md b/specs/001-wix-v6-migration/tasks.md new file mode 100644 index 0000000000..5932695900 --- /dev/null +++ b/specs/001-wix-v6-migration/tasks.md @@ -0,0 +1,195 @@ +# Tasks: WiX v6 Migration + +**Feature**: WiX v6 Migration +**Status**: Planned +**Spec**: [specs/001-wix-v6-migration/spec.md](specs/001-wix-v6-migration/spec.md) + +## Phase 1: Setup (Project Initialization) + +**Goal**: Initialize the new WiX v6 project structure and migrate shared code from `genericinstaller`. + +- [x] T001 Create `FLExInstaller/Shared` directory structure +- [x] T002 Copy `PatchableInstaller/BaseInstallerBuild` content to `FLExInstaller/Shared/Base` +- [x] T003 Copy `PatchableInstaller/CustomActions` content to `FLExInstaller/Shared/CustomActions` +- [x] T036 Copy `PatchableInstaller/ProcRunner` content to `FLExInstaller/Shared/ProcRunner` +- [x] T004 Create `FLExInstaller/wix6/FieldWorks.Installer.wixproj` (SDK-style) +- [x] T005 Create `FLExInstaller/wix6/FieldWorks.Bundle.wixproj` (SDK-style) +- [x] T006 Create `Build/Installer.targets` skeleton + +## Phase 0b: Parallel WiX 3 + WiX 6 Transition (NEW) + +**Goal**: Reintroduce WiX 3 installer inputs, keep WiX 6 available, and make WiX 3 the default build. + +- [x] T080 Restore `Build/Installer.targets` from `release/9.3` as `Build/Installer.Wix3.targets` (preserve legacy batch flow) +- [x] T081 Restore `FLExInstaller/*.wxi` from `release/9.3` into `FLExInstaller/` root (WiX 3 stays in main path) +- [x] T082 Restore `PatchableInstaller/` tree from `release/9.3` (BaseInstallerBuild, Common, CustomActions, ProcRunner, CreateUpdatePatch, libs, resources, props/README) +- [x] T083 Move WiX 6 assets under `FLExInstaller/wix6/` (projects + shared authoring) +- [x] T084 Add `InstallerToolset=Wix3|Wix6` selection to `build.ps1` and `Build/Orchestrator.proj` (default **Wix3**) +- [x] T085 Add explicit targets `BuildInstallerWix3` and `BuildInstallerWix6` with `BuildInstaller` routed to Wix3 by default +- [x] T086 Ensure WiX 3 and WiX 6 include paths are isolated (root vs `FLExInstaller/wix6/Shared/`) +- [x] T087 Update documentation for dual builds (ReadMe, quickstart, `FLExInstaller/COPILOT.md`) and call out default Wix3 +- [x] T088 Update CI to build Wix3 by default and add an opt-in Wix6 lane +- [x] T089 Add guardrails that fail the Wix3 build if WiX 6 namespaces/refs leak into Wix3 inputs +- [x] T090 Define MSBuild override properties for installer customization (FR-008) and document them for both toolsets + +## Phase 2: Foundational (Blocking Prerequisites) + +**Goal**: Convert core WiX source files and Custom Actions to be compatible with WiX v6. + +- [x] T007 [P] Convert `FLExInstaller/*.wxs` and `*.wxl` files to WiX v6 syntax using `wix convert` +- [x] T008 [P] Convert `FLExInstaller/Shared/Base/*.wxs` and `*.wxl` files to WiX v6 syntax +- [x] T009 [P] Update Custom Actions project to target .NET 4.8 +- [x] T037 [P] Update `ProcRunner` project to target .NET 4.8 +- [x] T010 Implement `DownloadPrerequisites` target in `Build/Installer.targets` (FR-011) +- [x] T038 Replace WiX 3 `heat.exe` harvesting with a pinned WixToolset.Heat v6 (NuGet-delivered) approach +- [x] T011 Port `GIInstallDirDlg.wxs` (Dual Directory UI) to WiX v6 +- [x] T012 Port `GICustomizeDlg.wxs` (Custom Feature Tree) to WiX v6 +- [x] T013 Verify `WixUI_DialogFlow.wxs` compatibility with `WixToolset.UI.wixext` + +### Phase 2a: Toolchain Purge (WiX 6-only) + +**Goal**: Remove all remaining WiX 3 build-time tool dependencies and legacy build scripts from the active build path. + +- [x] T040 Ensure installer build does not rely on WiX 3 binaries on PATH (harvesting uses WixToolset.Heat v6 from NuGet) +- [x] T041 Remove reliance on `candle.exe`/`light.exe`/`insignia.exe` in any invoked build step (MSBuild/CI) +- [x] T042 Remove or quarantine legacy `build*.bat` scripts under `FLExInstaller/Shared/Base/` so they cannot be mistaken as source-of-truth +- [x] T043 Add a pinned, reproducible acquisition path for WiX 6 tooling (NuGet/MSBuild SDK) used by both local builds and CI + +## Phase 3: User Story 1 - Developer Builds Installer (P1) + +**Goal**: Enable developers to build the installer locally using MSBuild. + +- [x] T014 [US1] Implement `BuildInstaller` target in `Build/Installer.targets` +- [x] T015 [US1] Configure `FieldWorks.Installer.wixproj` to reference `Shared` components +- [x] T016 [US1] Configure `FieldWorks.Bundle.wixproj` to reference the MSI +- [x] T017 [US1] Implement Code Signing logic in `FieldWorks.Installer.wixproj` (FR-009) +- [x] T018 [US1] Implement Code Signing logic in `FieldWorks.Bundle.wixproj` (FR-009) +- [x] T019 [US1] Verify local build produces `FieldWorks.msi` and `FieldWorksBundle.exe` + +### Phase 3a: Local Verification (WiX6-only proof) + +**Goal**: Prove a developer can build the installer without any external `genericinstaller` repo and without WiX 3 installed. + +- [ ] T044 [US1] Verify build succeeds on a machine with *no* `genericinstaller` checkout present +- [ ] T045 [US1] Verify build succeeds with *no* WiX 3 installed (or at minimum: WiX 3 tools not present on PATH) +- [x] T046 [US1] Record artifact locations + names (MSI, bundle EXE, wixpdb) in a short checklist under this spec +- [x] T066 [US1] Add small-grained installer artifact verification tests (non-installing): MSI properties + artifact presence + +## Phase 4: User Story 2 - CI Builds Installer (P1) + +**Goal**: Automate the installer build in CI. + +- [ ] T020 [US2] Update `.github/workflows/main.yml` (or relevant workflow) to install WiX v6 +- [ ] T021 [US2] Update CI workflow to call `msbuild Build/Orchestrator.proj /t:BuildInstaller` +- [ ] T022 [US2] Configure CI environment variables for Code Signing +- [ ] T023 [US2] Verify CI build artifacts are uploaded + +### Phase 4a: CI Verification (Parity + provenance) + +**Goal**: CI proves the installer is fully WiX 6-based and does not rely on external templates. + +- [ ] T047 [US2] Add a CI check that fails if the build invokes WiX 3 tools (`heat.exe`, `candle.exe`, `light.exe`, `insignia.exe`) +- [ ] T048 [US2] Add a CI check that fails if any build step requires a `PatchableInstaller/` directory or `genericinstaller` checkout + +## Phase 5: User Story 3 - End User Installation (Online) (P1) + +**Goal**: Ensure the installer works for online users (downloading prerequisites). + +- [ ] T024 [US3] Configure Bundle `Chain` to enable downloading of prerequisites +- [ ] T025 [US3] Verify `Redistributables.wxi` URLs are valid and accessible +- [ ] T026 [US3] Test Online Install on clean VM (Manual Verification) +- [ ] T034 [US3] Verify localized installer builds (French, Spanish, etc.) +- [ ] T035 [US3] Test Major Upgrade from previous version (Optional) + +### Phase 5a: Online Install Validation (Behavioral parity) + +**Goal**: Validate installer UX + behavior parity (paths, registry, shortcuts, env vars) for the online scenario. + +- [ ] T049 [US3] Validate dual-directory UX (App + Data) on clean VM (choose non-default paths; verify install succeeds) +- [ ] T050 [US3] Validate feature selection UI (select/deselect features; verify payload matches selection) +- [ ] T051 [US3] Validate registry keys/values are written and removed on uninstall (including upgrade scenarios) +- [ ] T052 [US3] Validate shortcuts (desktop + start menu + uninstall) and URL protocol registration (`silfw:`) +- [ ] T053 [US3] Validate environment variables are set and removed correctly (including PATH modifications) +- [ ] T054 [US3] Validate custom action behavior (app-close, path checks) and confirm no modal assertion UI blocks installs + +## Phase 6: User Story 4 - End User Installation (Offline) (P2) + +**Goal**: Ensure the installer works for offline users (bundled prerequisites). + +- [ ] T027 [US4] Configure Bundle to support offline layout creation +- [ ] T028 [US4] Verify `msbuild /t:Publish` (or similar) creates offline layout +- [ ] T029 [US4] Test Offline Install on disconnected VM (Manual Verification) + +### Phase 6a: Offline Bundle Wiring (WiX 6) + +**Goal**: Produce an offline-capable bundle from WiX 6 build outputs (not legacy candle/light scripts). + +- [ ] T055 [US4] Wire `OfflineBundle.wxs` into the WiX 6 build (second bundle project or conditional compile/output) +- [ ] T056 [US4] Define the offline distribution format (single EXE vs layout folder) and make it reproducible via MSBuild target +- [ ] T057 [US4] Verify offline prerequisites are embedded/available (NetFx, VC++ redists, FLEx Bridge offline) + +## Phase 7: Polish & Cross-Cutting Concerns + +**Goal**: Cleanup and documentation. + +- [x] T030 Remove `PatchableInstaller` directory +- [x] T031 Remove `genericinstaller` submodule reference from `.gitmodules` +- [x] T032 Update `FLExInstaller/COPILOT.md` with new build instructions +- [x] T033 Update `ReadMe.md` with new build instructions +- [x] T039 Create WiX 3.x → WiX 6 parity audit report (`specs/001-wix-v6-migration/wix3-to-wix6-audit.md`) + +### Phase 7a: Remove External Template Dependency (genericinstaller) + +**Goal**: Ensure the repository no longer instructs or requires developers/CI to fetch `genericinstaller`/`PatchableInstaller`. + +- [x] T058 Remove any developer setup steps that clone/expect `PatchableInstaller` (e.g., `Setup-Developer-Machine.ps1`) +- [x] T059 Remove workspace configuration references that pull `genericinstaller` into the worktree (optional but recommended) +- [x] T060 Repo-wide verification: no remaining references to `PatchableInstaller` in active build paths (docs/scripts/targets) + +## Phase 8: Validation & Regression Proof + +**Goal**: Provide repeatable evidence that WiX 6 implementation fully replaces the legacy template and behaves identically for users. + +- [x] T061 Create verification matrix: `specs/001-wix-v6-migration/verification-matrix.md` +- [x] T062 Add golden install checklist: `specs/001-wix-v6-migration/golden-install-checklist.md` +- [ ] T067 Create and run WiX3→WiX6 parity check procedure (`specs/001-wix-v6-migration/parity-check.md`) +- [ ] T063 Capture and archive installer logs for each scenario (MSI log + bundle log) and document where to find them +- [ ] T064 Smoke-test upgrade from a previous released FieldWorks version on VM and verify data-path lock behavior +- [ ] T065 Validate localization: build and run at least one non-English installer UI end-to-end + +### Phase 8a: Sustainable Clean-Machine Verification (CI) + +**Goal**: Make clean-machine installer verification repeatable and sustainable. + +- [x] T068 Adopt C# 8 language version repo-wide (keep nullable disabled initially) +- [ ] T069 Add CI installer verification on `windows-latest` (build installer, run installer check, upload evidence artifacts) +- [x] T071 Add installer evidence tooling scripts (snapshot + compare + check) and wire them into VS Code tasks + +### Phase 8b: Local Development PC Verification (No Sandbox/Hyper-V) + +**Goal**: Validate WiX3 and WiX6 installers directly on the local development PC and capture evidence without sandboxing. + +- [ ] T091 Define local validation checklist for Wix3 and Wix6 (install/upgrade/uninstall + log capture on dev PC) +- [ ] T092 Run Wix3 default installer locally and archive MSI/bundle logs +- [ ] T093 Run Wix6 opt-in installer locally and archive MSI/bundle logs +- [ ] T094 Document evidence locations and expectations for local-only testing in spec docs + + +## Dependencies + +1. **Phase 1 & 2** must be completed before any User Story phases. +2. **Phase 3 (US1)** is a prerequisite for **Phase 4 (US2)**. +3. **Phase 3 (US1)** is a prerequisite for **Phase 5 (US3)** and **Phase 6 (US4)**. + +## Parallel Execution Opportunities + +- **T007, T008, T009** (Conversion tasks) can be done in parallel. +- **T011, T012** (UI Porting) can be done in parallel. +- **T017, T018** (Signing logic) can be done in parallel. + +## Implementation Strategy + +1. **MVP**: Complete Phases 1, 2, and 3. This gives a working local build. +2. **CI Integration**: Complete Phase 4. +3. **Validation**: Complete Phases 5 and 6 to ensure end-user scenarios work. +4. **Cleanup**: Complete Phase 7. diff --git a/specs/001-wix-v6-migration/verification-matrix.md b/specs/001-wix-v6-migration/verification-matrix.md new file mode 100644 index 0000000000..61c1698437 --- /dev/null +++ b/specs/001-wix-v6-migration/verification-matrix.md @@ -0,0 +1,55 @@ +# WiX 6 Migration Verification Matrix (FieldWorks) + +**Purpose**: Provide repeatable, reviewable verification that the WiX 6 implementation fully replaces the legacy WiX 3.x behavior. + +**How to use this document** +- Treat each row as a *claim* that must be proven. +- For each row, attach evidence (logs/screenshots/file listings) and record where it lives. +- Prefer running on a clean VM snapshot and keeping evidence in a consistent location (e.g., `C:\Temp\FwInstallerEvidence\\...`). + +## Evidence conventions + +- **Bundle log**: run the bundle with a log switch (recommended): `FieldWorks.exe /log C:\Temp\FwInstallerEvidence\bundle.log` +- **MSI log**: run MSI with verbose logging: `msiexec /i FieldWorks.msi /l*v C:\Temp\FwInstallerEvidence\msi-install.log` +- **Uninstall log**: `msiexec /x {PRODUCT-CODE} /l*v C:\Temp\FwInstallerEvidence\msi-uninstall.log` (or uninstall via ARP and capture bundle/MSI logs if possible) + +> Note: exact command-line switches can vary by bootstrapper; if `/log` is not accepted, capture `%TEMP%` logs and record their names/paths in the Evidence field. + +## Matrix + +| Area | Requirement / expected outcome | Verification method | Evidence to capture | Status | +|---|---|---|---|---| +| Toolchain | Build/install does not require `genericinstaller` checkout | Build in a fresh clone/worktree without genericinstaller present | Build logs showing success + no missing path errors | ☐ | +| Toolchain | Build does not invoke WiX 3 tools (`candle.exe`, `light.exe`, `insignia.exe`) | Run build with command echoing enabled (or CI check) and search logs | Build logs (or CI log) demonstrating absence of those tool names (note: WiX v6 Heat is currently expected and emits `HEAT5149`) | ☐ | +| Build outputs | Local build produces expected artifacts | Build installer and confirm output filenames/locations | File listing of output folder + hashes | ☑ | +| Staging | Installer input staging copies correct payload | Inspect staged input folder(s) and compare to expected | Folder tree / file listing of staged inputs | ☑ | +| Harvesting | MSI contains harvested app files under APPFOLDER | Install, then verify files exist under chosen APPFOLDER; optionally inspect MSI tables | MSI log + file listing under install dir | ☐ | +| Harvesting | MSI contains harvested data files under DATAFOLDER | Install with non-default data dir and verify data payload exists | MSI log + file listing under data dir | ☐ | +| UI (dual dirs) | Dual-directory UI allows choosing App + Data directories | Run bundle/MSI; select custom App + Data paths; verify install uses them | Screenshots of dialog selections + MSI log showing resolved properties | ☐ | +| UI (features) | Feature selection UI matches expected feature tree | Choose a non-default feature subset and verify installed components reflect it | Screenshots + MSI log showing feature states | ☐ | +| UI flow | Dialog flow and navigation behave correctly | Run through install and confirm Back/Next/Cancel behavior | Bundle log + screenshots of key dialogs | ☐ | +| Upgrade | Major upgrade removes prior version without side-by-side | Install old version, then install new version; verify single entry + expected behavior | MSI/bundle logs for upgrade + ARP screenshot | ☐ | +| Upgrade | Data path lock behavior on upgrade (if required) | Upgrade with existing data folder and confirm rules (e.g., data path fixed) | MSI/bundle logs + screenshot of any explanation text | ☐ | +| Registry | Install writes expected HKLM keys/values (paths + version) | After install, inspect registry values | Export of relevant registry keys (reg export) | ☐ | +| Registry | Uninstall removes expected registry keys/values | Uninstall; confirm keys removed | Export before/after + uninstall log | ☐ | +| Shortcuts | Desktop shortcut installed when expected | Install with defaults; verify desktop link target | Screenshot + link properties (or file listing) | ☐ | +| Shortcuts | Start menu shortcuts installed (docs/tools/help) | Verify Start Menu folder contains expected links | Screenshot/file listing | ☐ | +| Protocol | `silfw:` URL protocol registration works | After install: run `start silfw:test` or click a test link | Registry export + observed behavior note | ☐ | +| Env vars | Environment variables are created/updated correctly | Check System Environment Variables (and PATH modifications) | Screenshot/export of env vars + reboot/logoff note | ☐ | +| Env vars | Env vars removed/restored on uninstall | Uninstall; confirm env vars removed and PATH restored (as appropriate) | Before/after env snapshot + uninstall log | ☐ | +| Custom actions | Custom actions run without blocking UI (no modal asserts) | Run install/uninstall/upgrade; check for assertion dialogs/hangs | Bundle/MSI logs + any trace log if enabled | ☐ | +| Prereqs (online) | Online bundle downloads prerequisites successfully | Install on VM with internet; observe downloads succeed | Bundle log + screenshot of progress | ☐ | +| Prereqs (detect) | Prereq detection works (skips if already installed) | Preinstall VC++/.NET; run bundle and confirm it skips | Bundle log showing detection results | ☐ | +| FLEx Bridge | Offline FLEx Bridge prerequisite is detected/installed as expected | Validate on clean VM and on VM with existing FLEx Bridge | Bundle log + registry evidence | ☐ | +| Theme/license | Bundle theme and license UX appear correct | Run bundle and confirm theme + license link/text | Screenshot(s) | ☐ | +| Offline mode | Offline installer story works end-to-end | Build offline artifact/layout; install on disconnected VM | Bundle log + proof no network required | ☐ | +| Localization | At least one non-English locale builds and runs end-to-end | Build localized bundle/MSI; run and confirm UI strings load | Screenshot(s) + artifact listing | ☐ | +| Signing | Signing is applied where required | Inspect signatures of MSI/EXE; ensure verification passes | Sigcheck output or file properties screenshots | ☐ | + +## Notes / deviations + +Record any deviations from expected behavior here, with links to logs/evidence and any follow-up tasks. + +- 2025-12-17: Release build outputs + staging roots captured in [specs/001-wix-v6-migration/parity-check.md](specs/001-wix-v6-migration/parity-check.md) (hashes + staging root). +- 2025-12-17: Bundle log captured at `Output\InstallerEvidence\20251217-local-bundle-passive\bundle.log` (local run; not a clean VM). +- 2025-12-17: FLEx Bridge package is now present in the bundle chain: `Output\InstallerEvidence\flexbridge-parity\bundle.log` contains `Detected package: FBInstaller` and `Planned package: FBInstaller` (still needs clean-VM behavior validation). diff --git a/specs/001-wix-v6-migration/wix3-to-wix6-audit.md b/specs/001-wix-v6-migration/wix3-to-wix6-audit.md new file mode 100644 index 0000000000..19074ef2fd --- /dev/null +++ b/specs/001-wix-v6-migration/wix3-to-wix6-audit.md @@ -0,0 +1,145 @@ +# WiX 3.x → WiX 6 Migration Audit (FieldWorks) + +**Branch audited:** `001-wix-v6-migration` + +**How to verify this audit** + +- Parity procedure: [parity-check.md](parity-check.md) +- Evidence tracking: [verification-matrix.md](verification-matrix.md) +- Manual VM runs: [golden-install-checklist.md](golden-install-checklist.md) + +## Executive summary + +This worktree contains a WiX 6 (SDK-style) MSI + Burn bundle implementation with the critical installer UX pieces (dual-directory UI + feature tree) and the key registry/shortcut behaviors implemented in WiX authoring. + +However, this is not yet a fully “WiX 6-native” build pipeline: + +- The MSI project still **uses WiX 3 `heat.exe`** to harvest binaries/data, then runs **WiX 6 `wix.exe`** to compile the converted harvest output. +- The legacy WiX 3 batch scripts are still present (and appear to describe the historical pipeline), but the new “source of truth” build path is via MSBuild targets + the SDK-style `.wixproj` files. +- Several spec tasks remain incomplete (notably: end-to-end local artifact verification, CI integration, online/offline end-user validation). + +## Scope of audit + +Per request, this audit focuses on: + +- **UX parity** (dialogs and dialog flow, including the dual-directory selection) +- **File payload parity** (what gets staged and harvested into the MSI) +- **Registry parity** (keys/values written, detection searches) +- **Installer build system parity** (how WiX 3 produced artifacts vs how WiX 6 produces them now) + +## Baseline availability / limitations + +You requested using commit `6a2d976e` as the WiX 3.11 baseline. In the FieldWorks repo at that commit (and also on `release/9.3`), the installer’s **product-specific includes** existed (`FLExInstaller/*.wxi`), but the **base WiX authoring** (`*.wxs`, dialogs, bundle theme, etc.) did not live in-tree. + +If you have the `genericinstaller/` repo available externally, you *can* point to the WiX 3.x baseline authoring directly: + +- **WiX 3.x template authoring:** `genericinstaller/BaseInstallerBuild/*` and `genericinstaller/Common/*` +- **FieldWorks-specific WiX 3.x customization:** `FLExInstaller/*.wxi` as of commit `6a2d976e` (these are the same functional “custom include” files that were historically layered on top of the generic template) + +This audit therefore treats “WiX 3.x” as **(FieldWorks FLExInstaller includes at `6a2d976e`)** and (optionally) **(genericinstaller template)**, and compares that against the current WiX 6 implementation in this worktree. + +> Note: this worktree does not currently include a `genericinstaller/` folder; without it, comparisons to the generic template are limited to the in-repo `FLExInstaller/*.wxi` baseline. + +## Where things live now (WiX 6) + +### Build orchestration + +- MSBuild target entry points: `Build/Installer.targets` +- MSI project: `FLExInstaller/wix6/FieldWorks.Installer.wixproj` +- Bundle project: `FLExInstaller/wix6/FieldWorks.Bundle.wixproj` + +### WiX authoring (MSI + bundle) + +- MSI package authoring: `FLExInstaller/wix6/Shared/Base/Framework.wxs` +- UI flow: `FLExInstaller/wix6/Shared/Base/WixUI_DialogFlow.wxs` +- Custom dialogs: `FLExInstaller/wix6/Shared/Base/GIInstallDirDlg.wxs`, `GICustomizeDlg.wxs`, `GISetupTypeDlg.wxs`, `GIWelcomeDlg.wxs`, `GIProgressDlg.wxs` +- Bundle authoring: `FLExInstaller/wix6/Shared/Base/Bundle.wxs` +- Bundle theme: `FLExInstaller/wix6/Shared/Base/BundleTheme.xml`, `BundleTheme.wxl` +- MSI localization: `FLExInstaller/wix6/Shared/Base/WixUI_en-us.wxl` + +**Includes used by the WiX 6 build (referenced by `Shared/Base/*.wxs`):** + +- `FLExInstaller/wix6/Shared/Common/CustomActionSteps.wxi` +- `FLExInstaller/wix6/Shared/Common/CustomComponents.wxi` +- `FLExInstaller/wix6/Shared/Common/CustomFeatures.wxi` +- `FLExInstaller/wix6/Shared/Common/Overrides.wxi` +- `FLExInstaller/wix6/Shared/Common/Redistributables.wxi` + +> Note: the repo also contains `FLExInstaller/*.wxi` at the top level; these are the historical FieldWorks include files and are useful as the WiX 3.x baseline reference, but the current WiX 6 authoring under `Shared/Base/` includes the `Shared/Common/` copies. + +### Custom actions and helper binaries + +- Custom Actions (CA): `FLExInstaller/wix6/Shared/CustomActions/CustomActions/CustomActions.csproj` +- ProcRunner: `FLExInstaller/wix6/Shared/ProcRunner/ProcRunner/ProcRunner.csproj` + +## Component-by-component parity map + +> Legend for **Parity**: +> - **PASS**: present and appears equivalent +> - **PARTIAL**: present but with notable divergence / risk +> - **GAP**: missing or not wired into the WiX 6 build + +| Component | WiX 3.x (legacy) implementation | WiX 6 (current) implementation | Parity | Notes / evidence | +|---|---|---|---|---| +| MSI build pipeline | `genericinstaller/BaseInstallerBuild/buildMsi.bat` (heat → candle/light → sign) | `FLExInstaller/wix6/FieldWorks.Installer.wixproj` (heat → `wix.exe convert` → SDK compile) | PARTIAL | Still depends on `heat.exe` (WiX 3). The “WiX 6 compile” is via WixToolset.Sdk. | +| Bundle build pipeline | `genericinstaller/BaseInstallerBuild/buildExe.bat` (candle/light, online+offline, insignia signing) | `FLExInstaller/wix6/FieldWorks.Bundle.wixproj` (SDK bundle build) + `StageBundlePayloads` downloads | PARTIAL | The WiX 6 project only compiles `Shared/Base/Bundle.wxs`; offline bundle output is not clearly produced. | +| Artifact orchestration | (historically batch-driven) `buildBaseInstaller.bat` chains `buildMsi.bat` + `buildExe.bat` | `Build/Installer.targets` provides `BuildInstaller` target | PASS | `BuildInstaller` depends on staging + prerequisites + ProcRunner build. | +| Input staging for harvest | (legacy assumed in external scripts; not in-repo baseline) | `Build/Installer.targets` target `StageInstallerInputs` | PASS | Copies Output + DistFiles + fonts + ICU + localizations into `BuildDir/FieldWorks_InstallerInput_*`. | +| Harvesting app files | `buildMsi.bat` uses `heat.exe dir %MASTERBUILDDIR% ... -cg HarvestedAppFiles ... -dr APPFOLDER` | `FieldWorks.Installer.wixproj` target `HarvestAppAndData` uses `heat.exe dir $(_MasterBuildDirFull) ... -cg HarvestedAppFiles ... -dr APPFOLDER` | PASS | Same component group ID + same directory ref pattern. | +| Harvesting data files | `buildMsi.bat` uses `heat.exe dir %MASTERDATADIR% ... -cg HarvestedDataFiles ... -dr HARVESTDATAFOLDER` | `FieldWorks.Installer.wixproj` target `HarvestAppAndData` uses `heat.exe dir $(_MasterDataDirFull) ... -cg HarvestedDataFiles ... -dr HARVESTDATAFOLDER` | PASS | Same component group ID + same directory ref pattern. | +| MSI major upgrade | (legacy expected) | `Framework.wxs` `` | PASS | Major upgrade is explicitly declared in MSI authoring. Downgrades are allowed; the newer product is removed early to avoid file-versioning blocks. | +| ARP/installer metadata | (legacy expected) | `Framework.wxs` sets `ARPPRODUCTICON`, `ARPNOREMOVE`, `DISPLAYNAME`, `FULL_VERSION_NUMBER` | PASS | Matches the typical FieldWorks behavior (remove disabled, branded icon). | +| Dual-directory UI (App + Data) | (legacy expected; custom dialogs) | `GIInstallDirDlg.wxs` + `Framework.wxs` sets `WIXUI_INSTALLDIR=APPFOLDER`, `WIXUI_PROJECTSDIR=DATAFOLDER` | PASS | Both directories are first-class and driven by custom UI. | +| Custom feature selection UI | (legacy expected; custom dialog) | `GICustomizeDlg.wxs` + `CustomFeatures.wxi` | PASS | Feature tree is defined in include and shown via dialog flow. | +| Dialog flow wiring | (legacy expected) | `Framework.wxs` ``; flow defined in `WixUI_DialogFlow.wxs` | PASS | Custom UI flow is explicitly referenced by MSI package. | +| Upgrade/path restrictions UX | (legacy expected) | `Framework.wxs` sets `EXPLANATIONTEXT` when upgrading and data folder is already existing | PASS | Text indicates data folder becomes fixed during upgrade. | +| Registry: install path + version | (legacy expected) | `Framework.wxs` writes HKLM `SOFTWARE\[REGISTRYKEY]` values for app folder + version | PASS | Component `RegKeyValues` writes `Program_Files_Directory_*` and `FieldWorksVersion`. | +| Registry: settings/data directories | (legacy expected) | `Framework.wxs` components `RegKeySettingsDir` and `HarvestDataDir` write HKLM values | PASS | Also uses `RegistrySearch` to read prior install values. | +| Registry: uninstall cleanup | (legacy expected) | `Framework.wxs` uses `ForceDeleteOnUninstall="yes"` for HKLM key | PASS | Combined with conditional CA `DeleteRegistryVersionNumber` on uninstall. | +| Shortcuts (desktop/start menu) | (legacy expected) | `Framework.wxs` components `ApplicationShortcutDesktop` and `ApplicationShortcutMenu` | PASS | Desktop + start menu + uninstall shortcut are present. | +| Start menu shortcuts (help/docs/utilities) | FieldWorks includes at `6a2d976e`: `FLExInstaller/CustomComponents.wxi` `StartMenuShortcuts` group | `FLExInstaller/Shared/Common/CustomComponents.wxi` `StartMenuShortcuts` group | PASS | Adds shortcuts like Help, Morphology Intro, Unicode Character Editor, EULA, and font documentation. | +| Environment variables | FieldWorks includes at `6a2d976e`: `FLExInstaller/CustomComponents.wxi` `FwEnvironmentVars` group | `FLExInstaller/Shared/Common/CustomComponents.wxi` `FwEnvironmentVars` group | PASS | Sets system env vars including `PATH` prefix, `FIELDWORKSDIR`, `ICU_DATA`, and `WEBONARY_API`. | +| URL protocol registration | FieldWorks includes at `6a2d976e`: `FLExInstaller/CustomComponents.wxi` registry under HKCR | `FLExInstaller/Shared/Common/CustomComponents.wxi` registry under HKCR | PASS | Registers `silfw` URL protocol and command handler. | +| ProcRunner install | (legacy expected) | `Framework.wxs` installs `ProcRunner_5.0.exe` under CommonFiles | PASS | Component `ProcRunner` is referenced by the main feature. | +| Custom actions (path checks, close apps, etc.) | (legacy expected) | `Framework.wxs` defines CA entries from `CustomActions.CA.dll` + includes `CustomActionSteps.wxi` | PASS | Core CA hooks present; detailed sequencing is inside include. | +| Bundle prerequisites: .NET 4.8 | `genericinstaller/BaseInstallerBuild/Bundle.wxs` defines `NetFx48Web` | `FLExInstaller/Shared/Base/Bundle.wxs` references `NetFx48Web` | PASS | Online bootstrapper includes netfx group. | +| Bundle prerequisites: VC++ redists | (legacy expected) | `Bundle.wxs` defines `redist_vc*` groups + `Redistributables.wxi` composes them | PASS | Uses registry detection for each VC redist. | +| Bundle prerequisite: FLEx Bridge offline | FieldWorks includes at `6a2d976e`: `FLExInstaller/Redistributables.wxi` adds `FLExBridge_Offline.exe` in `FlexBridgeInstaller` group | `FLExInstaller/Shared/Common/Redistributables.wxi` adds `FLExBridge_Offline.exe` | PASS | Detected via registry under `SIL\FLEx Bridge\9`. | +| Bundle theme + license UX | (legacy expected) | `Bundle.wxs` uses `WixStandardBootstrapperApplication Theme="hyperlinkLicense"` and theme variables | PASS | Theme + WXL are referenced; license is bundled from resources. | +| Offline bundle | `genericinstaller/BaseInstallerBuild/buildExe.bat` builds OfflineBundle via candle/light | `OfflineBundle.wxs` exists but is not compiled by `FieldWorks.Bundle.wixproj` | GAP | Offline bundle scenario appears not wired into current WiX 6 build output. | +| Code signing | `buildMsi.bat`/`buildExe.bat` call `signingProxy` and use `insignia` for bundle engine | `FieldWorks.Installer.wixproj`/`FieldWorks.Bundle.wixproj` call `signingProxy.bat` after build | PARTIAL | Bundle engine-signing parity with WiX 3/`insignia` is not demonstrated in the WiX 6 targets. | + +## Notable divergences / risks + +1. **Heat.exe dependency remains:** The MSI build depends on WiX 3 `heat.exe` for harvesting. This is likely intentional short-term, but it means a “pure WiX 6” toolchain isn’t achieved yet. +2. **Offline install story incomplete:** The presence of `OfflineBundle.wxs` and the legacy batch suggests offline bundles existed historically, but the current WiX 6 project compiles only `Bundle.wxs`. +3. **End-to-end verification not recorded:** Spec task `T019` (“Verify local build produces FieldWorks.msi and FieldWorks.exe”) is still unchecked; likewise CI integration tasks are unchecked. + +## Recommendations (to finish parity) + +- Wire offline bundle output into the WiX 6 build (either compile `OfflineBundle.wxs` as a second bundle project/output, or implement layout creation per the spec). +- Replace the custom `heat.exe` harvesting step with a WiX 6-compatible harvesting mechanism (or a controlled, version-pinned `heat.exe` tool acquisition step) so the build is reproducible. +- Complete `T019` + add a short “artifact checklist” (expected filenames + locations) so regressions are easy to spot. + +## Files reviewed + +- `genericinstaller/BaseInstallerBuild/Framework.wxs` +- `genericinstaller/BaseInstallerBuild/Bundle.wxs` +- `genericinstaller/BaseInstallerBuild/OfflineBundle.wxs` +- `genericinstaller/BaseInstallerBuild/GI*.wxs` +- `genericinstaller/BaseInstallerBuild/WixUI_DialogFlow.wxs` +- `genericinstaller/BaseInstallerBuild/buildMsi.bat` +- `genericinstaller/BaseInstallerBuild/buildExe.bat` +- FieldWorks (WiX 3.x includes) at `6a2d976e`: `FLExInstaller/CustomComponents.wxi`, `FLExInstaller/CustomFeatures.wxi`, `FLExInstaller/Redistributables.wxi` + +- `Build/Installer.targets` +- `FLExInstaller/wix6/FieldWorks.Installer.wixproj` +- `FLExInstaller/wix6/FieldWorks.Bundle.wixproj` +- `FLExInstaller/wix6/Shared/Base/Framework.wxs` +- `FLExInstaller/wix6/Shared/Base/WixUI_DialogFlow.wxs` +- `FLExInstaller/wix6/Shared/Base/GI*.wxs` +- `FLExInstaller/wix6/Shared/Base/Bundle.wxs` +- `FLExInstaller/wix6/Shared/Common/Redistributables.wxi` +- `FLExInstaller/wix6/Shared/Common/CustomComponents.wxi` +- `FLExInstaller/wix6/Shared/Common/CustomFeatures.wxi` +- `WIX_MIGRATION_STATUS.md` From 6455d07fdc2358cc088a8685c63cdfd962b8aa05 Mon Sep 17 00:00:00 2001 From: Hasso Date: Wed, 25 Feb 2026 15:51:26 -0600 Subject: [PATCH 3/7] fix & bandage; change Base build offset to 1000 1000 makes easier mental math than 1100, and we've burned 100 build numbers since our last release, so now is the time to reduce it. --- .github/workflows/base-installer-cd.yml | 4 ++-- .github/workflows/patch-installer-cd.yml | 3 +-- Build/Localize.targets | 3 +++ Build/SetupInclude.targets | 7 +++---- build.ps1 | 2 +- 5 files changed, 10 insertions(+), 9 deletions(-) diff --git a/.github/workflows/base-installer-cd.yml b/.github/workflows/base-installer-cd.yml index f2633291f8..c86f41684c 100644 --- a/.github/workflows/base-installer-cd.yml +++ b/.github/workflows/base-installer-cd.yml @@ -55,7 +55,7 @@ jobs: - name: Compute build number id: build_number run: | - $lastJenkins = 1100 # The last base build from jenkins, rounded to the next hundred + $lastJenkins = 1000 # The last base build from jenkins, rounded to the nearest thousand $githubRun = $env:GITHUB_RUN_NUMBER $combined = $lastJenkins + $githubRun echo "Calculated build number: $combined" @@ -103,6 +103,7 @@ jobs: fetch-depth: 0 path: 'Localizations/LCM' + # TODO (Hasso) 2026.02: use .NET 4.8 targeting pack (if not already installed) - name: Download .NET 461 targeting pack uses: suisei-cn/actions-download-file@818d6b7dc8fe73f2f924b6241f2b1134ca1377d9 # 1.6.0 id: downloadfile # Remember to give an ID if you need the output filename @@ -142,7 +143,6 @@ jobs: - name: Scan Build Output shell: powershell - working-directory: Build run: | $results = Select-String -Path "build.log" -Pattern "^\s*[1-9][0-9]* Error\(s\)" if ($results) { diff --git a/.github/workflows/patch-installer-cd.yml b/.github/workflows/patch-installer-cd.yml index 12e7b29901..c625c473d8 100644 --- a/.github/workflows/patch-installer-cd.yml +++ b/.github/workflows/patch-installer-cd.yml @@ -118,6 +118,7 @@ jobs: fetch-depth: 0 path: 'Localizations/LCM' + # TODO (Hasso) 2026.02: use .NET 4.8 targeting pack (if not already installed) - name: Download .NET 461 targeting pack uses: suisei-cn/actions-download-file@818d6b7dc8fe73f2f924b6241f2b1134ca1377d9 # 1.6.0 id: downloadfile # Remember to give an ID if you need the output filename @@ -185,7 +186,6 @@ jobs: # Set an OS feature in the registry that will allow Wix v3 to use temporary files without error - name: Prepare for build - working-directory: Build run: | # Define paths and the key/value to set $regPaths = @( @@ -212,7 +212,6 @@ jobs: - name: Scan Debug Build Output shell: powershell - working-directory: Build run: | $results = Select-String -Path "build.log" -Pattern "^\s*[1-9][0-9]* Error\(s\)" if ($results) { diff --git a/Build/Localize.targets b/Build/Localize.targets index d90bf81996..72eed307ba 100644 --- a/Build/Localize.targets +++ b/Build/Localize.targets @@ -36,6 +36,8 @@ $(ListsDirectory)/GramCats $(L10nsBaseDir)/messages.pot $(LcmRootDir)/src + true + $(fwrt)/Downloads $(DownloadsDir)/Crowdin.zip $(CROWDIN_API_KEY) true @@ -114,6 +116,7 @@ To update localizations, liblcm must be cloned locally to the location specified DependsOnTargets="ValidateCrowdinApiKey;InstallOvercrowdin" > + 70 - - $([System.IO.Directory]::GetParent($(MSBuildProjectDirectory))) + + $([System.IO.Directory]::GetParent($(MSBuildProjectDirectory))) $(MSBuildThisFileDirectory).. + $(fwrt)/Downloads