acdream/tools/publish-dist.ps1
Erik 600c331ac6 feat(launcher): Gitea-backed alpha update feed replaces the GitHub Releases source
The launcher reported "no client available" because its update source was
pinned to a GitHub Releases manifest in a PRIVATE repo — nothing anonymous
could ever be fetched from it. Switch the feed to the PUBLIC Gitea repo so a
friend needs no account, and add the two commands that publish it.

- ReleaseManifestClient.ProductionManifestUri now points at
  git.snakedesert.se/erik/acdream raw on the `dist` branch. No update
  machinery changed: the existing strict reader already accepts any HTTPS
  manifest, so this is a URL swap plus a build script.

- tools/publish-bin.ps1 publishes the payloads into /bin and writes
  bin/manifest.json (schema v1, SHA-256 + size per artifact):
    client-win-x64.zip    AcDream.App + acdream-headless
    launcher-win-x64.zip  acdream-launcher + co-deployed acdream-bake
  Stamps InformationalVersion ONLY — never -p:Version, which also rewrites
  project-reference versions inside the committed packages.<rid>.lock.json
  files and churned every one of them with a throwaway build stamp.

- tools/publish-dist.ps1 pushes /bin to the Gitea-only `dist` branch from a
  throwaway worktree, leaving the developer's checkout, index, and HEAD
  untouched. It refuses a GitHub remote outright.

Why `dist` and not main: the launcher payload is ~103 MB because the launcher
and its co-deployed bake CLI are each self-contained single files (deliberate,
see AcDream.Launcher.csproj). GitHub hard-rejects files over 100 MB, and all
three refs currently track main, so payloads on main would break every GitHub
push. `dist` is a single-commit orphan branch that each publish REPLACES, so
superseded builds never accumulate. /bin stays gitignored repo-wide and is
force-added only on that branch.

Verified live: manifest and both payloads serve anonymously over HTTPS, and a
downloaded client payload matches its declared SHA-256 and size byte for byte.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-18 19:49:06 +02:00

161 lines
6.3 KiB
PowerShell

