feat: WiX ProductVersion from HelperVersion + build-tray-msi + CERT live QR
- build-tray-msi.ps1 candle/light or tip; version from common.ps1 - certificate.html final QR via qrcode CDN when digest present
This commit is contained in:
parent
b72324edc4
commit
f766e537ba
6 changed files with 299 additions and 19 deletions
|
|
@ -14,11 +14,11 @@ Repo now includes **authoring/docs scaffolds** for the two commercial gaps that
|
||||||
|
|
||||||
| Scaffold | Path | What it is | What it is not |
|
| Scaffold | Path | What it is | What it is not |
|
||||||
|----------|------|------------|----------------|
|
|----------|------|------------|----------------|
|
||||||
| **WiX tray presence MSI skeleton** | [`platform/windows/wix/`](../platform/windows/wix/) — `VeraeTrayShortcuts.wxs`, `VeraeTrayShortcutsFragment.wxs`, [`README.md`](../platform/windows/wix/README.md) | Non-elevated **per-user** WiX v3 product + shortcut fragment: stages `verae-tray.ps1`, Start Menu `Programs\Verae\Verae Tray.lnk`, Startup `Verae Tray.lnk`. Build when **candle/light** (or adapted WiX v4) are present. | Full elevated commercial MSI (printers, Program Files, Authenticode, NSIS parity, CI-signed MSI in `dist/`). No MSI binary is produced in-repo without the WiX toolset. |
|
| **WiX tray presence MSI skeleton** | [`platform/windows/wix/`](../platform/windows/wix/) — `VeraeTrayShortcuts.wxs`, `VeraeTrayShortcutsFragment.wxs`, [`README.md`](../platform/windows/wix/README.md); driver [`scripts/build-tray-msi.ps1`](../platform/windows/scripts/build-tray-msi.ps1) | Non-elevated **per-user** WiX v3 product + shortcut fragment: stages `verae-tray.ps1`, Start Menu `Programs\Verae\Verae Tray.lnk`, Startup `Verae Tray.lnk`. **ProductVersion** wired from `Get-VeraePackageVersion` → `internal/job.HelperVersion` (or `VERSION=` / `-Version`). `build-tray-msi.ps1` runs **candle/light** when present; otherwise prints WiX install tip (exit 0). | Full elevated commercial MSI (printers, Program Files, Authenticode, NSIS parity, CI-signed MSI in `dist/`). No MSI binary without the WiX toolset on the build host. |
|
||||||
| **Menubar notarize checklist** | [`platform/macos/menubar/NOTARIZE-CHECKLIST.md`](../platform/macos/menubar/NOTARIZE-CHECKLIST.md) | Operator step list: unsigned build → codesign hardened runtime → `ditto` zip → `notarytool` (keychain profile **or** API key placeholders) → staple → `spctl`. No secrets. | Automated sign/notarize in `build-menubar-app.sh`, stapled `.app.zip`/DMG release, or CI keychain import. |
|
| **Menubar notarize checklist** | [`platform/macos/menubar/NOTARIZE-CHECKLIST.md`](../platform/macos/menubar/NOTARIZE-CHECKLIST.md) | Operator step list: unsigned build → codesign hardened runtime → `ditto` zip → `notarytool` (keychain profile **or** API key placeholders) → staple → `spctl`. No secrets. | Automated sign/notarize in `build-menubar-app.sh`, stapled `.app.zip`/DMG release, or CI keychain import. |
|
||||||
|
|
||||||
**Still use for day-to-day presence (no WiX / no Apple certs):**
|
**Still use for day-to-day presence (no WiX / no Apple certs):**
|
||||||
Windows `scripts/build-tray-startmenu.ps1` + `install-tray-startup.ps1`; macOS `verae-print menubar --install` / unsigned `build-menubar-app.sh`. Full product install remains NSIS (`installer-windows.nsi`) and macOS `pkg/build-pkg.sh`.
|
Windows `scripts/build-tray-startmenu.ps1` + `install-tray-startup.ps1`; macOS `verae-print menubar --install` / unsigned `build-menubar-app.sh`. Full product install remains NSIS (`installer-windows.nsi`) and macOS `pkg/build-pkg.sh`. Tray MSI skeleton build: `scripts/build-tray-msi.ps1` (optional WiX).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -47,7 +47,7 @@ Related but out of scope for “done” above (still backlog-adjacent): Linux `.
|
||||||
| Tray NotifyIcon + GET `/status` | [`platform/windows/scripts/verae-tray.ps1`](../platform/windows/scripts/verae-tray.ps1) | User-level; see [PACKAGING.md](PACKAGING.md) § Windows tray |
|
| Tray NotifyIcon + GET `/status` | [`platform/windows/scripts/verae-tray.ps1`](../platform/windows/scripts/verae-tray.ps1) | User-level; see [PACKAGING.md](PACKAGING.md) § Windows tray |
|
||||||
| Startup-folder tray install | [`platform/windows/scripts/install-tray-startup.ps1`](../platform/windows/scripts/install-tray-startup.ps1) | **Not** MSI; no elevation |
|
| Startup-folder tray install | [`platform/windows/scripts/install-tray-startup.ps1`](../platform/windows/scripts/install-tray-startup.ps1) | **Not** MSI; no elevation |
|
||||||
| WiX-free Start Menu stub | [`platform/windows/scripts/build-tray-startmenu.ps1`](../platform/windows/scripts/build-tray-startmenu.ps1) | Explicitly “future MSI can supersede” |
|
| WiX-free Start Menu stub | [`platform/windows/scripts/build-tray-startmenu.ps1`](../platform/windows/scripts/build-tray-startmenu.ps1) | Explicitly “future MSI can supersede” |
|
||||||
| **WiX tray presence skeleton** (per-user, non-elevated) | [`platform/windows/wix/`](../platform/windows/wix/) (`VeraeTrayShortcuts.wxs` + fragment + README) | **Scaffold shipped** — Start Menu + Startup → `verae-tray.ps1` only; needs candle/light to emit MSI |
|
| **WiX tray presence skeleton** (per-user, non-elevated) | [`platform/windows/wix/`](../platform/windows/wix/) + [`scripts/build-tray-msi.ps1`](../platform/windows/scripts/build-tray-msi.ps1) | **Scaffold shipped** — ProductVersion ← HelperVersion; candle/light when present, else WiX tip; not full product MSI |
|
||||||
| Shared version helper | [`platform/windows/scripts/common.ps1`](../platform/windows/scripts/common.ps1) `Get-VeraePackageVersion` | Aligns tray scripts with `HelperVersion` |
|
| Shared version helper | [`platform/windows/scripts/common.ps1`](../platform/windows/scripts/common.ps1) `Get-VeraePackageVersion` | Aligns tray scripts with `HelperVersion` |
|
||||||
| End-user Windows readme | [`platform/windows/README-WINDOWS.txt`](../platform/windows/README-WINDOWS.txt), [`README.txt`](../platform/windows/README.txt) | Documents NSIS flow; states **no elevated MSI** for tray |
|
| End-user Windows readme | [`platform/windows/README-WINDOWS.txt`](../platform/windows/README-WINDOWS.txt), [`README.txt`](../platform/windows/README.txt) | Documents NSIS flow; states **no elevated MSI** for tray |
|
||||||
| Packaging policy doc | [PACKAGING.md](PACKAGING.md) | “**No elevated MSI**” for tray presence by design |
|
| Packaging policy doc | [PACKAGING.md](PACKAGING.md) | “**No elevated MSI**” for tray presence by design |
|
||||||
|
|
@ -146,7 +146,7 @@ NSIS product already covers much of the *install experience* (files under Progra
|
||||||
|-------------|--------|------------------|
|
|-------------|--------|------------------|
|
||||||
| Windows tray / Startup / Start Menu (user) | Yes | Yes for **presence**; not enterprise MSI |
|
| Windows tray / Startup / Start Menu (user) | Yes | Yes for **presence**; not enterprise MSI |
|
||||||
| Windows NSIS `setup.exe` | Yes | **Partial** — full GUI install; needs Authenticode + version align; not MSI |
|
| Windows NSIS `setup.exe` | Yes | **Partial** — full GUI install; needs Authenticode + version align; not MSI |
|
||||||
| Windows **MSI** (WiX) tray presence skeleton | **Yes** (`.wxs` + README under `platform/windows/wix/`) | **No** — scaffold only; needs toolset build + is not full product MSI |
|
| Windows **MSI** (WiX) tray presence skeleton | **Yes** (`.wxs` + `build-tray-msi.ps1`; ProductVersion ← HelperVersion) | **No** — scaffold only; MSI only when candle/light installed; not full product MSI |
|
||||||
| Windows **MSI** full product (NSIS parity) | **No** | **No** |
|
| Windows **MSI** full product (NSIS parity) | **No** | **No** |
|
||||||
| Windows Authenticode pipeline | **No** (in-repo) | **No** |
|
| Windows Authenticode pipeline | **No** (in-repo) | **No** |
|
||||||
| macOS menu bar LaunchAgent | Yes | Yes (dev/ops) |
|
| macOS menu bar LaunchAgent | Yes | Yes (dev/ops) |
|
||||||
|
|
@ -162,7 +162,7 @@ NSIS product already covers much of the *install experience* (files under Progra
|
||||||
|
|
||||||
- **[PACKAGING.md](PACKAGING.md)** — tray, menubar, unsigned `.app`, notarization steps (docs), CERT-01, statuslang, IPP split, smoke tests
|
- **[PACKAGING.md](PACKAGING.md)** — tray, menubar, unsigned `.app`, notarization steps (docs), CERT-01, statuslang, IPP split, smoke tests
|
||||||
- [`platform/windows/`](../platform/windows/) — NSIS + tray scripts
|
- [`platform/windows/`](../platform/windows/) — NSIS + tray scripts
|
||||||
- [`platform/windows/wix/`](../platform/windows/wix/) — per-user tray shortcut MSI **skeleton** (candle/light)
|
- [`platform/windows/wix/`](../platform/windows/wix/) + [`build-tray-msi.ps1`](../platform/windows/scripts/build-tray-msi.ps1) — per-user tray MSI skeleton; ProductVersion ← HelperVersion
|
||||||
- [`platform/macos/pkg/`](../platform/macos/pkg/) — commercial `.pkg` build
|
- [`platform/macos/pkg/`](../platform/macos/pkg/) — commercial `.pkg` build
|
||||||
- [`platform/macos/menubar/`](../platform/macos/menubar/) — menu bar + unsigned `.app` + notarize docs/checklist
|
- [`platform/macos/menubar/`](../platform/macos/menubar/) — menu bar + unsigned `.app` + notarize docs/checklist
|
||||||
- [`platform/macos/menubar/NOTARIZE-CHECKLIST.md`](../platform/macos/menubar/NOTARIZE-CHECKLIST.md) — menubar notarytool step list
|
- [`platform/macos/menubar/NOTARIZE-CHECKLIST.md`](../platform/macos/menubar/NOTARIZE-CHECKLIST.md) — menubar notarytool step list
|
||||||
|
|
|
||||||
|
|
@ -188,7 +188,8 @@ Site page: [`site/printit.verae.ai/certificate.html`](../site/printit.verae.ai/c
|
||||||
| Load paths | Query string (`?sha256=&receipt=&…`), optional `.verae.json` sidecar drop, “Sample data” button |
|
| Load paths | Query string (`?sha256=&receipt=&…`), optional `.verae.json` sidecar drop, “Sample data” button |
|
||||||
| Print CSS | Letter page, toolbar hidden, frame colors preserved |
|
| Print CSS | Letter page, toolbar hidden, frame colors preserved |
|
||||||
| Live verify | Link to `explorer.verae.ai/time?receipt=&sha256=` (same as `pdfcert.LookupURL`) |
|
| Live verify | Link to `explorer.verae.ai/time?receipt=&sha256=` (same as `pdfcert.LookupURL`) |
|
||||||
| Residual gap | Full **CERT-01.html** pixel match (self-hosted design-kit Fraunces/Cantarell, live QR bitmap, Substrate extrinsic polish) still **not** claimed — web title ships CDN Fraunces + Georgia/serif offline fallback; issued artifact remains PDF (`internal/pdfcert` + `platform/registration-pdf/build.mjs`) |
|
| Live QR (web) | **Shipped** — final + receipt/digest paints QR via qrcode.js CDN → data-URL; offline/provisional keeps SVG placeholder (print-safe) |
|
||||||
|
| Residual gap | Full **CERT-01.html** pixel match (self-hosted design-kit Fraunces/Cantarell, Substrate extrinsic polish) still **not** claimed — web title ships CDN Fraunces + Georgia/serif offline fallback; issued artifact remains PDF (`internal/pdfcert` + `platform/registration-pdf/build.mjs`) |
|
||||||
|
|
||||||
Design source of truth for the paper mock: `verae-2026-design/ui-designs/src/html/CERT-01.html`.
|
Design source of truth for the paper mock: `verae-2026-design/ui-designs/src/html/CERT-01.html`.
|
||||||
Cross-product siblings (not owned here): Sign `certificate.html` (SG-P1), Verify ZP-V5, MCP Mac export PDF.
|
Cross-product siblings (not owned here): Sign `certificate.html` (SG-P1), Verify ZP-V5, MCP Mac export PDF.
|
||||||
|
|
|
||||||
179
platform/windows/scripts/build-tray-msi.ps1
Normal file
179
platform/windows/scripts/build-tray-msi.ps1
Normal file
|
|
@ -0,0 +1,179 @@
|
||||||
|
#Requires -Version 5.1
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Build the per-user Verae Tray presence MSI (WiX v3 candle/light) when available.
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Stages verae-tray.ps1 + common.ps1, stamps ProductVersion from
|
||||||
|
internal/job.HelperVersion via Get-VeraePackageVersion (or $env:VERSION),
|
||||||
|
then runs candle.exe + light.exe if on PATH (or under Program Files WiX).
|
||||||
|
|
||||||
|
If WiX Toolset is missing, prints an install tip and exits 0 without failing
|
||||||
|
the day-to-day tray path (use build-tray-startmenu.ps1 instead).
|
||||||
|
|
||||||
|
Scope: non-elevated per-user tray shortcuts only — not the full commercial MSI.
|
||||||
|
|
||||||
|
.PARAMETER Version
|
||||||
|
Override package version (default: Get-VeraePackageVersion / HelperVersion).
|
||||||
|
|
||||||
|
.PARAMETER OutDir
|
||||||
|
Output directory for .wixobj and .msi (default: platform/windows/wix/out).
|
||||||
|
|
||||||
|
.PARAMETER StageDir
|
||||||
|
Staging directory for payload files (default: platform/windows/wix/Stage).
|
||||||
|
|
||||||
|
.PARAMETER WixDir
|
||||||
|
Directory containing VeraeTrayShortcuts.wxs (+ fragment).
|
||||||
|
|
||||||
|
.PARAMETER SkipTip
|
||||||
|
When candle/light are missing, exit 1 instead of printing the install tip.
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
powershell -NoProfile -ExecutionPolicy Bypass -File build-tray-msi.ps1
|
||||||
|
$env:VERSION = "0.3.7"; .\build-tray-msi.ps1
|
||||||
|
#>
|
||||||
|
param(
|
||||||
|
[string]$Version = "",
|
||||||
|
[string]$OutDir = "",
|
||||||
|
[string]$StageDir = "",
|
||||||
|
[string]$WixDir = "",
|
||||||
|
[switch]$SkipTip
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
$common = Join-Path $PSScriptRoot "common.ps1"
|
||||||
|
if (Test-Path -LiteralPath $common) { . $common }
|
||||||
|
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not $WixDir) {
|
||||||
|
$WixDir = Join-Path (Split-Path -Parent $PSScriptRoot) "wix"
|
||||||
|
}
|
||||||
|
if (-not $StageDir) {
|
||||||
|
$StageDir = Join-Path $WixDir "Stage"
|
||||||
|
}
|
||||||
|
if (-not $OutDir) {
|
||||||
|
$OutDir = Join-Path $WixDir "out"
|
||||||
|
}
|
||||||
|
|
||||||
|
$productWxs = Join-Path $WixDir "VeraeTrayShortcuts.wxs"
|
||||||
|
$fragmentWxs = Join-Path $WixDir "VeraeTrayShortcutsFragment.wxs"
|
||||||
|
if (-not (Test-Path -LiteralPath $productWxs)) {
|
||||||
|
Write-Error "Missing WiX product: $productWxs"
|
||||||
|
}
|
||||||
|
if (-not (Test-Path -LiteralPath $fragmentWxs)) {
|
||||||
|
Write-Error "Missing WiX fragment: $fragmentWxs"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Find-WixTool([string]$Name) {
|
||||||
|
$cmd = Get-Command $Name -ErrorAction SilentlyContinue
|
||||||
|
if ($cmd -and $cmd.Source) { return $cmd.Source }
|
||||||
|
$cmdExe = Get-Command "$Name.exe" -ErrorAction SilentlyContinue
|
||||||
|
if ($cmdExe -and $cmdExe.Source) { return $cmdExe.Source }
|
||||||
|
|
||||||
|
$roots = @()
|
||||||
|
if (${env:ProgramFiles(x86)}) { $roots += ${env:ProgramFiles(x86)} }
|
||||||
|
if ($env:ProgramFiles) { $roots += $env:ProgramFiles }
|
||||||
|
if ($env:WIX) {
|
||||||
|
$wixBin = Join-Path $env:WIX "bin"
|
||||||
|
if (Test-Path -LiteralPath $wixBin) { $roots = @($wixBin) + $roots }
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($root in $roots) {
|
||||||
|
if (-not $root -or -not (Test-Path -LiteralPath $root)) { continue }
|
||||||
|
$direct = Join-Path $root "$Name.exe"
|
||||||
|
if (Test-Path -LiteralPath $direct) { return (Resolve-Path -LiteralPath $direct).Path }
|
||||||
|
Get-ChildItem -LiteralPath $root -Directory -Filter "WiX Toolset*" -ErrorAction SilentlyContinue |
|
||||||
|
ForEach-Object {
|
||||||
|
$bin = Join-Path $_.FullName "bin\$Name.exe"
|
||||||
|
if (Test-Path -LiteralPath $bin) { return (Resolve-Path -LiteralPath $bin).Path }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $null
|
||||||
|
}
|
||||||
|
|
||||||
|
$candle = Find-WixTool "candle"
|
||||||
|
$light = Find-WixTool "light"
|
||||||
|
|
||||||
|
Write-Host "Verae tray presence MSI (skeleton)"
|
||||||
|
Write-Host " ProductVersion: $Version (HelperVersion / Get-VeraePackageVersion)"
|
||||||
|
Write-Host " WixDir: $WixDir"
|
||||||
|
Write-Host " StageDir: $StageDir"
|
||||||
|
Write-Host " OutDir: $OutDir"
|
||||||
|
|
||||||
|
if (-not $candle -or -not $light) {
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "WiX Toolset (candle/light) not found on PATH or under Program Files."
|
||||||
|
Write-Host "No MSI produced. Day-to-day tray presence:"
|
||||||
|
Write-Host " powershell -NoProfile -ExecutionPolicy Bypass -File `"$(Join-Path $PSScriptRoot 'build-tray-startmenu.ps1')`""
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Install WiX tip (Windows host or CI runner):"
|
||||||
|
Write-Host " 1. Download WiX Toolset v3.14+ from https://wixtoolset.org/ (or winget install --id WiXToolset.WiX)"
|
||||||
|
Write-Host " 2. Add candle.exe and light.exe to PATH, e.g.:"
|
||||||
|
Write-Host " set PATH=%PATH%;%ProgramFiles(x86)%\WiX Toolset v3.14\bin"
|
||||||
|
Write-Host " 3. Re-run this script: build-tray-msi.ps1"
|
||||||
|
Write-Host " WiX v4 (dotnet tool install --global wix) needs authoring adapted to v4 namespaces."
|
||||||
|
Write-Host " See platform/windows/wix/README.md"
|
||||||
|
if ($SkipTip) { exit 1 }
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host " candle: $candle"
|
||||||
|
Write-Host " light: $light"
|
||||||
|
|
||||||
|
# Stage payload next to .wxs expectations (Stage\verae-tray.ps1, Stage\common.ps1).
|
||||||
|
New-Item -ItemType Directory -Force -Path $StageDir | Out-Null
|
||||||
|
New-Item -ItemType Directory -Force -Path $OutDir | Out-Null
|
||||||
|
|
||||||
|
$traySrc = Join-Path $PSScriptRoot "verae-tray.ps1"
|
||||||
|
$commonSrc = Join-Path $PSScriptRoot "common.ps1"
|
||||||
|
if (-not (Test-Path -LiteralPath $traySrc)) {
|
||||||
|
Write-Error "Missing tray script: $traySrc"
|
||||||
|
}
|
||||||
|
Copy-Item -LiteralPath $traySrc -Destination (Join-Path $StageDir "verae-tray.ps1") -Force
|
||||||
|
if (Test-Path -LiteralPath $commonSrc) {
|
||||||
|
Copy-Item -LiteralPath $commonSrc -Destination (Join-Path $StageDir "common.ps1") -Force
|
||||||
|
} else {
|
||||||
|
Write-Warning "common.ps1 not found; MSI will still require it at light time if Component is present."
|
||||||
|
}
|
||||||
|
|
||||||
|
$stageAbs = (Resolve-Path -LiteralPath $StageDir).Path
|
||||||
|
$outAbs = (Resolve-Path -LiteralPath $OutDir).Path
|
||||||
|
$wixAbs = (Resolve-Path -LiteralPath $WixDir).Path
|
||||||
|
|
||||||
|
Write-Host "Staging done. Running candle..."
|
||||||
|
& $candle -nologo -out (Join-Path $outAbs "\") `
|
||||||
|
"-dProductVersion=$Version" `
|
||||||
|
"-dStageDir=$stageAbs" `
|
||||||
|
(Join-Path $wixAbs "VeraeTrayShortcuts.wxs") `
|
||||||
|
(Join-Path $wixAbs "VeraeTrayShortcutsFragment.wxs")
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Error "candle failed with exit $LASTEXITCODE"
|
||||||
|
}
|
||||||
|
|
||||||
|
$msiName = "VeraeTray-$Version-peruser.msi"
|
||||||
|
$msiPath = Join-Path $outAbs $msiName
|
||||||
|
Write-Host "Running light -> $msiPath"
|
||||||
|
& $light -nologo -out $msiPath `
|
||||||
|
(Join-Path $outAbs "VeraeTrayShortcuts.wixobj") `
|
||||||
|
(Join-Path $outAbs "VeraeTrayShortcutsFragment.wixobj")
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Error "light failed with exit $LASTEXITCODE"
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Built: $msiPath"
|
||||||
|
Write-Host " ProductVersion=$Version (from HelperVersion / Get-VeraePackageVersion)."
|
||||||
|
Write-Host " Per-user, non-elevated tray presence only — not full commercial MSI."
|
||||||
|
Write-Host " Silent: msiexec /i `"$msiPath`" /qn VERAE_IPP_ADDR=127.0.0.1:8631"
|
||||||
|
Write-Host " Hold ids are never evidence."
|
||||||
|
exit 0
|
||||||
|
|
@ -53,7 +53,25 @@ copy /Y platform\windows\scripts\common.ps1 %STAGE%\
|
||||||
Elevated Program Files install, printers, scheduled helper, Authenticode,
|
Elevated Program Files install, printers, scheduled helper, Authenticode,
|
||||||
NSIS key wizard parity, machine-wide Start Menu under ProgramData.
|
NSIS key wizard parity, machine-wide Start Menu under ProgramData.
|
||||||
|
|
||||||
### Build (WiX v3 candle / light)
|
### Build (preferred): `build-tray-msi.ps1`
|
||||||
|
|
||||||
|
From `platform/windows/scripts` on Windows (or any cwd):
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
powershell -NoProfile -ExecutionPolicy Bypass -File platform\windows\scripts\build-tray-msi.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
The script:
|
||||||
|
|
||||||
|
1. Resolves **ProductVersion** via `Get-VeraePackageVersion` → `internal/job.HelperVersion`
|
||||||
|
(override with `-Version` or `$env:VERSION`).
|
||||||
|
2. Stages `verae-tray.ps1` + `common.ps1` under `wix/Stage`.
|
||||||
|
3. Runs **candle** + **light** when found on PATH / `%WIX%` / Program Files.
|
||||||
|
4. Writes `wix/out/VeraeTray-<version>-peruser.msi`.
|
||||||
|
5. If candle/light are missing, prints a **WiX install tip** and exits 0
|
||||||
|
(day-to-day tray path stays `build-tray-startmenu.ps1`).
|
||||||
|
|
||||||
|
### Build (manual WiX v3 candle / light)
|
||||||
|
|
||||||
From repo root on Windows:
|
From repo root on Windows:
|
||||||
|
|
||||||
|
|
@ -61,6 +79,7 @@ From repo root on Windows:
|
||||||
set WIXDIR=platform\windows\wix
|
set WIXDIR=platform\windows\wix
|
||||||
set STAGE=%WIXDIR%\Stage
|
set STAGE=%WIXDIR%\Stage
|
||||||
set OUT=%WIXDIR%\out
|
set OUT=%WIXDIR%\out
|
||||||
|
REM Prefer HelperVersion; build-tray-msi.ps1 does this automatically:
|
||||||
set VER=0.3.6
|
set VER=0.3.6
|
||||||
|
|
||||||
mkdir %STAGE% 2>nul
|
mkdir %STAGE% 2>nul
|
||||||
|
|
@ -81,9 +100,9 @@ light.exe -nologo -out %OUT%\VeraeTray-%VER%-peruser.msi ^
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
|
|
||||||
- Set `VER` from `internal/job.HelperVersion` (see `docs/PACKAGING.md`), or
|
- **ProductVersion** is wired by `build-tray-msi.ps1` → `Get-VeraePackageVersion`
|
||||||
pass `-dProductVersion=` explicitly. The `.wxs` defaults to `0.3.6` when
|
/ `HelperVersion`. Manual builds should pass `-dProductVersion=` the same way.
|
||||||
unset (HelperVersion at scaffold time).
|
The `.wxs` `<?ifndef?>` fallback is `0.3.6` (must match current HelperVersion).
|
||||||
- `Product Id="*"` generates a new ProductCode per build; `UpgradeCode` is
|
- `Product Id="*"` generates a new ProductCode per build; `UpgradeCode` is
|
||||||
fixed for major-upgrade of this **tray presence** package only.
|
fixed for major-upgrade of this **tray presence** package only.
|
||||||
- Skeleton is **UI-less** (silent-friendly). For WixUI_Minimal, uncomment
|
- Skeleton is **UI-less** (silent-friendly). For WixUI_Minimal, uncomment
|
||||||
|
|
@ -102,9 +121,10 @@ Uninstall via ARP (**Verae Tray (presence)**) or `msiexec /x {ProductCode} /qn`.
|
||||||
|
|
||||||
| File | Role |
|
| File | Role |
|
||||||
|------|------|
|
|------|------|
|
||||||
| `VeraeTrayShortcuts.wxs` | Per-user Product + file components |
|
| `VeraeTrayShortcuts.wxs` | Per-user Product + file components (`ProductVersion`) |
|
||||||
| `VeraeTrayShortcutsFragment.wxs` | Start Menu + Startup shortcut fragment |
|
| `VeraeTrayShortcutsFragment.wxs` | Start Menu + Startup shortcut fragment |
|
||||||
| `README.md` | This file |
|
| `README.md` | This file |
|
||||||
|
| `../scripts/build-tray-msi.ps1` | candle/light driver; wires ProductVersion; WiX tip if missing |
|
||||||
|
|
||||||
### Relation to other Windows paths
|
### Relation to other Windows paths
|
||||||
|
|
||||||
|
|
@ -113,6 +133,7 @@ Uninstall via ARP (**Verae Tray (presence)**) or `msiexec /x {ProductCode} /qn`.
|
||||||
| `../scripts/verae-tray.ps1` | Runtime tray (source staged into MSI) |
|
| `../scripts/verae-tray.ps1` | Runtime tray (source staged into MSI) |
|
||||||
| `../scripts/install-tray-startup.ps1` | WiX-free Startup shortcut |
|
| `../scripts/install-tray-startup.ps1` | WiX-free Startup shortcut |
|
||||||
| `../scripts/build-tray-startmenu.ps1` | WiX-free Start Menu + Startup |
|
| `../scripts/build-tray-startmenu.ps1` | WiX-free Start Menu + Startup |
|
||||||
|
| `../scripts/build-tray-msi.ps1` | Builds this skeleton MSI when WiX is installed |
|
||||||
| `../installer-windows.nsi` | Elevated **full product** setup.exe |
|
| `../installer-windows.nsi` | Elevated **full product** setup.exe |
|
||||||
| **`wix/` (this folder)** | Per-user tray shortcut **MSI skeleton** |
|
| **`wix/` (this folder)** | Per-user tray shortcut **MSI skeleton** |
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,9 @@
|
||||||
Requires WiX Toolset (candle + light, or WiX v4 `wix build`) to produce MSI.
|
Requires WiX Toolset (candle + light, or WiX v4 `wix build`) to produce MSI.
|
||||||
See README.md in this folder.
|
See README.md in this folder.
|
||||||
|
|
||||||
Version default tracks internal/job.HelperVersion; override at candle time:
|
ProductVersion: prefer -dProductVersion from build-tray-msi.ps1
|
||||||
-dProductVersion=x.y.z
|
(Get-VeraePackageVersion → internal/job.HelperVersion, or VERSION=).
|
||||||
|
Fallback below must match HelperVersion when candle is invoked bare.
|
||||||
-->
|
-->
|
||||||
<?ifndef ProductVersion ?>
|
<?ifndef ProductVersion ?>
|
||||||
<?define ProductVersion = "0.3.6" ?>
|
<?define ProductVersion = "0.3.6" ?>
|
||||||
|
|
|
||||||
|
|
@ -69,10 +69,14 @@
|
||||||
}
|
}
|
||||||
.qr{
|
.qr{
|
||||||
width:112px;height:112px;flex:none;border:2px solid var(--ink,#1D1534);border-radius:8px;
|
width:112px;height:112px;flex:none;border:2px solid var(--ink,#1D1534);border-radius:8px;
|
||||||
|
/* Offline / provisional placeholder — print-safe without CDN */
|
||||||
background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 29 29'%3E%3Cpath fill='%231D1534' d='M1 1h7v7H1zm2 2v3h3V3zm10 0h7v7h-7zm2 2v3h3V3zM1 13h7v7H1zm2 2v3h3v-3zm18-14h7v7h-7zm2 2v3h3V3zm0 10h3v2h-3zm-4 0h2v2h-2zm4 4h3v3h-3zm-4 2h2v3h-2zm-2-4h2v2h-2zm6 6h3v2h-3zm-8 0h2v2h-2zm4-2h2v4h-2zm-6 0h2v2h-2z'/%3E%3C/svg%3E") center/72% no-repeat;
|
background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 29 29'%3E%3Cpath fill='%231D1534' d='M1 1h7v7H1zm2 2v3h3V3zm10 0h7v7h-7zm2 2v3h3V3zM1 13h7v7H1zm2 2v3h3v-3zm18-14h7v7h-7zm2 2v3h3V3zm0 10h3v2h-3zm-4 0h2v2h-2zm4 4h3v3h-3zm-4 2h2v3h-2zm-2-4h2v2h-2zm6 6h3v2h-3zm-8 0h2v2h-2zm4-2h2v4h-2zm-6 0h2v2h-2z'/%3E%3C/svg%3E") center/72% no-repeat;
|
||||||
position:relative;
|
position:relative;overflow:hidden;
|
||||||
}
|
}
|
||||||
.qr span{position:absolute;inset:auto;left:50%;top:50%;transform:translate(-50%,-50%);background:#fff;padding:2px 5px;font:700 10px system-ui;color:var(--plum,#531B59)}
|
.qr .qr-fallback{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);background:#fff;padding:2px 5px;font:700 10px system-ui;color:var(--plum,#531B59);pointer-events:none}
|
||||||
|
.qr img.qr-bitmap,.qr canvas.qr-bitmap{width:100%;height:100%;display:block;border-radius:6px;object-fit:contain;background:#fff}
|
||||||
|
.qr.has-qr{background:#fff}
|
||||||
|
.qr.has-qr .qr-fallback{display:none}
|
||||||
.verify-box ol{margin:6px 0 0 16px;padding:0;line-height:1.6;color:var(--ink-soft,#3B3250);font-size:12.5px}
|
.verify-box ol{margin:6px 0 0 16px;padding:0;line-height:1.6;color:var(--ink-soft,#3B3250);font-size:12.5px}
|
||||||
.verify-box a{color:var(--info,#3B2A8C);font-size:10px;word-break:break-all}
|
.verify-box a{color:var(--info,#3B2A8C);font-size:10px;word-break:break-all}
|
||||||
.foot{
|
.foot{
|
||||||
|
|
@ -97,8 +101,11 @@
|
||||||
}
|
}
|
||||||
/* CDN may be absent offline; keep plum/gold frame + system serif title */
|
/* CDN may be absent offline; keep plum/gold frame + system serif title */
|
||||||
.title{font-family:'Fraunces',Georgia,'Times New Roman',Times,serif!important}
|
.title{font-family:'Fraunces',Georgia,'Times New Roman',Times,serif!important}
|
||||||
.rule,.banner,.mh,.qr{ -webkit-print-color-adjust:exact;print-color-adjust:exact }
|
.rule,.banner,.mh,.qr,.qr img.qr-bitmap,.qr canvas.qr-bitmap{
|
||||||
|
-webkit-print-color-adjust:exact;print-color-adjust:exact
|
||||||
|
}
|
||||||
.rule{background:#C4A85A!important}
|
.rule{background:#C4A85A!important}
|
||||||
|
.qr{width:96px;height:96px;border-color:#1D1534!important}
|
||||||
a{color:inherit;text-decoration:none}
|
a{color:inherit;text-decoration:none}
|
||||||
}
|
}
|
||||||
@page{size:letter;margin:12mm}
|
@page{size:letter;margin:12mm}
|
||||||
|
|
@ -164,11 +171,13 @@
|
||||||
</dl>
|
</dl>
|
||||||
|
|
||||||
<div class="verify-box">
|
<div class="verify-box">
|
||||||
<div class="qr" aria-hidden="true"><span>Verae</span></div>
|
<div class="qr" id="qr" role="img" aria-label="Verify QR — explorer lookup when final">
|
||||||
|
<span class="qr-fallback">Verae</span>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<b style="font-size:12.5px">Verify this certificate</b>
|
<b style="font-size:12.5px">Verify this certificate</b>
|
||||||
<ol>
|
<ol>
|
||||||
<li>Open the explorer link (or scan the QR on a printed PDF).</li>
|
<li>Open the explorer link (or scan the QR when final).</li>
|
||||||
<li>Compare the local file’s SHA-256 to the multihash below — file never uploaded.</li>
|
<li>Compare the local file’s SHA-256 to the multihash below — file never uploaded.</li>
|
||||||
<li>Only a <b>final</b> / already-registered receipt is evidence. Hold ids are not proof.</li>
|
<li>Only a <b>final</b> / already-registered receipt is evidence. Hold ids are not proof.</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
@ -192,7 +201,8 @@
|
||||||
<span class="mono">?document=&sha256=&receipt=&created_at=&merkle=&blake3=&block=&helper_version=</span>
|
<span class="mono">?document=&sha256=&receipt=&created_at=&merkle=&blake3=&block=&helper_version=</span>
|
||||||
Multihash / BLAKE3 lines appear <b>only when data is present</b> — never invented.
|
Multihash / BLAKE3 lines appear <b>only when data is present</b> — never invented.
|
||||||
Title uses Fraunces (Google Fonts CDN) with Georgia/serif offline fallback; plum/gold frame kept.
|
Title uses Fraunces (Google Fonts CDN) with Georgia/serif offline fallback; plum/gold frame kept.
|
||||||
Full CERT-01.html pixel match (self-hosted design-kit fonts, live QR) remains a residual.
|
Final certificates paint a live QR (qrcode.js CDN → data-URL) from the explorer lookup; offline / provisional keeps the SVG placeholder so print still works.
|
||||||
|
Residual: full CERT-01.html self-hosted Fraunces/Cantarell pixel match (design-kit fonts).
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -283,6 +293,70 @@
|
||||||
return u;
|
return u;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Live QR when final + payload; CDN fail / offline / provisional → SVG placeholder (print-safe). */
|
||||||
|
function clearQR(el) {
|
||||||
|
if (!el) return;
|
||||||
|
el.classList.remove('has-qr');
|
||||||
|
var bit = el.querySelectorAll('img.qr-bitmap, canvas.qr-bitmap');
|
||||||
|
for (var i = 0; i < bit.length; i++) bit[i].remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
function paintQRDataUrl(el, url) {
|
||||||
|
if (!el || !url) return;
|
||||||
|
clearQR(el);
|
||||||
|
var img = document.createElement('img');
|
||||||
|
img.className = 'qr-bitmap';
|
||||||
|
img.alt = '';
|
||||||
|
img.width = 112;
|
||||||
|
img.height = 112;
|
||||||
|
img.decoding = 'async';
|
||||||
|
img.onload = function () { el.classList.add('has-qr'); };
|
||||||
|
img.onerror = function () { img.remove(); /* keep placeholder */ };
|
||||||
|
img.src = url;
|
||||||
|
el.appendChild(img);
|
||||||
|
}
|
||||||
|
|
||||||
|
function withQRCodeLib(fn) {
|
||||||
|
if (window.QRCode && typeof window.QRCode.toDataURL === 'function') {
|
||||||
|
fn(window.QRCode);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var existing = document.querySelector('script[data-verae-qrcode-lib]');
|
||||||
|
if (existing) {
|
||||||
|
existing.addEventListener('load', function () {
|
||||||
|
if (window.QRCode && window.QRCode.toDataURL) fn(window.QRCode);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var s = document.createElement('script');
|
||||||
|
s.src = 'https://cdn.jsdelivr.net/npm/qrcode@1.5.4/build/qrcode.min.js';
|
||||||
|
s.async = true;
|
||||||
|
s.setAttribute('data-verae-qrcode-lib', '1');
|
||||||
|
s.onload = function () {
|
||||||
|
if (window.QRCode && window.QRCode.toDataURL) fn(window.QRCode);
|
||||||
|
};
|
||||||
|
/* onerror: leave placeholder — offline print fallback */
|
||||||
|
document.head.appendChild(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setVerifyQR(payload, isFinal) {
|
||||||
|
var el = $('qr');
|
||||||
|
if (!el) return;
|
||||||
|
clearQR(el);
|
||||||
|
if (!isFinal || !payload) return;
|
||||||
|
withQRCodeLib(function (QRCode) {
|
||||||
|
QRCode.toDataURL(String(payload), {
|
||||||
|
errorCorrectionLevel: 'M',
|
||||||
|
margin: 1,
|
||||||
|
width: 224,
|
||||||
|
color: { dark: '#1D1534', light: '#FFFFFF' }
|
||||||
|
}, function (err, dataUrl) {
|
||||||
|
if (err || !dataUrl) return;
|
||||||
|
paintQRDataUrl(el, dataUrl);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function apply(data) {
|
function apply(data) {
|
||||||
data = data || {};
|
data = data || {};
|
||||||
var doc = data.document_name || data.document || data.file || '';
|
var doc = data.document_name || data.document || data.file || '';
|
||||||
|
|
@ -351,6 +425,10 @@
|
||||||
var a = $('verify-link');
|
var a = $('verify-link');
|
||||||
a.href = url;
|
a.href = url;
|
||||||
a.textContent = url;
|
a.textContent = url;
|
||||||
|
|
||||||
|
/* Real QR only when final evidence + receipt or digest; else placeholder. */
|
||||||
|
var qrPayload = (sha || receipt || mh) ? url : '';
|
||||||
|
setVerifyQR(qrPayload, evidence && !!qrPayload);
|
||||||
}
|
}
|
||||||
|
|
||||||
function fromQuery() {
|
function fromQuery() {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue