Files
james.maesandClaude Fable 5.1 f8b85ddd83
QRun-IO/ci-policy: ci / fork-guard (pull_request) Skipped
QRun-IO/ci-policy: native / fork-deny (pull_request) Skipped
QRun-IO/ci-policy: ci / Scoped workflow [workflow 5e6bfd71ee8a5674882f7680c695a0ada599622f] (pull_request) Successful in 6s
QRun-IO/ci-policy: ci / head-profile (pull_request) Skipped
QRun-IO/ci-policy: ci / repository-policy (pull_request) Skipped
QRun-IO/ci-policy: ci / application-template-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / gradle-1 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-core-2-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-3 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-3-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-4 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-frontend-browser-5 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-6 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-6-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-7 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-7-head (pull_request) Skipped
QRun-IO/ci-policy: ci / node-8 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-9 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-10 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-playwright-11 (pull_request) Skipped
QRun-IO/ci-policy: ci / repository-policy-12 (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-13 (pull_request) Skipped
QRun-IO/ci-policy: ci / android-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / original-business (pull_request) Skipped
QRun-IO/ci-policy: ci / original-mcp (pull_request) Skipped
QRun-IO/ci-policy: ci / original-voyage (pull_request) Skipped
QRun-IO/ci-policy: ci / original-cli (pull_request) Skipped
QRun-IO/ci-policy: ci / not-enrolled (pull_request) Skipped
QRun-IO/ci-policy: ci / shared-pipeline (pull_request) Skipped
QRun-IO/ci-policy: Shared .NET CI / hosted (pull_request) Failing after 3m11s
QRun-IO/ci-policy: Shared .NET CI / verify (pull_request) Skipped
QRun-IO/ci-policy: Shared .NET CI / portable (pull_request) Skipped
QRun-IO/ci-policy: Shared .NET CI / secrets (pull_request) Successful in 21s
QRun-IO/ci-policy: Shared source and config CI / verify (pull_request) Successful in 13s
QRun-IO/ci-policy: Shared source and config CI / gate (pull_request) Canceled after 0s
QRun-IO/ci-policy: ci / ci-result (pull_request) Canceled after 0s
QRun-IO/ci-policy: Shared source and config CI / secrets (pull_request) Canceled after 8s
QRun-IO/ci-policy: ci / source-config-14 (pull_request) Canceled after 23s
QRun-IO/ci-policy: Shared .NET CI / gate (pull_request) Canceled after 0s
QRun-IO/ci-policy: native / dotnet-0 (pull_request) Canceled after 4m31s
QRun-IO/ci-policy: native / native-result (pull_request) Canceled after 0s
feat: client editions with fully branded builds, installers and update assets
A folder under editions/<id> (descriptor, SVG mark, ICO icon, optional logo) and
Build-Installer.ps1 -Edition <id> produce a fully branded build and installer: the
client's mark and icon on the splash, About, window, taskbar and Start menu; the
client's product, company and file description in every executable; the client's
product name, manufacturer and service display name in the MSI, forced over any
stored labels; the MSI and release asset named after the edition's asset prefix;
and the installed updater following that asset on the shared feed, so a client
install only ever sees its own edition.

CI builds every edition's unsigned installer. The stable release workflow lists
the editions, signs and publishes one installer per edition beside the generic
one through a matrix sign job and an assemble job that re-verifies every
signature and writes a single SHA256SUMS. Package-Release.ps1 includes every
edition. First edition: gg-forge (GG Forge Print Agent by Greater Goods).
Version 1.2.0. No wire change.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: James Maes <james@kof22.com>
2026-11-05 10:51:36 -06:00
..

Machine installer

Build the service, AppKit companion and CLI into one x64 machine MSI:

./build/Build-Installer.ps1 -ProductVersion 1.0.1 -ReleaseVersion 1.0.1

The build publishes self-contained .NET applications into a fresh per-run directory under artifacts/package and builds WiX 6.0.2 authoring. An explicit publish directory must not already exist; this prevents obsolete files from entering the harvested MSI. Existing reviewed outputs can be packaged using -SkipPublish -PayloadDirectory <path>; all component versions must match the requested release and source version. The resulting MSI is local development output and is unsigned unless a signing certificate is explicitly provided.

For a signing certificate already installed in the Windows certificate store:

./build/Build-Installer.ps1 -ProductVersion 1.0.1 -ReleaseVersion 1.0.1 -CertificateThumbprint '<thumbprint>' -TimestampServer 'https://your-approved-timestamp-service'

Signing needs signtool.exe on PATH. The script signs the first-party executables before packaging and signs/verifies the resulting MSI. Stable releases are signed by the release workflow with Azure Artifact Signing instead; see RELEASE-SIGNING.md. Never store signing secrets here.

Install the resulting MSI through an elevated Windows Installer session. It installs QqqPrintAgent as an automatic LocalSystem service, depends on the Windows Spooler, starts the service, and adds the companion to the Start menu. Configure and test printers accessible to LocalSystem; user-only network printer mappings are not sufficient. The current companion's management pipe requires local administrator access.

Customer branding uses this same MSI without a rebuild. Validate a deployment with PowerShell 7.4 or newer:

./build/Install-Branded.ps1 -MsiPath 'C:\Deployment\QqqPrintAgent.msi' -BrandingFile './docs/branding.example.json' -WhatIf

Remove -WhatIf in an elevated administrator PowerShell to install. Optional -ExpectedSHA256 verifies an approved release checksum first. The wrapper validates bounded JSON, 180-character customer/company labels, HTTPS support links and optional local PNG/ICO assets; it stages them under protected ProgramData and restores the previous configuration if MSI installation fails. It launches the standard /i /passive /norestart machine installation. The sample JSON needs no image files. The dry-run fixture ./build/Test-BrandedInstaller.ps1 validates inputs without requesting an installation and runs in CI after Verify.

BRANDNAME and BRANDCOMPANY public MSI properties set the service display name and installed-product labels. Their persisted HKLM values are reused by subsequent ordinary MSI upgrades, including updates installed from the companion. The installer creates a branded shortcut in the all-users Print Connectors Start menu folder; rename/full-uninstall cleanup verifies the exact owned target before deleting it. Optional ICO branding affects the AppKit application icon and this shortcut; embedded release binaries/icons remain shared. Internal service/AppId/MSI upgrade identity and paths stay fixed, and source/license attribution remains visible. See Customer branding for configuration precedence, rename/reset behavior and asset limits.

The installer protects %ProgramData%\QqqPrintAgent for SYSTEM and Administrators and preserves the journal/enrollment through upgrades and uninstall. It replaces app/service/CLI together and stops the old service before removing files. Preserved credentials/data can be deliberately removed by an administrator after decommissioning; do not delete the journal while a device still has unresolved print jobs.

No inbound firewall rule or persistent privileged update daemon is installed. The companion uses AppKit update prompts with a machine MSI adapter: manual or optional startup checks use the fixed GitHub release feed, and installation requires confirmation and administrator rights. Protected staging and a copied CLI helper update all components together; completed stages/logs are retained for administrator review. Same-feed SHA-256 checks do not replace publisher signing. See Updates. Production release qualification must include signed installation, service-account printing while logged out, upgrade/reboot, and uninstall/reinstall recovery. Building an MSI alone does not establish those checks.

The installer suppresses MSI validator ICE03 because the bundled Microsoft WinUI Microsoft.ui.xaml.dll/Microsoft.UI.Xaml.Phone.dll have multi-language version resources longer than that validator's File.Language constraint, and their MUI resources include newer locale IDs absent from its static list. The vendor binaries are retained unchanged. ICE61 is also suppressed for intentional AllowSameVersionUpgrades, supporting RC-to-RC/stable releases that share a numeric MSI version. The app updater requires a strictly newer semantic release; manual MSI deployments must follow semantic release order and avoid downgrades. All other MSI validation remains enabled; installation and launch qualification are still required. See ADR-0002.

References: WiX service installation, file harvesting, MSI directory security.

The signed stable 1.0.1 MSI is published and installed. See release readiness for the dated release and installation evidence. Same-numeric-version upgrades support RC transitions; the updater separately enforces a strictly newer semantic version.