docker / build-push-arm64cross (push) Successful in 10s
docker / deploy-docs (push) Successful in 31s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 26m54s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 26m56s
apple / screenshots (push) Canceled after 11m30s
windows-host / package (push) Failing after 12s
windows-host / winget-source (push) Skipped
apple / swift (push) Successful in 6m2s
ci / web (push) Successful in 1m53s
ci / docs-site (push) Successful in 1m29s
ci / rust-arm64 (push) Successful in 9m56s
android / android (push) Successful in 12m33s
ci / bench (push) Successful in 7m17s
decky / build-publish (push) Successful in 29s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 12s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 12s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 11s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 11s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 19s
arch / build-publish (push) Successful in 18m18s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 16s
deb / build-publish (push) Successful in 9m2s
ci / rust (push) Failing after 19m38s
deb / build-publish-client-arm64 (push) Successful in 7m21s
deb / build-publish-host (push) Successful in 9m43s
`winget install unom.PunktfunkHost` after adding the source, and `winget upgrade`
from then on. Windows had no update path at all before this — no self-update, no
package manager — so keeping a host current meant noticing a release and
re-running an installer by hand.
Ships the manifest trio in winget-pkgs' own format (so submitting upstream later
is a copy, not a rewrite) plus a release-time generator that substitutes only
version, URL, hash and release-notes link. Everything reviewable — the silent
switches, the agreements, the installation notes — stays in the checked-in
templates rather than buried in a generator.
Silent installs deliberately take the SAME task defaults the wizard shows: a
per-channel default is a support trap. The disclosures the wizard puts on screen
travel as manifest Agreements instead, shown before install and requiring
acceptance — VB-Audio's bundling grant wants the user to see VB-CABLE's origin
and donationware status, and a silent install shows them nothing otherwise. The
console password can only be pointed at, never printed: it is generated per
install and the notes are fixed at publish time.
Self-hosted rather than the community repo, which gates on Defender/SmartScreen
validation the self-signed installer cert would not clear today. The source is
three endpoints — /information, /manifestSearch, /packageManifests/{id}. The
reference implementation's other twenty are its admin API for mutating a
CosmosDB; a catalogue generated at release time has nothing to mutate.
It runs on unom-1 as a stock bun image with the two .mjs files bind-mounted, the
same shape as the flatpak server, so there is no image to build or pull. The
catalogue is derived from the RELEASES rather than local files: winget resolves
--version and upgrade against the version list, so a source that only knew the
newest release could neither pin an older one nor show an upgrade path from it.
That also leaves no state to drift — re-running the build reproduces it exactly.
NormalizedPackageNameAndPublisher is declared unsupported on purpose. winget
derives it client-side with its own normalization, and a near-miss silently
mis-correlates an installed host; ProductCode is exact and Inno gives us one.
28 checks drive the handler directly, and CI gates on them before anything
reaches the box — a wrong response shape does not fail loudly, it just makes
winget report "no package found".
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
76 lines
3.9 KiB
PowerShell
76 lines
3.9 KiB
PowerShell
# 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: <OutDir>/unom.PunktfunkHost.yaml
|
|
# <OutDir>/unom.PunktfunkHost.installer.yaml
|
|
# <OutDir>/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"
|