<#
.SYNOPSIS
Publishes the /bin alpha feed to the Gitea-only `dist` branch.
.DESCRIPTION
Copies the payloads written by tools/publish-bin.ps1 onto an orphan `dist`
branch and pushes it to Gitea (the `origin` remote). The launcher's update
check reads that branch's raw URLs.
Why a separate branch, not main:
* The launcher payload is ~103 MB. GitHub hard-rejects any file over
100 MB, so payloads on main would break every GitHub push.
* Each build is ~150 MB. On main that weight would land in the history
every developer clones forever.
`dist` is a single-commit ORPHAN branch — each publish REPLACES it, so the
feed never accumulates old builds. Nothing on it is a parent of main.
.PARAMETER Remote
Remote to publish to. Defaults to `origin` (Gitea). Never pass the GitHub
remote: the payload exceeds its per-file limit.
.EXAMPLE
pwsh -NoProfile -File tools/publish-bin.ps1
pwsh -NoProfile -File tools/publish-dist.ps1
#>
[CmdletBinding()]
param(
[string]$Remote = 'origin',
[switch]$KeepHistory
)
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
if ($PSVersionTable.PSVersion.Major -lt 7) {
throw 'publish-dist requires PowerShell 7 or newer.'
}
$RepoRoot = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..'))
$BinRoot = Join-Path $RepoRoot 'bin'
$ManifestPath = Join-Path $BinRoot 'manifest.json'
if (-not (Test-Path -LiteralPath $ManifestPath)) {
throw "No feed found at '$ManifestPath'. Run tools/publish-bin.ps1 first."
}
$manifest = Get-Content -LiteralPath $ManifestPath -Raw | ConvertFrom-Json
$version = $manifest.version
if ([string]::IsNullOrWhiteSpace($version)) {
throw 'The manifest has no version.'
}
$payloads = @(Get-ChildItem -LiteralPath $BinRoot -File -Filter *.zip)
if ($payloads.Count -eq 0) {
throw "No .zip payloads in '$BinRoot'. Run tools/publish-bin.ps1 first."
}
$remoteUrl = (& git -C $RepoRoot remote get-url $Remote 2>&1)
if ($LASTEXITCODE) { throw "Remote '$Remote' is not configured." }
if ($remoteUrl -match 'github\.com') {
throw "Refusing to publish payloads to '$Remote' ($remoteUrl): GitHub " +
'rejects files over 100 MB and the alpha feed is Gitea-only.'
}
Write-Host "Publishing alpha feed $version to $Remote ($remoteUrl)" -ForegroundColor Cyan
foreach ($payload in $payloads) {
' {0,-26} {1,8:N1} MB' -f $payload.Name, ($payload.Length / 1MB) | Write-Host
}
# A throwaway worktree keeps the developer's checkout, index, and HEAD
# completely untouched while the dist branch is built and pushed.
$stamp = [DateTime]::UtcNow.ToString('yyyyMMddHHmmss')
$workTree = Join-Path ([IO.Path]::GetTempPath()) "acdream-dist-$stamp"
$branch = 'dist'
# Build under a unique local branch and push it AS dist. Reusing the name
# locally breaks the second publish outright: `checkout --orphan dist` fails
# once a local dist ref exists (observed 2026-08-18).
$stagingBranch = "dist-publish-$stamp"
try {
if ($KeepHistory) {
& git -C $RepoRoot fetch $Remote $branch 2>&1 | Out-Null
$hasRemoteBranch = -not $LASTEXITCODE
& git -C $RepoRoot worktree add --no-checkout -b $stagingBranch $workTree `
$(if ($hasRemoteBranch) { "$Remote/$branch" } else { 'HEAD' }) 2>&1 | Out-Null
if ($LASTEXITCODE) { throw 'Could not create the dist worktree.' }
& git -C $workTree checkout . 2>&1 | Out-Null
}
else {
# Default: one commit, no ancestry. Each publish REPLACES the branch so
# superseded payloads never pile up in the object store.
& git -C $RepoRoot worktree add --detach $workTree 2>&1 | Out-Null
if ($LASTEXITCODE) { throw 'Could not create the dist worktree.' }
& git -C $workTree checkout --orphan $stagingBranch 2>&1 | Out-Null
if ($LASTEXITCODE) { throw 'Could not start the dist branch.' }
& git -C $workTree rm -rf --cached . 2>&1 | Out-Null
Get-ChildItem -LiteralPath $workTree -Force |
Where-Object { $_.Name -ne '.git' } |
Remove-Item -Recurse -Force
}
$targetBin = Join-Path $workTree 'bin'
if (Test-Path -LiteralPath $targetBin) {
Remove-Item -LiteralPath $targetBin -Recurse -Force
}
$null = New-Item -ItemType Directory -Path $targetBin -Force
Copy-Item -LiteralPath $ManifestPath -Destination $targetBin
foreach ($payload in $payloads) {
Copy-Item -LiteralPath $payload.FullName -Destination $targetBin
}
$readme = @"
# acdream alpha feed
Published by ``tools/publish-dist.ps1``. This branch carries ONLY the launcher
update feed it has no source history and is never merged into ``main``.
Current release: **$version**
## For players
1. Download ``bin/launcher-win-x64.zip``.
2. Unzip it anywhere and run ``acdream-launcher.exe``.
3. The launcher installs the game client and keeps both up to date.
You need Asheron's Call's DAT files for first-run setup.
"@
[IO.File]::WriteAllText(
(Join-Path $workTree 'README.md'),
$readme,
[Text.UTF8Encoding]::new($false))
# bin/ is gitignored repo-wide (so main can never take the payloads by
# accident) — force-add it here, where it is the whole point of the branch.
& git -C $workTree add -f bin README.md
if ($LASTEXITCODE) { throw 'Could not stage the feed.' }
& git -C $workTree commit -q -m "release: acdream alpha $version" 2>&1 | Out-Null
if ($LASTEXITCODE) {
Write-Host 'Nothing changed since the last publish.' -ForegroundColor Yellow
}
& git -C $workTree push --force $Remote "${stagingBranch}:${branch}"
if ($LASTEXITCODE) { throw "Push to $Remote/$branch failed." }
}
finally {
if (Test-Path -LiteralPath $workTree) {
& git -C $RepoRoot worktree remove --force $workTree 2>&1 | Out-Null
if (Test-Path -LiteralPath $workTree) {
Remove-Item -LiteralPath $workTree -Recurse -Force -ErrorAction SilentlyContinue
}
}
& git -C $RepoRoot worktree prune 2>&1 | Out-Null
# The staging branch exists only to carry one publish to the remote.
& git -C $RepoRoot branch -D $stagingBranch 2>&1 | Out-Null
}
Write-Host ''
Write-Host "Published $version." -ForegroundColor Green
Write-Host 'Feed: https://git.snakedesert.se/erik/acdream/raw/branch/dist/bin/manifest.json'
Write-Host 'Players: https://git.snakedesert.se/erik/acdream/src/branch/dist'