# Emit the winget manifest trio for a released Windows host installer. # # winget pins the installer by SHA256, so a manifest is only ever valid for ONE immutable artifact. # The `latest/` channel alias in the generic registry is therefore NOT usable here — this always # writes the versioned Gitea release URL, which the registry makes immutable (a re-upload 409s). # # The three files are templated from packaging/winget/, which holds the reviewed, hand-maintained # copy: everything except PackageVersion / InstallerUrl / InstallerSha256 is edited THERE, not here. # That keeps the switches, agreements and installation notes under normal code review rather than # buried in a generator. # # Usage (from windows-host.yml, stable tags only): # & scripts/ci/winget-manifest.ps1 -Version 0.19.2 -InstallerPath C:\t\out\punktfunk-host-setup-0.19.2.exe -OutDir C:\t\out\winget # # Emits: /unom.PunktfunkHost.yaml # /unom.PunktfunkHost.installer.yaml # /unom.PunktfunkHost.locale.en-US.yaml [CmdletBinding()] param( [Parameter(Mandatory = $true)][string]$Version, # The packed setup.exe — hashed here so the manifest can never disagree with what shipped. [Parameter(Mandatory = $true)][string]$InstallerPath, [Parameter(Mandatory = $true)][string]$OutDir, [string]$TemplateDir = (Join-Path $PSScriptRoot '..\..\packaging\winget'), # Overridable so a fork/mirror can retarget without editing the templates. [string]$UrlBase = 'https://git.unom.io/unom/punktfunk/releases/download' ) $ErrorActionPreference = 'Stop' if (-not (Test-Path $InstallerPath)) { throw "installer not found: $InstallerPath" } $TemplateDir = (Resolve-Path $TemplateDir).Path # winget's own tooling emits UPPERCASE hex; match it so a manifest diff against a wingetcreate- # generated one is empty rather than a case-only churn. $sha = (Get-FileHash -Path $InstallerPath -Algorithm SHA256).Hash.ToUpperInvariant() $url = "$UrlBase/v$Version/$(Split-Path $InstallerPath -Leaf)" Write-Host "winget manifest: version=$Version sha256=$sha" Write-Host " url=$url" New-Item -ItemType Directory -Force -Path $OutDir | Out-Null # PackageVersion appears in all three files; InstallerUrl/Sha256 only in the installer manifest. # Anchored replacements (line-leading key) so a version string inside prose — e.g. the release-notes # URL in the locale manifest — is never rewritten by accident. $files = @( 'unom.PunktfunkHost.yaml', 'unom.PunktfunkHost.installer.yaml', 'unom.PunktfunkHost.locale.en-US.yaml' ) foreach ($f in $files) { $src = Join-Path $TemplateDir $f if (-not (Test-Path $src)) { throw "template missing: $src" } $text = Get-Content -Path $src -Raw $text = [regex]::Replace($text, '(?m)^PackageVersion:.*$', "PackageVersion: $Version") $text = [regex]::Replace($text, '(?m)^(\s*)InstallerUrl:.*$', "`${1}InstallerUrl: $url") $text = [regex]::Replace($text, '(?m)^(\s*)InstallerSha256:.*$', "`${1}InstallerSha256: $sha") # The release-notes link is per-version and lives outside the anchored keys above. $text = [regex]::Replace($text, '(?m)^ReleaseNotesUrl:.*$', "ReleaseNotesUrl: https://git.unom.io/unom/punktfunk/src/tag/v$Version/docs/releases/v$Version.md") $dest = Join-Path $OutDir $f # UTF-8 *without* BOM: winget's YAML parser rejects a BOM'd manifest. [IO.File]::WriteAllText($dest, $text, (New-Object Text.UTF8Encoding $false)) Write-Host " wrote $dest" } # Fail loudly if a template ever loses one of the fields we rewrite — a silently un-substituted # manifest would publish the PREVIOUS release's hash, which winget would then refuse to install. $inst = Get-Content -Path (Join-Path $OutDir 'unom.PunktfunkHost.installer.yaml') -Raw foreach ($needle in @($Version, $sha, $url)) { if ($inst -notmatch [regex]::Escape($needle)) { throw "substitution failed - '$needle' missing from the installer manifest" } } Write-Host "winget manifests ready in $OutDir"