docs: thin-NSIS exit checklist + menubar ci-notarize.yml.example
Some checks failed
windows-msi / full-msi-unsigned (push) Has been cancelled

DUAL-SHIP exit criteria A–E; notary CI sketch with secret names only.
This commit is contained in:
George Lambert 2026-10-14 00:13:54 -04:00
commit dcee1918dc
6 changed files with 186 additions and 22 deletions

View file

@ -15,8 +15,9 @@ 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); 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. | | **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 live CI keychain import. |
| **Full elevated MSI (StageFull + light)** | [`Product-Full.wxs`](../platform/windows/wix/Product-Full.wxs) + [`scripts/build-full-msi.ps1`](../platform/windows/scripts/build-full-msi.ps1) (+ outline, [`SILENT_PROPS.md`](../platform/windows/wix/SILENT_PROPS.md), [`CI-SIGN-NOTARY.md`](../platform/windows/wix/CI-SIGN-NOTARY.md), [`DUAL-SHIP.md`](../platform/windows/wix/DUAL-SHIP.md)) | Elevated WiX v3 product: perMachine, Feature/ComponentGroups for **bin** (`verae-print.exe` required), **scripts**, **docs**, Start Menu; silent public props; distinct UpgradeCode. `build-full-msi.ps1` creates `StageFull/{bin,scripts}`, copies or Go-builds EXEs, copies scripts/LICENSE/README, stamps **ProductVersion** from `Get-VeraePackageVersion`, runs candle/light → `VeraePrintStamp-windows-*-<version>.msi`. WiX missing → **exit 1** + clear tip. Version defaults on build-windows / build-pkg / NSIS / build-installer aligned to HelperVersion. Commented deferred CA + `InstallExecuteSequence` stubs for `install-printer.ps1` (no binaries required). Dual-ship runbook documents NSIS vs MSI clean-VM gates. | **Not** Authenticode-signed; printer CAs still **commented** (not scheduled/tested); properties not yet honored by live CAs; not Intune-proven; NSIS still primary full install; CI builds **unsigned** MSI only (sign step not live). | | **Menubar CI notary sketch** | [`platform/macos/menubar/ci-notarize.yml.example`](../platform/macos/menubar/ci-notarize.yml.example) | GHA/Forgejo **example** workflow: default **unsigned** `build-menubar-app.sh` + artifact; commented notarytool job gated `if: false`; secret **names** only (`APPLE_API_KEY_*`, `APPLE_CODESIGN_IDENTITY`, …). | Not a live workflow under `.github/`; no staple; not Gatekeeper-proven. |
| **Full elevated MSI (StageFull + light)** | [`Product-Full.wxs`](../platform/windows/wix/Product-Full.wxs) + [`scripts/build-full-msi.ps1`](../platform/windows/scripts/build-full-msi.ps1) (+ outline, [`SILENT_PROPS.md`](../platform/windows/wix/SILENT_PROPS.md), [`CI-SIGN-NOTARY.md`](../platform/windows/wix/CI-SIGN-NOTARY.md), [`DUAL-SHIP.md`](../platform/windows/wix/DUAL-SHIP.md)) | Elevated WiX v3 product: perMachine, Feature/ComponentGroups for **bin** (`verae-print.exe` required), **scripts**, **docs**, Start Menu; silent public props; distinct UpgradeCode. `build-full-msi.ps1` creates `StageFull/{bin,scripts}`, copies or Go-builds EXEs, copies scripts/LICENSE/README, stamps **ProductVersion** from `Get-VeraePackageVersion`, runs candle/light → `VeraePrintStamp-windows-*-<version>.msi`. WiX missing → **exit 1** + clear tip. Version defaults on build-windows / build-pkg / NSIS / build-installer aligned to HelperVersion. Commented deferred CA + `InstallExecuteSequence` stubs for `install-printer.ps1` (no binaries required). Dual-ship runbook documents NSIS vs MSI clean-VM gates + **thin-NSIS exit checklist**. | **Not** Authenticode-signed; printer CAs still **commented** (not scheduled/tested); properties not yet honored by live CAs; not Intune-proven; NSIS still primary full install; CI builds **unsigned** MSI only (sign step not live). |
| **CI unsigned full MSI** | [`.github/workflows/windows-msi.yml`](../.github/workflows/windows-msi.yml) + DUAL-SHIP § CI artifact | `windows-latest`: install WiX v3 → `build-full-msi.ps1` → upload artifact `VeraePrintStamp-windows-msi-unsigned`. Future Authenticode secret **names** only in workflow comments / CI-SIGN-NOTARY. | No live `signtool`; no certs in git; not a production download channel. | | **CI unsigned full MSI** | [`.github/workflows/windows-msi.yml`](../.github/workflows/windows-msi.yml) + DUAL-SHIP § CI artifact | `windows-latest`: install WiX v3 → `build-full-msi.ps1` → upload artifact `VeraePrintStamp-windows-msi-unsigned`. Future Authenticode secret **names** only in workflow comments / CI-SIGN-NOTARY. | No live `signtool`; no certs in git; not a production download channel. |
### Issue 1 — what landed vs still open ### Issue 1 — what landed vs still open
@ -27,11 +28,12 @@ Repo now includes **authoring/docs scaffolds** for the two commercial gaps that
| `build-full-msi.ps1` StageFull + ProductVersion + exit codes | Live CI: **signtool / Trusted Signing** on MSI + EXEs (workflow sign step commented; secret names only) | | `build-full-msi.ps1` StageFull + ProductVersion + exit codes | Live CI: **signtool / Trusted Signing** on MSI + EXEs (workflow sign step commented; secret names only) |
| `CI-SIGN-NOTARY.md` GHA/Forgejo draft (secret **names** only) | Live notarytool staple on macOS runners; clean-VM Gatekeeper proof | | `CI-SIGN-NOTARY.md` GHA/Forgejo draft (secret **names** only) | Live notarytool staple on macOS runners; clean-VM Gatekeeper proof |
| `SILENT_PROPS.md` msiexec one-liner + exit codes for full MSI | Properties honored by **scheduled** CAs (config write, skip printers/key) | | `SILENT_PROPS.md` msiexec one-liner + exit codes for full MSI | Properties honored by **scheduled** CAs (config write, skip printers/key) |
| HelperVersion path on build-windows / build-pkg / NSIS / build-installer / full MSI | Dual-ship **clean-VM validation** then thin NSIS; Intune silent smoke | | HelperVersion path on build-windows / build-pkg / NSIS / build-installer / full MSI | Dual-ship **clean-VM validation** (A/B/C); then execute thin-NSIS exit checklist |
| Commented CA + `InstallExecuteSequence` stubs (`install-printer` schedule) | Publish 0.3.6 download blobs + refresh `DOWNLOADS-MANIFEST` checksums | | Commented CA + `InstallExecuteSequence` stubs (`install-printer` schedule) | Intune/SCCM silent smoke with real exit codes; publish 0.3.6 signed blobs + manifest checksums |
| [`DUAL-SHIP.md`](../platform/windows/wix/DUAL-SHIP.md) NSIS+MSI side-by-side + **CI artifact** section | macOS menubar automated notarize (checklist exists; script default still unsigned) | | [`DUAL-SHIP.md`](../platform/windows/wix/DUAL-SHIP.md) NSIS+MSI + CI artifact + **thin-NSIS exit checklist** | Site primary download still NSIS; thin/demote NSIS only after exit checklist A–D |
| Site product string → **0.3.6**; manifest header notes HelperVersion vs last-published 0.3.2 | Do **not** claim Authenticode / notary / dual-ship complete | | Site product string → **0.3.6**; manifest header notes HelperVersion vs last-published 0.3.2 | Do **not** claim Authenticode / notary / dual-ship / thin-NSIS complete |
| [`.github/workflows/windows-msi.yml`](../.github/workflows/windows-msi.yml) unsigned MSI on `windows-latest` + artifact upload | Wire org secrets and enable sign step; publish signed assets to site | | [`.github/workflows/windows-msi.yml`](../.github/workflows/windows-msi.yml) unsigned MSI on `windows-latest` + artifact upload | Wire org secrets and enable sign step; publish signed assets to site |
| [`ci-notarize.yml.example`](../platform/macos/menubar/ci-notarize.yml.example) menubar GHA/Forgejo sketch (unsigned default; secret names) | Promote example → live workflow; automate `build-menubar-app.sh` sign when `SIGNING_ENV` set |
**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`. Optional: `build-tray-msi.ps1` (WiX tip exit 0); `build-full-msi.ps1` (needs WiX; exit 1 if missing). 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`. Optional: `build-tray-msi.ps1` (WiX tip exit 0); `build-full-msi.ps1` (needs WiX; exit 1 if missing).
@ -66,7 +68,7 @@ Related but out of scope for “done” above (still backlog-adjacent): Linux `.
| **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 | | **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 |
| **WiX full elevated MSI** (StageFull driver) | [`Product-Full.wxs`](../platform/windows/wix/Product-Full.wxs) + [`scripts/build-full-msi.ps1`](../platform/windows/scripts/build-full-msi.ps1) | **Authoring + stage/light path** — MSI when WiX present; unsigned; printer CAs commented stubs; dual-ship NSIS | | **WiX full elevated MSI** (StageFull driver) | [`Product-Full.wxs`](../platform/windows/wix/Product-Full.wxs) + [`scripts/build-full-msi.ps1`](../platform/windows/scripts/build-full-msi.ps1) | **Authoring + stage/light path** — MSI when WiX present; unsigned; printer CAs commented stubs; dual-ship NSIS |
| **CI unsigned full MSI** | [`.github/workflows/windows-msi.yml`](../.github/workflows/windows-msi.yml) | **Live workflow** — WiX install → `build-full-msi.ps1` → unsigned artifact; sign step commented (secret names only) | | **CI unsigned full MSI** | [`.github/workflows/windows-msi.yml`](../.github/workflows/windows-msi.yml) | **Live workflow** — WiX install → `build-full-msi.ps1` → unsigned artifact; sign step commented (secret names only) |
| **Dual-ship runbook** | [`wix/DUAL-SHIP.md`](../platform/windows/wix/DUAL-SHIP.md) | **Docs** — NSIS + MSI clean-VM matrix; CI artifact section; thin-NSIS exit criteria | | **Dual-ship runbook** | [`wix/DUAL-SHIP.md`](../platform/windows/wix/DUAL-SHIP.md) | **Docs** — NSIS + MSI clean-VM matrix; CI artifact section; **thin-NSIS exit checklist** (A–E) |
| Shared version helper | [`platform/windows/scripts/common.ps1`](../platform/windows/scripts/common.ps1) `Get-VeraePackageVersion` | Aligns tray/full MSI/NSIS drivers with `HelperVersion` | | Shared version helper | [`platform/windows/scripts/common.ps1`](../platform/windows/scripts/common.ps1) `Get-VeraePackageVersion` | Aligns tray/full MSI/NSIS drivers 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 |
@ -99,7 +101,7 @@ NSIS product already covers much of the *install experience* (files under Progra
5. Authenticode on binaries + MSI (**not shipped** — enable commented sign step; secret names in workflow + `CI-SIGN-NOTARY.md`). 5. Authenticode on binaries + MSI (**not shipped** — enable commented sign step; secret names in workflow + `CI-SIGN-NOTARY.md`).
6. Honor silent props in live CAs + Intune sample command line proof. 6. Honor silent props in live CAs + Intune sample command line proof.
7. Publish 0.3.6 artifacts; refresh site `DOWNLOADS-MANIFEST` checksums; keep smoke on HelperVersion drift. 7. Publish 0.3.6 artifacts; refresh site `DOWNLOADS-MANIFEST` checksums; keep smoke on HelperVersion drift.
8. Deprecate or thin NSIS once MSI is the primary download (exit criteria in DUAL-SHIP.md). 8. Deprecate or thin NSIS once MSI is the primary download (**thin-NSIS exit checklist** A–D in DUAL-SHIP.md — all boxes).
--- ---
@ -143,7 +145,7 @@ NSIS product already covers much of the *install experience* (files under Progra
2. Decide distribution primary: **`.pkg` only** (already closest to done) vs **self-contained notarized `.app`** (+ optional DMG). 2. Decide distribution primary: **`.pkg` only** (already closest to done) vs **self-contained notarized `.app`** (+ optional DMG).
3. If `.app` primary: bundle helper + resources inside the app; first-run equals today’s postinstall responsibilities without root where possible. 3. If `.app` primary: bundle helper + resources inside the app; first-run equals today’s postinstall responsibilities without root where possible.
4. Automate menubar sign/notarize as accessory artifact (or embed menubar in product app) — **not shipped** as automated path. 4. Automate menubar sign/notarize as accessory artifact (or embed menubar in product app) — **not shipped** as automated path.
5. CI: notarize + staple + `spctl` + checksums; never commit `signing.env`. 5. CI: promote [`ci-notarize.yml.example`](../platform/macos/menubar/ci-notarize.yml.example) → live notarize + staple + `spctl` + checksums; never commit `signing.env`.
6. Optional: DMG, Sparkle. 6. Optional: DMG, Sparkle.
--- ---
@ -169,7 +171,8 @@ NSIS product already covers much of the *install experience* (files under Progra
| 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` + `build-tray-msi.ps1`; ProductVersion ← HelperVersion) | **No** — scaffold only; MSI only when candle/light installed; 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 (files + ARP) | **Yes authoring** (`Product-Full.wxs` + `build-full-msi.ps1` StageFull → light; CA schedule stubs commented) | **No commercial** — unsigned; printer CAs not live; NSIS still primary | | Windows **MSI** full product (files + ARP) | **Yes authoring** (`Product-Full.wxs` + `build-full-msi.ps1` StageFull → light; CA schedule stubs commented) | **No commercial** — unsigned; printer CAs not live; NSIS still primary |
| Dual-ship NSIS↔MSI runbook | **Yes** ([`DUAL-SHIP.md`](../platform/windows/wix/DUAL-SHIP.md)) | **Docs only** — clean-VM validation + thin-NSIS still left | | Dual-ship NSIS↔MSI runbook | **Yes** ([`DUAL-SHIP.md`](../platform/windows/wix/DUAL-SHIP.md)) | **Docs only** — clean-VM validation + thin-NSIS exit checklist still unchecked |
| Menubar CI notary example | **Yes** ([`ci-notarize.yml.example`](../platform/macos/menubar/ci-notarize.yml.example)) | **Example only** — unsigned default; notary job `if: false` |
| Windows Authenticode pipeline | **Draft doc** (`wix/CI-SIGN-NOTARY.md`) | **No** — no live signtool job (**do not claim shipped**) | | Windows Authenticode pipeline | **Draft doc** (`wix/CI-SIGN-NOTARY.md`) | **No** — no live signtool job (**do not claim shipped**) |
| macOS menu bar LaunchAgent | Yes | Yes (dev/ops) | | macOS menu bar LaunchAgent | Yes | Yes (dev/ops) |
| macOS unsigned menubar `.app` | Yes | **No** (Gatekeeper) | | macOS unsigned menubar `.app` | Yes | **No** (Gatekeeper) |
@ -192,6 +195,7 @@ NSIS product already covers much of the *install experience* (files under Progra
- [`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
- [`platform/macos/menubar/ci-notarize.yml.example`](../platform/macos/menubar/ci-notarize.yml.example) — GHA/Forgejo menubar CI sketch (unsigned default; secret names only)
- [`docs/installer-registration-api.md`](installer-registration-api.md) — installer ↔ portal registration API - [`docs/installer-registration-api.md`](installer-registration-api.md) — installer ↔ portal registration API
- [`README.md`](../README.md) — top-level packaging pointers - [`README.md`](../README.md) — top-level packaging pointers

View file

@ -149,12 +149,15 @@ spctl --assess --type execute -vv "$APP"
## Automation gap (still backlog) ## Automation gap (still backlog)
- [x] CI workflow **example** (unsigned default; notary secret **names** only, gated off):
[`ci-notarize.yml.example`](ci-notarize.yml.example) — copy into `.github/workflows/` or
`.forgejo/workflows/` when enabling; do not treat the example as a live release job
- [ ] Wire `build-menubar-app.sh` (or shared `sign-and-notarize.sh`) to run the - [ ] Wire `build-menubar-app.sh` (or shared `sign-and-notarize.sh`) to run the
above when `SIGNING_ENV` / CI secrets exist; keep **unsigned default** for dev above when `SIGNING_ENV` / CI secrets exist; keep **unsigned default** for dev
- [ ] CI macOS runner: import Developer ID + notary key → staple → upload checksums - [ ] Promote example → live CI macOS runner: import Developer ID + notary key → staple → checksums
- [ ] Smoke: `spctl` + `stapler validate` on signed hosts only - [ ] Smoke: `spctl` + `stapler validate` on signed hosts only
Until those land, this checklist is the operator runbook. Until live CI + script automation land, this checklist is the operator runbook.
--- ---

View file

@ -49,6 +49,10 @@ It does not apply codesign itself and never embeds secrets.
Codesign + notarization (document only — needs Apple certs) Codesign + notarization (document only — needs Apple certs)
----------------------------------------------------------- -----------------------------------------------------------
Operator steps: NOTARIZE-CHECKLIST.md
CI sketch (unsigned default; notary secret names only): ci-notarize.yml.example
Copy into .github/workflows or .forgejo/workflows when enabling; not live.
You need an Apple Developer Program membership and a Developer ID You need an Apple Developer Program membership and a Developer ID
Application certificate in the keychain. This project does not ship Application certificate in the keychain. This project does not ship
secrets or signing.env values. When certs are available: secrets or signing.env values. When certs are available:

View file

@ -0,0 +1,109 @@
# EXAMPLE ONLY — not a live workflow.
# Copy into .github/workflows/ or .forgejo/workflows/ and enable when Apple certs exist.
# Default job path is UNSIGNED (build-menubar-app.sh). Sign/notary steps stay gated off.
#
# Secret / var NAMES only — never commit .p8, .p12, passwords, or real Team IDs as values.
# See NOTARIZE-CHECKLIST.md and platform/windows/wix/CI-SIGN-NOTARY.md.
#
# GitHub Actions: secrets.* / vars.* as below.
# Forgejo Actions: same secret names; map runs-on to your macOS label; keep names identical.
#
# name: macos-menubar-notarize
# on:
# workflow_dispatch:
# # push:
# # tags: ["printstamp-v*"]
name: macos-menubar-ci-example
on:
workflow_dispatch:
permissions:
contents: read
jobs:
menubar-unsigned-default:
runs-on: macos-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Build unsigned menubar .app
working-directory: platform/macos/menubar
run: |
set -euo pipefail
# Default product path: no codesign, no notarytool, no secrets.
./build-menubar-app.sh "${RUNNER_TEMP}/Verae Menubar.app"
test -d "${RUNNER_TEMP}/Verae Menubar.app"
echo "Unsigned app at ${RUNNER_TEMP}/Verae Menubar.app"
cat "${RUNNER_TEMP}/Verae Menubar.app/Contents/Resources/VERSION" || true
- name: Upload unsigned .app zip (dev/CI smoke only)
if: ${{ true }}
run: |
set -euo pipefail
APP="${RUNNER_TEMP}/Verae Menubar.app"
ZIP="${RUNNER_TEMP}/VeraeMenubar-unsigned.zip"
ditto -c -k --keepParent "$APP" "$ZIP"
echo "UNSIGNED_ZIP=$ZIP" >> "$GITHUB_ENV"
# Forgejo: write GITHUB_ENV-equivalent or upload path directly.
- name: Upload artifact (unsigned)
uses: actions/upload-artifact@v4
with:
name: VeraeMenubar-macos-unsigned
path: ${{ env.UNSIGNED_ZIP }}
if-no-files-found: warn
retention-days: 14
# --- Future: Developer ID + notarytool (NOT enabled) ---
# Enable only when org secret store holds the names below. Keep unsigned job as default.
# Secret / var names (placeholders — values never in git):
# secrets.APPLE_API_KEY_P8_BASE64 # base64 of AuthKey_*.p8
# secrets.APPLE_API_KEY_ID
# secrets.APPLE_API_ISSUER_ID
# secrets.APPLE_TEAM_ID
# secrets.APPLE_CODESIGN_IDENTITY # "Developer ID Application: … (TEAMID)"
# secrets.APPLE_INSTALLER_IDENTITY # optional; product .pkg path, not menubar
# vars.NOTARY_KEYCHAIN_PROFILE # optional laptop path name; CI prefers API key trio
#
# menubar-notarize:
# if: ${{ false }} # flip only after secrets exist; do not ship Gatekeeper claims until green
# runs-on: macos-latest
# steps:
# - uses: actions/checkout@v4
# - name: Materialize notary API key
# env:
# APPLE_API_KEY_P8_BASE64: ${{ secrets.APPLE_API_KEY_P8_BASE64 }}
# APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
# run: |
# # mkdir -p "$RUNNER_TEMP/private_keys"
# # echo "$APPLE_API_KEY_P8_BASE64" | base64 -d > \
# # "$RUNNER_TEMP/private_keys/AuthKey_${APPLE_API_KEY_ID}.p8"
# # chmod 600 "$RUNNER_TEMP/private_keys/AuthKey_${APPLE_API_KEY_ID}.p8"
# # Never upload .p8 as an artifact; delete in always() step.
# echo "TODO: materialize .p8 outside workspace"
# - name: Build + codesign + notarytool + staple
# env:
# APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
# APPLE_API_ISSUER_ID: ${{ secrets.APPLE_API_ISSUER_ID }}
# APPLE_CODESIGN_IDENTITY: ${{ secrets.APPLE_CODESIGN_IDENTITY }}
# APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
# NOTARY_KEY_PATH: ${{ runner.temp }}/private_keys/AuthKey_${{ secrets.APPLE_API_KEY_ID }}.p8
# run: |
# set -euo pipefail
# # 1) ./build-menubar-app.sh "$RUNNER_TEMP/Verae Menubar.app"
# # 2) codesign --force --deep --options runtime --timestamp \
# # --entitlements … --sign "$APPLE_CODESIGN_IDENTITY" "$APP"
# # 3) ditto -c -k --keepParent "$APP" /tmp/VeraeMenubar.zip
# # 4) xcrun notarytool submit /tmp/VeraeMenubar.zip \
# # --key "$NOTARY_KEY_PATH" --key-id "$APPLE_API_KEY_ID" \
# # --issuer "$APPLE_API_ISSUER_ID" --wait
# # 5) xcrun stapler staple "$APP" && xcrun stapler validate "$APP"
# # 6) spctl --assess --type execute -vv "$APP"
# # Operator detail: NOTARIZE-CHECKLIST.md
# echo "TODO: notary path not enabled — unsigned default only"
# - name: Scrub API key material
# if: always()
# run: rm -rf "${RUNNER_TEMP}/private_keys" || true

View file

@ -178,7 +178,8 @@ jobs:
echo "TODO: fail job on assess failure once pipeline is real" echo "TODO: fail job on assess failure once pipeline is real"
``` ```
Menubar-only accessory: follow [`NOTARIZE-CHECKLIST.md`](../../macos/menubar/NOTARIZE-CHECKLIST.md) (`ditto` zip → `notarytool` → staple app). Menubar-only accessory: follow [`NOTARIZE-CHECKLIST.md`](../../macos/menubar/NOTARIZE-CHECKLIST.md) (`ditto` zip → `notarytool` → staple app).
GHA/Forgejo copy-paste sketch (unsigned default, notary job gated off): [`ci-notarize.yml.example`](../../macos/menubar/ci-notarize.yml.example).
--- ---

View file

@ -2,7 +2,7 @@
**Status:** operator validation while NSIS remains the **primary** full-product Windows download and the elevated MSI (`Product-Full.wxs` + `build-full-msi.ps1`) is **unsigned** and not yet CA-complete. **Status:** operator validation while NSIS remains the **primary** full-product Windows download and the elevated MSI (`Product-Full.wxs` + `build-full-msi.ps1`) is **unsigned** and not yet CA-complete.
**Do not** thin or deprecate NSIS until this runbook’s clean-VM gates pass and Authenticode is live (see [COMMERCIAL-INSTALLER-BACKLOG.md](../../../docs/COMMERCIAL-INSTALLER-BACKLOG.md), [CI-SIGN-NOTARY.md](CI-SIGN-NOTARY.md)). **Do not** thin or deprecate NSIS until the **thin-NSIS exit criteria checklist** (below) is fully checked — clean-VM gates, live Authenticode, silent/fleet proof, and site cutover (see [COMMERCIAL-INSTALLER-BACKLOG.md](../../../docs/COMMERCIAL-INSTALLER-BACKLOG.md), [CI-SIGN-NOTARY.md](CI-SIGN-NOTARY.md)).
**Related:** [Product-Full.wxs](Product-Full.wxs) · [SILENT_PROPS.md](SILENT_PROPS.md) · [../installer-windows.nsi](../installer-windows.nsi) · [PACKAGING.md](../../../docs/PACKAGING.md). **Related:** [Product-Full.wxs](Product-Full.wxs) · [SILENT_PROPS.md](SILENT_PROPS.md) · [../installer-windows.nsi](../installer-windows.nsi) · [PACKAGING.md](../../../docs/PACKAGING.md).
@ -154,17 +154,59 @@ Do **not** mark dual-ship complete until VM C matches VM A on a clean image.
--- ---
## Exit: when to thin NSIS ## Exit: when to thin NSIS (MSI becomes primary)
Thin or demote NSIS to “key-wizard offline only” only when **all** are true: Thin or demote NSIS to “key-wizard offline only” (or drop from primary download) only when **every** box below is checked. Partial passes do **not** promote MSI. Until then: **ship NSIS as primary**; MSI is dual-ship candidate only.
1. VM C printer/helper parity signed off. ### Thin-NSIS exit criteria checklist
2. Live Authenticode on EXE + MSI (CI secrets; never in git).
3. Silent props honored by scheduled CAs; Intune/SCCM one-liner documented with real exit codes.
4. Site primary Windows download points at MSI; NSIS linked as alternate if kept.
5. OPEN-ISSUES Issue 1 “Still left” no longer lists dual-ship/thin NSIS.
Until then: **ship NSIS as primary**; MSI is dual-ship candidate only. #### A — Product parity (clean VMs)
- [ ] **VM A** NSIS baseline signed off (files, printers, helper task, key path, ARP uninstall).
- [ ] **VM B** MSI files-only (`VERAE_SKIP_PRINTERS=1`) install/uninstall clean; version = `HelperVersion`.
- [ ] **VM C** MSI with printer/helper deferred CAs **uncommented, scheduled, and tested** — parity with VM A:
- [ ] Same Program Files layout (`bin\`, `scripts\`, docs).
- [ ] IPP loopback `127.0.0.1:8631` and/or Local Port printers match NSIS names/behavior.
- [ ] Scheduled task **Verae Print Helper** (same principal / trigger class).
- [ ] Start Menu shortcuts resolve; one-page PDF stamp path works (receipt/CID evidence — never jobId alone).
- [ ] `VERAE_SKIP_PRINTERS=1` skips CAs; default installs printers.
- [ ] Uninstall (`msiexec /x`) removes printers, task, files, Start Menu; no orphan HKLM `Software\Verae\PrintStamp`.
- [ ] Side-by-side comparison table (this doc) fully checked for columns A and C.
- [ ] Repair / same-version upgrade (`AllowSameVersionUpgrades`) smoke on MSI after CA enablement.
#### B — Trust / signing
- [ ] Live **Authenticode** on `verae-print.exe`, `verae-printhelper.exe`, and full `.msi` (CI or release host).
- [ ] CI sign step enabled only with org secrets — **never** PFX/passwords in git (names in [CI-SIGN-NOTARY.md](CI-SIGN-NOTARY.md) / `windows-msi.yml` comments).
- [ ] SmartScreen: signed install path validated on a clean VM (warn-only unsigned is **not** enough to promote).
- [ ] Timestamp counter-signature present (`WINDOWS_SIGN_TIMESTAMP_URL` / equivalent TSA).
#### C — Fleet / silent contract
- [ ] Public silent props in [SILENT_PROPS.md](SILENT_PROPS.md) **honored by scheduled CAs** (not docs-only):
- [ ] `VERAE_SKIP_PRINTERS`, `VERAE_SKIP_KEY_SETUP`, channel / API URL props as authored.
- [ ] `msiexec /qn /norestart` one-liner documented with **real** exit codes from clean-VM runs.
- [ ] Intune and/or SCCM sample deployment recorded (success + known failure codes); no reboot required for default path unless documented.
- [ ] UpgradeCode stays distinct from tray MSI; no dual “same product” ARP entries in production guidance.
#### D — Channel / site cutover
- [ ] Site **primary** Windows download points at signed full MSI (`VeraePrintStamp-windows-*-<ver>.msi`).
- [ ] NSIS `setup.exe` linked as **alternate** only (or removed from primary CTA) — not the default button.
- [ ] `DOWNLOADS-MANIFEST.txt` checksums refreshed for the promoted MSI (and remaining NSIS if kept).
- [ ] Release notes / PACKAGING state MSI as primary full-product Windows install.
- [ ] OPEN-ISSUES Issue 1 **Still left** no longer lists dual-ship gate / thin-NSIS / “NSIS still primary”.
#### E — Explicit non-goals until checked
Do **not** thin NSIS if any of these remain true:
- Printer CAs still commented or untested (VM C blocked).
- CI only uploads **unsigned** MSI (`VeraePrintStamp-windows-msi-unsigned`).
- Operators still need NSIS for key wizard with no MSI-equivalent first-run path.
- Tray per-user MSI is being mistaken for full-product MSI.
**When all of A–D are checked:** demote NSIS (thin installer or archive-only), keep dual-ship docs as historical validation, and treat full MSI as the commercial Windows path.
--- ---
@ -207,4 +249,5 @@ Do **not** put PFX bytes or passwords in git. Do not upload the PFX as a workflo
| CI unsigned MSI workflow | [../../../.github/workflows/windows-msi.yml](../../../.github/workflows/windows-msi.yml) | | CI unsigned MSI workflow | [../../../.github/workflows/windows-msi.yml](../../../.github/workflows/windows-msi.yml) |
| Silent properties | [SILENT_PROPS.md](SILENT_PROPS.md) | | Silent properties | [SILENT_PROPS.md](SILENT_PROPS.md) |
| Sign/notary draft | [CI-SIGN-NOTARY.md](CI-SIGN-NOTARY.md) | | Sign/notary draft | [CI-SIGN-NOTARY.md](CI-SIGN-NOTARY.md) |
| Menubar CI notary example | [../../macos/menubar/ci-notarize.yml.example](../../macos/menubar/ci-notarize.yml.example) |
| Commercial backlog | [docs/COMMERCIAL-INSTALLER-BACKLOG.md](../../../docs/COMMERCIAL-INSTALLER-BACKLOG.md) | | Commercial backlog | [docs/COMMERCIAL-INSTALLER-BACKLOG.md](../../../docs/COMMERCIAL-INSTALLER-BACKLOG.md) |