PrintToVeraeTimeStamp/platform/windows/scripts/dual-ship-smoke.ps1
George Lambert 86809b27eb
Some checks failed
windows-msi / full-msi-unsigned (push) Has been cancelled
feat: enable-printer-cas.ps1 unlocks deferred printer CAs for VM C
Toggles Product-Full markers; dual-ship-smoke reports C blocked until enabled.
2026-10-14 00:47:09 -04:00

337 lines
14 KiB
PowerShell
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#Requires -Version 5.1
<#
.SYNOPSIS
Offline dual-ship smoke: print VM A/B/C checklist steps; optionally verify local MSI/NSIS + hash.
.DESCRIPTION
Operator helper for clean-VM dual-ship gates (NSIS primary + full MSI dual-ship).
Default mode prints the shared pre-checks and VM A / B / C steps from
platform/windows/wix/VM-DUAL-SHIP-CHECKLIST.md (narrative: DUAL-SHIP.md).
Does NOT install, uninstall, sign, upload, or drive a remote VM. It is a
printable offline runner for the host that built (or downloaded) artifacts.
With -VerifyArtifacts: look for NSIS setup.exe and/or full MSI under common
build paths (or -NsisPath / -MsiPath), report present/missing, then invoke
fill-manifest-sha256.ps1 for every file found (local SHA-256 only — no upload).
.EXITCODES
0 Checklist printed. If -VerifyArtifacts: at least one artifact found and hashed
(or -RequireBoth satisfied). Missing optional artifact is a warning only.
1 -VerifyArtifacts and no artifacts found; -RequireBoth and one leg missing;
fill-manifest failed; or terminating error.
.PARAMETER Version
Expected HelperVersion stamp (default: Get-VeraePackageVersion / 0.3.6).
.PARAMETER Arch
Artifact arch segment (default: amd64).
.PARAMETER NsisPath
Explicit NSIS setup.exe path. When omitted, searches dist/ and common out dirs.
.PARAMETER MsiPath
Explicit full MSI path. When omitted, searches wix/out and dist/.
.PARAMETER SearchRoot
Extra directory to search for VeraePrintStamp-windows-*-setup.exe / *.msi.
.PARAMETER VerifyArtifacts
Check local MSI/NSIS existence and hash via fill-manifest-sha256.ps1.
.PARAMETER RequireBoth
With -VerifyArtifacts: fail (exit 1) unless both NSIS and MSI are present.
.PARAMETER SkipHash
With -VerifyArtifacts: existence only (do not call fill-manifest).
.PARAMETER Format
Hash output format passed to fill-manifest (txt|md|both). Default txt.
.EXAMPLE
powershell -NoProfile -ExecutionPolicy Bypass -File dual-ship-smoke.ps1
.EXAMPLE
powershell -NoProfile -ExecutionPolicy Bypass -File dual-ship-smoke.ps1 -VerifyArtifacts
.EXAMPLE
.\dual-ship-smoke.ps1 -VerifyArtifacts -RequireBoth -NsisPath .\dist\VeraePrintStamp-windows-amd64-0.3.6-setup.exe
#>
param(
[string]$Version = "",
[string]$Arch = "amd64",
[string]$NsisPath = "",
[string]$MsiPath = "",
[string]$SearchRoot = "",
[switch]$VerifyArtifacts,
[switch]$RequireBoth,
[switch]$SkipHash,
[ValidateSet("txt", "md", "both")]
[string]$Format = "txt"
)
$ErrorActionPreference = "Stop"
$common = Join-Path $PSScriptRoot "common.ps1"
if (Test-Path -LiteralPath $common) { . $common }
$RepoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..\..")).Path
$WinDir = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path
$WixDir = Join-Path $WinDir "wix"
$FillScript = Join-Path $PSScriptRoot "fill-manifest-sha256.ps1"
if (-not $Version) {
if (Get-Command Get-VeraePackageVersion -ErrorAction SilentlyContinue) {
$Version = Get-VeraePackageVersion
} elseif ($env:VERSION -and $env:VERSION.Trim()) {
$Version = $env:VERSION.Trim()
} else {
$Version = "0.3.6"
}
}
$nsisLeaf = "VeraePrintStamp-windows-$Arch-$Version-setup.exe"
$msiLeaf = "VeraePrintStamp-windows-$Arch-$Version.msi"
function Write-Section([string]$Title) {
Write-Host ""
Write-Host ("==== {0} ====" -f $Title) -ForegroundColor Cyan
}
function Write-Step([string]$Id, [string]$Text) {
Write-Host (" [ ] {0} {1}" -f $Id, $Text)
}
function Write-Note([string]$Text) {
Write-Host (" {0}" -f $Text) -ForegroundColor DarkGray
}
function Resolve-FirstExisting {
param([string[]]$Candidates)
foreach ($c in $Candidates) {
if (-not $c) { continue }
if (Test-Path -LiteralPath $c) {
return (Get-Item -LiteralPath $c).FullName
}
}
return $null
}
function Find-Artifact {
param(
[string]$Explicit,
[string]$Leaf,
[string[]]$Dirs
)
if ($Explicit) {
if (Test-Path -LiteralPath $Explicit) {
return (Get-Item -LiteralPath $Explicit).FullName
}
return $null
}
$cands = @()
foreach ($d in $Dirs) {
if (-not $d) { continue }
if (-not (Test-Path -LiteralPath $d)) { continue }
$cands += (Join-Path $d $Leaf)
# Also accept any matching pattern if exact leaf missing (version drift).
Get-ChildItem -LiteralPath $d -File -ErrorAction SilentlyContinue |
Where-Object {
($Leaf -match '-setup\.exe$' -and $_.Name -like 'VeraePrintStamp-windows-*-setup.exe') -or
($Leaf -match '\.msi$' -and $_.Name -like 'VeraePrintStamp-windows-*.msi' -and $_.Name -notlike '*Tray*')
} |
ForEach-Object { $cands += $_.FullName }
}
return (Resolve-FirstExisting -Candidates $cands)
}
# --- Header ---
Write-Host "dual-ship-smoke (offline) — does not install, sign, or upload." -ForegroundColor DarkGray
Write-Host ("HelperVersion expect: {0} arch: {1}" -f $Version, $Arch)
Write-Host ("Repo: {0}" -f $RepoRoot)
Write-Host "Docs: platform/windows/wix/DUAL-SHIP.md"
Write-Host " platform/windows/wix/VM-DUAL-SHIP-CHECKLIST.md"
Write-Host "Rules: one channel per VM; receipt/CID only (never jobId-only)."
# Detect deferred printer CA enablement in Product-Full.wxs (authoring gate for VM C).
$productFullWxs = Join-Path $WixDir "Product-Full.wxs"
$casState = "unknown"
$vmCBlocked = $true
if (Test-Path -LiteralPath $productFullWxs) {
$wxsText = [System.IO.File]::ReadAllText($productFullWxs)
$hasOff = $wxsText.Contains("<!--DEFERRED_PRINTER_CAS:OFF") -and $wxsText.Contains("DEFERRED_PRINTER_CAS:OFF-->")
$hasOn = $wxsText.Contains("<!--DEFERRED_PRINTER_CAS:ON-->") -and $wxsText.Contains("<!--/DEFERRED_PRINTER_CAS:ON-->")
if ($hasOn -and -not $hasOff) {
$casState = "enabled"
$vmCBlocked = $false
} elseif ($hasOff -and -not $hasOn) {
$casState = "disabled"
$vmCBlocked = $true
} elseif ($wxsText -match 'CustomAction\s+Id="CA_InstallPrinters"' -and $wxsText -notmatch 'DEFERRED_PRINTER_CAS:OFF') {
# Markers absent but InstallPrinters CA appears live (manual enable).
$casState = "enabled-manual"
$vmCBlocked = $false
} else {
$casState = "disabled-or-missing"
$vmCBlocked = $true
}
}
if ($vmCBlocked) {
Write-Host ("VM C: BLOCKED until printer CAs enabled (Product-Full state: {0})." -f $casState) -ForegroundColor Yellow
Write-Host " Enable: platform\windows\scripts\enable-printer-cas.ps1 then rebuild MSI." -ForegroundColor DarkGray
Write-Host " Enabling authoring is NOT a dual-ship pass — clean-VM C still required." -ForegroundColor DarkGray
} else {
Write-Host ("VM C: CAs enabled in authoring ({0}) — still need rebuild + clean-VM parity vs A." -f $casState) -ForegroundColor Cyan
Write-Host " dual-ship gate stays open until VM C matches VM A (DUAL-SHIP.md)." -ForegroundColor DarkGray
}
# --- Shared pre-checks ---
Write-Section "Shared pre-checks (every VM)"
Write-Step "0.1" "Fresh Win10/11 x64 snapshot (or sysprep-clean); admin account available"
Write-Step "0.2" "No prior Verae Print Stamp under Program Files or ARP"
Write-Step "0.3" "Spooler running (Get-Service Spooler)"
Write-Step "0.4" ("Note HelperVersion on artifact = tree (expect {0})" -f $Version)
Write-Step "0.5" "Record SmartScreen / AV prompts (unsigned expected until Authenticode live)"
# --- VM A ---
Write-Section "VM A — NSIS primary (baseline)"
Write-Note ("Install: elevated setup.exe (typical: {0})" -f $nsisLeaf)
Write-Step "A.1" "Run setup.exe elevated; key wizard complete or skip per test plan"
Write-Step "A.2" "Confirm Program Files\Verae\PrintStamp\bin\verae-print.exe + helper"
Write-Step "A.3" "Confirm printers (IPP and/or Local Port) present"
Write-Step "A.4" "Confirm scheduled task Verae Print Helper"
Write-Step "A.5" "Print one-page PDF → stamp path OK (receipt/CID; never jobId-only)"
Write-Step "A.6" "Start Menu: Setup signing key, Start print helper, Collect diagnostics"
Write-Step "A.7" "Uninstall via ARP; printers/task removed; reboot optional"
Write-Note "VM A sign-off: pass / fail · log path · initials"
# --- VM B ---
Write-Section "VM B — MSI files-only (safe today)"
Write-Note ("Install: full MSI with printers skipped (typical: {0})" -f $msiLeaf)
Write-Note "msiexec /i VeraePrintStamp-windows-amd64-<ver>.msi /qn /norestart \"
Write-Note " VERAE_SKIP_KEY_SETUP=1 VERAE_SKIP_PRINTERS=1 VERAE_CHANNEL=zapier"
Write-Step "B.1" "msiexec as above (or UI); exit code recorded"
Write-Step "B.2" "Confirm InstallDir: bin\, scripts\ (incl. install-printer.ps1), LICENSE, README"
Write-Step "B.3" "ARP entry: Publisher Verae; DisplayVersion = HelperVersion"
Write-Step "B.4" "Start Menu shortcuts resolve to helper / diag"
Write-Step "B.5" "Optional: elevated manual scripts\install-printer.ps1 -InstallDir `"…`""
Write-Step "B.6" "msiexec /x {ProductCode} /qn — files + Start Menu gone; no orphan HKLM"
Write-Note "Pass (files-only): clean install/uninstall; version match; props accepted."
Write-Note "VM B sign-off: pass / fail · ProductCode · initials"
# --- VM C ---
if ($vmCBlocked) {
Write-Section "VM C — MSI printer CA (BLOCKED until CAs enabled)"
Write-Note ("Product-Full.wxs deferred CAs: {0}" -f $casState)
Write-Note "Prereq: enable-printer-cas.ps1 (or unwrap DEFERRED_PRINTER_CAS:OFF markers); rebuild MSI."
Write-Note "NOTE: VM C BLOCKED until CAs enabled — dual-ship gate stays open."
} else {
Write-Section "VM C — MSI printer CA (CAs enabled in authoring — clean-VM still left)"
Write-Note ("Product-Full.wxs deferred CAs: {0}" -f $casState)
Write-Note "Prereq done in tree; rebuild MSI if not yet rebuilt after enable."
Write-Note "NOTE: authoring enabled ≠ dual-ship pass (clean-VM C still required)."
}
Write-Step "C.0" "CAs scheduled; conditions NOT Installed + VERAE_SKIP_PRINTERS<>`"1`"; REMOVE~=`"ALL`" uninstall"
Write-Step "C.1" "Default MSI install (or VERAE_SKIP_PRINTERS=0) elevated"
Write-Step "C.2" "Program Files layout matches VM A (bin\, scripts\, docs)"
Write-Step "C.3" "IPP loopback 127.0.0.1:8631 and/or Local Port names match NSIS"
Write-Step "C.4" "Scheduled task Verae Print Helper (same principal / trigger class as A)"
Write-Step "C.5" "Start Menu OK; one-page PDF stamp path (receipt/CID evidence)"
Write-Step "C.6" "VERAE_SKIP_PRINTERS=1 skips CA; default installs printers"
Write-Step "C.7" "msiexec /x removes printers, task, files, Start Menu; no orphan HKLM"
Write-Step "C.8" "Repair / same-version upgrade smoke (AllowSameVersionUpgrades)"
Write-Note "Do not mark dual-ship complete until VM C matches VM A on a clean image."
if ($vmCBlocked) {
Write-Note "VM C sign-off: blocked (CAs not enabled) · initials"
} else {
Write-Note "VM C sign-off: pass / fail · initials (authoring enabled ≠ signed off)"
}
# --- Side-by-side ---
Write-Section "Side-by-side (tick when observed)"
Write-Host " Check NSIS(A) MSI-files(B) MSI+CA(C)"
Write-Host " Version = HelperVersion [ ] [ ] [ ]"
Write-Host " Program Files layout [ ] [ ] [ ]"
Write-Host " Start Menu shortcuts [ ] [ ] [ ]"
Write-Host " ARP uninstall clean [ ] [ ] [ ]"
Write-Host " IPP + Local Port printers [ ] N/A [ ]"
Write-Host " Scheduled task helper [ ] N/A [ ]"
Write-Host " Key wizard / first-run key [ ] skip prop [ ]"
Write-Host " msiexec /qn N/A [ ] [ ]"
Write-Host " Authenticode (signtool) still left still left still left"
Write-Section "After the VMs"
Write-Step "G.1" "Record logs + exit codes in release notes / dual-ship folder"
Write-Step "G.2" "Authenticode still left → SmartScreen warn is not promote-ready"
Write-Step "G.3" "Update DOWNLOADS-MANIFEST digests only after upload (hash with fill-manifest-sha256.ps1)"
Write-Step "G.4" "Thin-NSIS only when DUAL-SHIP exit checklist A–D all checked"
Write-Note "Overall dual-ship gate: open / closed"
# --- Optional local artifact verify ---
if (-not $VerifyArtifacts) {
Write-Section "Local artifacts (skipped)"
Write-Host " Pass -VerifyArtifacts to check MSI/NSIS existence and hash via fill-manifest-sha256.ps1."
Write-Host " Example:"
Write-Host " powershell -NoProfile -ExecutionPolicy Bypass -File platform\windows\scripts\dual-ship-smoke.ps1 -VerifyArtifacts"
exit 0
}
Write-Section "Local artifact verify"
$searchDirs = @(
(Join-Path $RepoRoot "dist"),
(Join-Path $RepoRoot "dist\windows"),
(Join-Path $WixDir "out"),
(Join-Path $WinDir "out"),
(Join-Path $RepoRoot "out")
)
if ($SearchRoot) { $searchDirs = @($SearchRoot) + $searchDirs }
$foundNsis = Find-Artifact -Explicit $NsisPath -Leaf $nsisLeaf -Dirs $searchDirs
$foundMsi = Find-Artifact -Explicit $MsiPath -Leaf $msiLeaf -Dirs $searchDirs
$nsisStatus = if ($foundNsis) { "FOUND $foundNsis" } else { "MISSING (expected leaf $nsisLeaf)" }
$msiStatus = if ($foundMsi) { "FOUND $foundMsi" } else { "MISSING (expected leaf $msiLeaf)" }
Write-Host (" NSIS setup.exe : {0}" -f $nsisStatus) -ForegroundColor $(if ($foundNsis) { "Green" } else { "Yellow" })
Write-Host (" Full MSI : {0}" -f $msiStatus) -ForegroundColor $(if ($foundMsi) { "Green" } else { "Yellow" })
$toHash = @()
if ($foundNsis) { $toHash += $foundNsis }
if ($foundMsi) { $toHash += $foundMsi }
if ($RequireBoth -and (-not $foundNsis -or -not $foundMsi)) {
Write-Host " FAIL: -RequireBoth set but NSIS and/or MSI missing." -ForegroundColor Red
exit 1
}
if ($toHash.Count -eq 0) {
Write-Host " FAIL: no local MSI/NSIS artifacts found under dist/, wix/out, or -NsisPath/-MsiPath." -ForegroundColor Red
Write-Host " Build first: platform\windows\build-windows.ps1 then scripts\build-full-msi.ps1" -ForegroundColor DarkGray
exit 1
}
if ($SkipHash) {
Write-Host " SkipHash: existence only (fill-manifest not invoked)." -ForegroundColor DarkGray
exit 0
}
if (-not (Test-Path -LiteralPath $FillScript)) {
Write-Host (" FAIL: fill-manifest-sha256.ps1 not found: {0}" -f $FillScript) -ForegroundColor Red
exit 1
}
Write-Host ""
Write-Host " Invoking fill-manifest-sha256.ps1 (local hash only — does not upload)…" -ForegroundColor DarkGray
& powershell -NoProfile -ExecutionPolicy Bypass -File $FillScript -Path $toHash -Format $Format
$fillExit = $LASTEXITCODE
if ($null -eq $fillExit) { $fillExit = 0 }
if ($fillExit -ne 0) {
Write-Host (" FAIL: fill-manifest-sha256.ps1 exit {0}" -f $fillExit) -ForegroundColor Red
exit 1
}
Write-Host ""
Write-Host " Hash rows above are for DOWNLOADS-MANIFEST paste only after CDN upload." -ForegroundColor DarkGray
Write-Host " dual-ship-smoke verify: OK" -ForegroundColor Green
exit 0