<# .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'