PrintToVeraeTimeStamp/.github/workflows/windows-msi-sign.yml.example
George Lambert 3140e6c76f
Some checks failed
windows-msi / full-msi-unsigned (push) Has been cancelled
feat: PUBLISH-DIGESTS.md + commented Authenticode CI paths + EmbedBrandFonts TTF
windows-msi-sign.yml.example; gofpdf register when EmbedBrandFonts and fonts present.
2026-10-14 01:24:11 -04:00

164 lines
8 KiB
Text

# EXAMPLE ONLY — not a live workflow.
# Copy into .github/workflows/windows-msi-sign.yml (or .forgejo/workflows/) and enable
# only when org secret store holds the names below. Default product CI remains
# windows-msi.yml (unsigned full MSI).
#
# Secret / var NAMES only — never commit PFX bytes, passwords, thumbprints as values,
# or Azure client secrets. See platform/windows/wix/CI-SIGN-NOTARY.md.
#
# GitHub Actions: secrets.* / vars.* as below.
# Forgejo Actions: same secret names; map runs-on to your Windows label; keep names identical.
#
# After sign + upload: docs/PUBLISH-DIGESTS.md + fill-manifest-sha256.ps1 (hash only; no upload).
#
# name: windows-msi-sign
# on:
# workflow_dispatch:
# # push:
# # tags: ["printstamp-v*"]
name: windows-msi-sign-example
on:
workflow_dispatch:
permissions:
contents: read
# id-token: write # uncomment if using Azure OIDC → Trusted Signing (no client secret)
jobs:
# Live default stays unsigned — see windows-msi.yml.
# This example job is gated off until secrets exist.
full-msi-sign:
if: ${{ false }} # flip only after secrets exist; do not claim SmartScreen until clean-VM proof
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
- name: Install WiX Toolset v3
shell: pwsh
run: |
choco install wixtoolset --version 3.14.1 -y --no-progress
$wixBin = "${env:ProgramFiles(x86)}\WiX Toolset v3.14\bin"
if (-not (Test-Path -LiteralPath (Join-Path $wixBin "candle.exe"))) {
$found = Get-ChildItem -Path "${env:ProgramFiles(x86)}" -Filter "candle.exe" -Recurse -ErrorAction SilentlyContinue |
Where-Object { $_.FullName -match 'WiX Toolset' } |
Select-Object -First 1
if ($found) { $wixBin = $found.Directory.FullName }
}
if (-not (Test-Path -LiteralPath (Join-Path $wixBin "candle.exe"))) {
Write-Error "WiX candle.exe not found after install."
}
"WIX_BIN=$wixBin" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
"PATH=$wixBin;$env:PATH" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
- name: Build full MSI (pre-sign)
shell: pwsh
run: |
powershell -NoProfile -ExecutionPolicy Bypass -File platform\windows\scripts\build-full-msi.ps1 -Arch amd64
$msi = Get-ChildItem -Path platform\windows\wix\out -Filter "VeraePrintStamp-windows-*.msi" |
Sort-Object LastWriteTime -Descending | Select-Object -First 1
if (-not $msi) { Write-Error "No MSI under platform/windows/wix/out" }
"MSI_PATH=$($msi.FullName)" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
"MSI_NAME=$($msi.Name)" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
$stage = "platform\windows\wix\StageFull\bin"
"STAGE_BIN=$stage" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
# --- Path A: PFX via secrets (signtool) ---
# Secret / var names (placeholders — values never in git):
# secrets.WINDOWS_CERT_PFX_BASE64
# secrets.WINDOWS_CERT_PFX_PASSWORD
# secrets.WINDOWS_CERT_THUMBPRINT # optional if /sha1 preferred after import
# vars.WINDOWS_SIGN_TIMESTAMP_URL # RFC 3161 TSA
#
# - name: Import code-signing PFX
# shell: pwsh
# env:
# WINDOWS_CERT_PFX_BASE64: ${{ secrets.WINDOWS_CERT_PFX_BASE64 }}
# WINDOWS_CERT_PFX_PASSWORD: ${{ secrets.WINDOWS_CERT_PFX_PASSWORD }}
# run: |
# $pfx = Join-Path $env:RUNNER_TEMP "codesign.pfx"
# [IO.File]::WriteAllBytes($pfx, [Convert]::FromBase64String($env:WINDOWS_CERT_PFX_BASE64))
# $pwd = ConvertTo-SecureString $env:WINDOWS_CERT_PFX_PASSWORD -AsPlainText -Force
# Import-PfxCertificate -FilePath $pfx -CertStoreLocation Cert:\CurrentUser\My -Password $pwd | Out-Null
# Remove-Item -LiteralPath $pfx -Force -ErrorAction SilentlyContinue
# # Never Upload-Artifact the PFX; never Write-Host password or base64.
#
# - name: signtool Authenticode (EXEs + MSI)
# shell: pwsh
# env:
# WINDOWS_SIGN_TIMESTAMP_URL: ${{ vars.WINDOWS_SIGN_TIMESTAMP_URL }}
# WINDOWS_CERT_THUMBPRINT: ${{ secrets.WINDOWS_CERT_THUMBPRINT }}
# run: |
# $tsa = $env:WINDOWS_SIGN_TIMESTAMP_URL
# if (-not $tsa) { $tsa = "https://timestamp.digicert.com" }
# $signtool = (Get-Command signtool.exe -ErrorAction Stop).Source
# $targets = @(
# (Join-Path $env:STAGE_BIN "verae-print.exe"),
# (Join-Path $env:STAGE_BIN "verae-printhelper.exe"),
# $env:MSI_PATH
# ) | Where-Object { $_ -and (Test-Path -LiteralPath $_) }
# foreach ($t in $targets) {
# if ($env:WINDOWS_CERT_THUMBPRINT) {
# & $signtool sign /fd SHA256 /tr $tsa /td SHA256 /sha1 $env:WINDOWS_CERT_THUMBPRINT $t
# } else {
# & $signtool sign /fd SHA256 /tr $tsa /td SHA256 /a $t
# }
# if ($LASTEXITCODE -ne 0) { Write-Error "signtool sign failed: $t" }
# & $signtool verify /pa $t
# if ($LASTEXITCODE -ne 0) { Write-Error "signtool verify failed: $t" }
# }
# --- Path B: Azure Trusted Signing (prefer over PFX when available) ---
# Secret / var names (placeholders — values never in git):
# secrets.WINDOWS_TRUSTED_SIGNING_TENANT_ID # or OIDC; skip client secret
# secrets.WINDOWS_TRUSTED_SIGNING_CLIENT_ID
# secrets.WINDOWS_TRUSTED_SIGNING_CLIENT_SECRET # omit when id-token: write + federated creds
# vars.WINDOWS_TRUSTED_SIGNING_ENDPOINT # e.g. https://cus.codesigning.azure.net
# vars.WINDOWS_TRUSTED_SIGNING_ACCOUNT
# vars.WINDOWS_TRUSTED_SIGNING_CERTIFICATE_PROFILE
# vars.WINDOWS_SIGN_TIMESTAMP_URL # optional; action may default TSA
#
# - name: Azure Trusted Signing (EXEs + MSI)
# # uses: azure/trusted-signing-action@v0 # pin a reviewed tag when enabling
# # with:
# # azure-tenant-id: ${{ secrets.WINDOWS_TRUSTED_SIGNING_TENANT_ID }}
# # azure-client-id: ${{ secrets.WINDOWS_TRUSTED_SIGNING_CLIENT_ID }}
# # azure-client-secret: ${{ secrets.WINDOWS_TRUSTED_SIGNING_CLIENT_SECRET }}
# # endpoint: ${{ vars.WINDOWS_TRUSTED_SIGNING_ENDPOINT }}
# # trusted-signing-account-name: ${{ vars.WINDOWS_TRUSTED_SIGNING_ACCOUNT }}
# # certificate-profile-name: ${{ vars.WINDOWS_TRUSTED_SIGNING_CERTIFICATE_PROFILE }}
# # files-folder: ${{ env.STAGE_BIN }}
# # files-folder-filter: exe
# # files-folder-recurse: true
# # file-digest: SHA256
# # timestamp-rfc3161: ${{ vars.WINDOWS_SIGN_TIMESTAMP_URL }}
# # timestamp-digest: SHA256
# shell: pwsh
# run: |
# Write-Error "Trusted Signing path not enabled — wire action + secret names above."
# - name: Upload signed MSI artifact
# uses: actions/upload-artifact@v4
# with:
# name: VeraePrintStamp-windows-msi-signed
# path: platform/windows/wix/out/VeraePrintStamp-windows-*.msi
# if-no-files-found: error
# retention-days: 14
# Operator after download: upload CDN blob → fill-manifest-sha256.ps1 → paste digests.
# See docs/PUBLISH-DIGESTS.md (does not upload; secret values never land in manifests).
- name: Placeholder (example not enabled)
shell: pwsh
run: |
Write-Host "windows-msi-sign.yml.example — sign job gated if: false."
Write-Host "Live unsigned build: .github/workflows/windows-msi.yml"
Write-Host "Local dry-run: platform/windows/scripts/sign-dry-run.ps1"
Write-Host "Digests after sign+upload: docs/PUBLISH-DIGESTS.md"