From 2f1d3eba0a75c34957eaace0986ea916e7dc41eb Mon Sep 17 00:00:00 2001 From: Rod Christiansen Date: Thu, 9 Apr 2026 10:57:19 -0700 Subject: [PATCH 1/2] build.ps1: switch packaging output from .pkg to .msi MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The new cimipkg (2026.04.09+) defaults to .msi output instead of .pkg. Update build.ps1 to match: rename the -Pkg switch to -Msi, update the output glob from ManageUsers-*.pkg to ManageUsers-*.msi, and rename log messages, variables, and staging paths to reflect the change. build-info.yaml is unchanged — cimipkg reads the same template and just produces a different output format based on its own defaults. --- build.ps1 | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/build.ps1 b/build.ps1 index 2799b62..c266a44 100644 --- a/build.ps1 +++ b/build.ps1 @@ -4,7 +4,7 @@ # # Examples: # .\build.ps1 # Build + sign for both architectures (default) -# .\build.ps1 -Pkg # Build, sign, and create .pkg with cimipkg +# .\build.ps1 -Msi # Build, sign, and create .msi with cimipkg # .\build.ps1 -Thumbprint "ABC123..." # Build with specific certificate # .\build.ps1 -AllowUnsigned # Development build without signing (NOT for production) # .\build.ps1 -Architecture arm64 # Build single architecture @@ -20,7 +20,7 @@ param( [switch]$AllowUnsigned, [switch]$ListCerts, [string]$FindCertSubject, - [switch]$Pkg + [switch]$Msi ) $ErrorActionPreference = 'Stop' @@ -248,7 +248,7 @@ Write-Host '=== ManageUsers Build ===' -ForegroundColor Magenta Write-Host "Version: $Version" -ForegroundColor Yellow Write-Host "Architecture: $Architecture" -ForegroundColor Yellow Write-Host "Code Signing: $(if ($AllowUnsigned) { 'DISABLED (dev only)' } else { 'REQUIRED' })" -ForegroundColor $(if ($AllowUnsigned) { 'Red' } else { 'Green' }) -Write-Host "Package: $(if ($Pkg) { 'YES (.pkg via cimipkg)' } else { 'No' })" -ForegroundColor $(if ($Pkg) { 'Green' } else { 'Gray' }) +Write-Host "Package: $(if ($Msi) { 'YES (.msi via cimipkg)' } else { 'No' })" -ForegroundColor $(if ($Msi) { 'Green' } else { 'Gray' }) if ($AllowUnsigned) { Write-Host '' Write-Host 'WARNING: Unsigned build - NOT suitable for production deployment' -ForegroundColor Red @@ -350,10 +350,10 @@ if ($SigningCert) { } } -# Package with cimipkg -if ($Pkg) { +# Package with cimipkg (new cimipkg defaults to .msi output) +if ($Msi) { Write-Host '' - Write-Log 'Building .pkg packages with cimipkg...' 'INFO' + Write-Log 'Building .msi packages with cimipkg...' 'INFO' $cimipkg = Get-Command cimipkg -ErrorAction SilentlyContinue if (-not $cimipkg) { @@ -368,8 +368,8 @@ if ($Pkg) { $buildInfoFile = Join-Path $RootDir 'build-info.yaml' $buildInfoTemplate = Get-Content -Path $buildInfoFile -Raw $payloadDir = Join-Path $RootDir 'payload' - $pkgStaging = Join-Path $env:TEMP "manageusers_pkg_$(Get-Date -Format 'yyyyMMddHHmmss')" - New-Item -ItemType Directory -Path $pkgStaging -Force | Out-Null + $msiStaging = Join-Path $env:TEMP "manageusers_msi_$(Get-Date -Format 'yyyyMMddHHmmss')" + New-Item -ItemType Directory -Path $msiStaging -Force | Out-Null foreach ($arch in $archs) { $sourceExe = Join-Path $OutputDir $arch 'manageusers.exe' @@ -387,22 +387,22 @@ if ($Pkg) { New-Item -ItemType Directory -Path $payloadDir -Force | Out-Null Copy-Item -Path $sourceExe -Destination (Join-Path $payloadDir 'manageusers.exe') -Force - # Run cimipkg - Write-Log "Building .pkg for $arch..." 'INFO' + # Run cimipkg — defaults to .msi in 2026.04.09+ + Write-Log "Building .msi for $arch..." 'INFO' & cimipkg $RootDir if ($LASTEXITCODE -ne 0) { Write-Log "cimipkg failed for $arch" 'ERROR' } else { - # Rescue .pkg from build/ before next cimipkg run wipes it - # cimipkg names it ManageUsers-{version}.pkg; rename to include arch - $pkgFile = Get-ChildItem -Path $buildDir -Filter 'ManageUsers-*.pkg' -File | + # Rescue .msi from build/ before next cimipkg run wipes it + # cimipkg names it ManageUsers-{version}.msi; rename to include arch + $msiFile = Get-ChildItem -Path $buildDir -Filter 'ManageUsers-*.msi' -File | Sort-Object LastWriteTime -Descending | Select-Object -First 1 - if ($pkgFile) { - $archName = $pkgFile.Name -replace '^ManageUsers-', "ManageUsers-${arch}-" + if ($msiFile) { + $archName = $msiFile.Name -replace '^ManageUsers-', "ManageUsers-${arch}-" $archPath = Join-Path $buildDir $archName - Rename-Item -Path $pkgFile.FullName -NewName $archName -Force - Move-Item -Path $archPath -Destination $pkgStaging -Force - $stagedFile = Get-Item (Join-Path $pkgStaging $archName) + Rename-Item -Path $msiFile.FullName -NewName $archName -Force + Move-Item -Path $archPath -Destination $msiStaging -Force + $stagedFile = Get-Item (Join-Path $msiStaging $archName) Write-Log "Created: $archName ($([math]::Round($stagedFile.Length / 1MB, 2)) MB)" 'SUCCESS' } } @@ -411,9 +411,9 @@ if ($Pkg) { Remove-Item $payloadDir -Recurse -Force -ErrorAction SilentlyContinue } - # Move staged .pkg files back to build/ - Get-ChildItem -Path $pkgStaging -Filter '*.pkg' -File | Move-Item -Destination $buildDir -Force - Remove-Item $pkgStaging -Recurse -Force -ErrorAction SilentlyContinue + # Move staged .msi files back to build/ + Get-ChildItem -Path $msiStaging -Filter '*.msi' -File | Move-Item -Destination $buildDir -Force + Remove-Item $msiStaging -Recurse -Force -ErrorAction SilentlyContinue # Restore build-info.yaml template with placeholders Set-Content -Path $buildInfoFile -Value $buildInfoTemplate -Encoding UTF8 -NoNewline @@ -430,11 +430,11 @@ foreach ($arch in $archs) { Write-Host " $arch : $exe ($size MB, $signed)" -ForegroundColor Cyan } } -if ($Pkg) { - $pkgFiles = Get-ChildItem -Path (Join-Path $RootDir 'build') -Filter '*.pkg' -File -ErrorAction SilentlyContinue | +if ($Msi) { + $msiFiles = Get-ChildItem -Path (Join-Path $RootDir 'build') -Filter '*.msi' -File -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending | Select-Object -First 2 - foreach ($pkgItem in $pkgFiles) { - Write-Host " pkg : $($pkgItem.FullName) ($([math]::Round($pkgItem.Length / 1MB, 2)) MB)" -ForegroundColor Green + foreach ($msiItem in $msiFiles) { + Write-Host " msi : $($msiItem.FullName) ($([math]::Round($msiItem.Length / 1MB, 2)) MB)" -ForegroundColor Green } } Write-Host " Version: $Version" -ForegroundColor Gray From 2a7007eb26ab8aeb15e7eb0603d2d9b3b6573a58 Mon Sep 17 00:00:00 2001 From: Rod Christiansen Date: Thu, 9 Apr 2026 11:49:32 -0700 Subject: [PATCH 2/2] Address Copilot review feedback Preserve backward compat and tighten cleanup semantics. - Add [Alias('Pkg')] on the -Msi switch so existing automation calling the script with -Pkg keeps working. cimipkg 2026.04.09+ produces .msi output regardless; the alias is purely for CLI ergonomics. - Wrap the packaging loop in try/finally so build-info.yaml is restored and the temp payload / staging dirs are removed even if a mid-loop Set-Content / Copy-Item / Rename-Item / Move-Item throws. Previously, a mid-loop failure could leave the repo with a stamped build-info.yaml and stale temp folders. --- build.ps1 | 89 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 49 insertions(+), 40 deletions(-) diff --git a/build.ps1 b/build.ps1 index c266a44..53edb32 100644 --- a/build.ps1 +++ b/build.ps1 @@ -20,6 +20,9 @@ param( [switch]$AllowUnsigned, [switch]$ListCerts, [string]$FindCertSubject, + # -Pkg retained as an alias so existing automation keeps working; + # cimipkg 2026.04.09+ produces .msi output by default. + [Alias('Pkg')] [switch]$Msi ) @@ -371,52 +374,58 @@ if ($Msi) { $msiStaging = Join-Path $env:TEMP "manageusers_msi_$(Get-Date -Format 'yyyyMMddHHmmss')" New-Item -ItemType Directory -Path $msiStaging -Force | Out-Null - foreach ($arch in $archs) { - $sourceExe = Join-Path $OutputDir $arch 'manageusers.exe' - if (-not (Test-Path $sourceExe)) { - Write-Log "Binary not found for ${arch}: $sourceExe — skipping" 'WARN' - continue - } - - # Stamp build-info.yaml with concrete architecture - $buildInfoContent = $buildInfoTemplate -replace '\$\{ARCH\}', $arch - Set-Content -Path $buildInfoFile -Value $buildInfoContent -Encoding UTF8 -NoNewline - - # Stage payload — only the signed binary - if (Test-Path $payloadDir) { Remove-Item $payloadDir -Recurse -Force } - New-Item -ItemType Directory -Path $payloadDir -Force | Out-Null - Copy-Item -Path $sourceExe -Destination (Join-Path $payloadDir 'manageusers.exe') -Force + # try/finally guarantees build-info.yaml is restored and the temp payload / + # staging dirs are cleaned even if a step mid-loop throws. + try { + foreach ($arch in $archs) { + $sourceExe = Join-Path $OutputDir $arch 'manageusers.exe' + if (-not (Test-Path $sourceExe)) { + Write-Log "Binary not found for ${arch}: $sourceExe — skipping" 'WARN' + continue + } - # Run cimipkg — defaults to .msi in 2026.04.09+ - Write-Log "Building .msi for $arch..." 'INFO' - & cimipkg $RootDir - if ($LASTEXITCODE -ne 0) { - Write-Log "cimipkg failed for $arch" 'ERROR' - } else { - # Rescue .msi from build/ before next cimipkg run wipes it - # cimipkg names it ManageUsers-{version}.msi; rename to include arch - $msiFile = Get-ChildItem -Path $buildDir -Filter 'ManageUsers-*.msi' -File | - Sort-Object LastWriteTime -Descending | Select-Object -First 1 - if ($msiFile) { - $archName = $msiFile.Name -replace '^ManageUsers-', "ManageUsers-${arch}-" - $archPath = Join-Path $buildDir $archName - Rename-Item -Path $msiFile.FullName -NewName $archName -Force - Move-Item -Path $archPath -Destination $msiStaging -Force - $stagedFile = Get-Item (Join-Path $msiStaging $archName) - Write-Log "Created: $archName ($([math]::Round($stagedFile.Length / 1MB, 2)) MB)" 'SUCCESS' + # Stamp build-info.yaml with concrete architecture + $buildInfoContent = $buildInfoTemplate -replace '\$\{ARCH\}', $arch + Set-Content -Path $buildInfoFile -Value $buildInfoContent -Encoding UTF8 -NoNewline + + # Stage payload — only the signed binary + if (Test-Path $payloadDir) { Remove-Item $payloadDir -Recurse -Force } + New-Item -ItemType Directory -Path $payloadDir -Force | Out-Null + Copy-Item -Path $sourceExe -Destination (Join-Path $payloadDir 'manageusers.exe') -Force + + # Run cimipkg — defaults to .msi in 2026.04.09+ + Write-Log "Building .msi for $arch..." 'INFO' + & cimipkg $RootDir + if ($LASTEXITCODE -ne 0) { + Write-Log "cimipkg failed for $arch" 'ERROR' + } else { + # Rescue .msi from build/ before next cimipkg run wipes it + # cimipkg names it ManageUsers-{version}.msi; rename to include arch + $msiFile = Get-ChildItem -Path $buildDir -Filter 'ManageUsers-*.msi' -File | + Sort-Object LastWriteTime -Descending | Select-Object -First 1 + if ($msiFile) { + $archName = $msiFile.Name -replace '^ManageUsers-', "ManageUsers-${arch}-" + $archPath = Join-Path $buildDir $archName + Rename-Item -Path $msiFile.FullName -NewName $archName -Force + Move-Item -Path $archPath -Destination $msiStaging -Force + $stagedFile = Get-Item (Join-Path $msiStaging $archName) + Write-Log "Created: $archName ($([math]::Round($stagedFile.Length / 1MB, 2)) MB)" 'SUCCESS' + } } } - # Clean up staged payload + # Move staged .msi files back to build/ (happy-path only; finally handles cleanup) + Get-ChildItem -Path $msiStaging -Filter '*.msi' -File -ErrorAction SilentlyContinue | + Move-Item -Destination $buildDir -Force + } + finally { + # Always restore the template and remove temp dirs, even if a step above threw. + if (Test-Path $buildInfoFile) { + Set-Content -Path $buildInfoFile -Value $buildInfoTemplate -Encoding UTF8 -NoNewline + } Remove-Item $payloadDir -Recurse -Force -ErrorAction SilentlyContinue + Remove-Item $msiStaging -Recurse -Force -ErrorAction SilentlyContinue } - - # Move staged .msi files back to build/ - Get-ChildItem -Path $msiStaging -Filter '*.msi' -File | Move-Item -Destination $buildDir -Force - Remove-Item $msiStaging -Recurse -Force -ErrorAction SilentlyContinue - - # Restore build-info.yaml template with placeholders - Set-Content -Path $buildInfoFile -Value $buildInfoTemplate -Encoding UTF8 -NoNewline } # Summary