Some checks failed
windows-msi / full-msi-unsigned (push) Has been cancelled
Toggles Product-Full markers; dual-ship-smoke reports C blocked until enabled.
337 lines
14 KiB
PowerShell
337 lines
14 KiB
PowerShell
#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
|