diff --git a/scripts/release-linux-pack.mjs b/scripts/release-linux-pack.mjs index 762ce09..0de1d63 100644 --- a/scripts/release-linux-pack.mjs +++ b/scripts/release-linux-pack.mjs @@ -34,11 +34,16 @@ function normalizeLinuxReleaseNames() { for (const name of fs.readdirSync(releaseDir)) { if (!name.includes('x86_64')) continue; const from = path.join(releaseDir, name); - const to = path.join(releaseDir, name.replaceAll('x86_64', 'x64')); - if (from !== to && !fs.existsSync(to)) { - fs.renameSync(from, to); - console.log(`[pack:linux] renamed ${name} -> ${path.basename(to)}`); + const toName = name.replaceAll('x86_64', 'x64'); + const to = path.join(releaseDir, toName); + if (from === to) continue; + // Always replace stale x64 — otherwise feed points at an old AppImage. + if (fs.existsSync(to)) { + fs.rmSync(to, { force: true }); + console.log(`[pack:linux] removed stale ${toName}`); } + fs.renameSync(from, to); + console.log(`[pack:linux] renamed ${name} -> ${toName}`); } for (const ymlName of fs.readdirSync(releaseDir)) { diff --git a/scripts/ttrpg-release/prepare-release.ps1 b/scripts/ttrpg-release/prepare-release.ps1 index 80a9a30..2f7af9c 100644 --- a/scripts/ttrpg-release/prepare-release.ps1 +++ b/scripts/ttrpg-release/prepare-release.ps1 @@ -367,20 +367,37 @@ function Copy-ReleaseArtifacts { $copied = 0 foreach ($name in ($names | Sort-Object)) { + # Always publish Linux AppImage under feed/site name (x64), never leave x86_64 alias. + if ($name -match 'x86_64' -and $name -match '\.AppImage$') { + $canonical = $name -replace 'x86_64', 'x64' + if ($names.Contains($canonical)) { + continue + } + $name = $canonical + } + $src = Join-Path $BuildReleaseDir $name + $destName = $name if (-not (Test-Path -LiteralPath $src)) { if ($name -match 'x64' -and $name -notmatch 'x86_64') { $alt = $name -replace 'x64', 'x86_64' - $src = Join-Path $BuildReleaseDir $alt + $altPath = Join-Path $BuildReleaseDir $alt + if (Test-Path -LiteralPath $altPath) { + $src = $altPath + } } } if (-not (Test-Path -LiteralPath $src)) { Write-Host " [--] skip (not built): $name" -ForegroundColor Yellow continue } - $dest = Join-Path $TargetDir ([System.IO.Path]::GetFileName($src)) + $dest = Join-Path $TargetDir $destName Copy-Item -LiteralPath $src -Destination $dest -Force - Write-Ok "copied $([System.IO.Path]::GetFileName($src))" + if ([System.IO.Path]::GetFileName($src) -ne $destName) { + Write-Ok "copied $([System.IO.Path]::GetFileName($src)) -> $destName" + } else { + Write-Ok "copied $destName" + } $copied += 1 } diff --git a/scripts/ttrpg-release/publish.ps1 b/scripts/ttrpg-release/publish.ps1 index b867a72..3e32286 100644 --- a/scripts/ttrpg-release/publish.ps1 +++ b/scripts/ttrpg-release/publish.ps1 @@ -102,12 +102,36 @@ function Resolve-ReleaseFile([string]$name) { return $null } -function Add-FileToUploadSet { +# electron-builder may leave TTRPGPlayer-x86_64.AppImage while yml/site expect x64. +# Prefer the newer x86_64 build and force the feed name before upload. +function Normalize-LinuxAppImageNames { + $imgs = Get-ChildItem -LiteralPath $ReleaseDir -Filter 'TTRPGPlayer-*.AppImage' -File -ErrorAction SilentlyContinue + foreach ($img in $imgs) { + if ($img.Name -notmatch 'x86_64') { continue } + $toName = $img.Name -replace 'x86_64', 'x64' + $toPath = Join-Path $ReleaseDir $toName + if (Test-Path -LiteralPath $toPath) { + Remove-Item -LiteralPath $toPath -Force + Write-Warn "removed stale $toName (replaced by $($img.Name))" + } + Rename-Item -LiteralPath $img.FullName -NewName $toName + Write-Ok "normalized $($img.Name) -> $toName" + } +} + +function Add-Upload { param( - [System.Collections.Generic.HashSet[string]]$set, + [System.Collections.Generic.Dictionary[string, string]]$map, + [string]$remoteName, [System.IO.FileInfo]$file ) - [void]$set.Add($file.FullName) + if ($map.ContainsKey($remoteName)) { + $prev = $map[$remoteName] + if (-not $prev.Equals($file.FullName, [StringComparison]::OrdinalIgnoreCase)) { + Write-Warn "upload name collision for $remoteName: keeping $($file.Name)" + } + } + $map[$remoteName] = $file.FullName } Write-Title 'TTRPG Release Publisher' @@ -129,7 +153,8 @@ if (-not (Test-Path -LiteralPath $sshKey)) { $errors = [System.Collections.Generic.List[string]]::new() $warnings = [System.Collections.Generic.List[string]]::new() -$uploadFiles = [System.Collections.Generic.HashSet[string]]::new([StringComparer]::OrdinalIgnoreCase) +# remote file name -> local full path (scp must use the feed/site name, not disk alias) +$uploadMap = [System.Collections.Generic.Dictionary[string, string]]::new([StringComparer]::OrdinalIgnoreCase) Write-Title 'Windows (required)' $winYml = Join-Path $ReleaseDir 'latest.yml' @@ -137,14 +162,14 @@ if (-not (Test-Path -LiteralPath $winYml)) { $errors.Add('Missing latest.yml') } else { Write-Ok 'latest.yml' - [void]$uploadFiles.Add($winYml) + Add-Upload $uploadMap ([System.IO.Path]::GetFileName($winYml)) (Get-Item -LiteralPath $winYml) foreach ($name in (Get-YmlReferencedFiles $winYml)) { $file = Resolve-ReleaseFile $name if ($null -eq $file) { $errors.Add("Windows: missing file $name (from latest.yml)") } else { Write-Ok $file.Name - Add-FileToUploadSet $uploadFiles $file + Add-Upload $uploadMap $name $file } } $blockmap = Resolve-ReleaseFile 'TTRPGPlayer-Setup.exe.blockmap' @@ -152,29 +177,30 @@ if (-not (Test-Path -LiteralPath $winYml)) { $warnings.Add('Missing TTRPGPlayer-Setup.exe.blockmap (recommended)') } else { Write-Ok $blockmap.Name - Add-FileToUploadSet $uploadFiles $blockmap + Add-Upload $uploadMap 'TTRPGPlayer-Setup.exe.blockmap' $blockmap } } Write-Title 'Linux (if latest-linux*.yml present)' +Normalize-LinuxAppImageNames $linuxYmls = Get-ChildItem -LiteralPath $ReleaseDir -Filter 'latest-linux*.yml' -File -ErrorAction SilentlyContinue if ($linuxYmls.Count -eq 0) { Write-Warn 'No latest-linux*.yml - skipping Linux' } else { foreach ($yml in $linuxYmls) { Write-Ok $yml.Name - [void]$uploadFiles.Add($yml.FullName) + Add-Upload $uploadMap $yml.Name $yml foreach ($name in (Get-YmlReferencedFiles $yml.FullName)) { $file = Resolve-ReleaseFile $name if ($null -eq $file) { $errors.Add("Linux ($($yml.Name)): missing file $name") } else { if ($file.Name -ne $name) { - Write-Warn "$($yml.Name): yml expects $name, disk has $($file.Name) - will upload $($file.Name)" + Write-Warn "$($yml.Name): yml expects $name, disk has $($file.Name) - uploading as $name" } else { Write-Ok "$($yml.Name) -> $name" } - Add-FileToUploadSet $uploadFiles $file + Add-Upload $uploadMap $name $file } } } @@ -188,7 +214,7 @@ if ($SkipMac) { Write-Warn 'No latest-mac.yml - skipping macOS' } else { Write-Ok 'latest-mac.yml' - [void]$uploadFiles.Add($macYml) + Add-Upload $uploadMap ([System.IO.Path]::GetFileName($macYml)) (Get-Item -LiteralPath $macYml) $macVersion = Get-YmlVersion $macYml $winYml = Join-Path $ReleaseDir 'latest.yml' @@ -216,7 +242,7 @@ if ($SkipMac) { ) } else { Write-Ok "primary update: $($primaryFile.Name)" - Add-FileToUploadSet $uploadFiles $primaryFile + Add-Upload $uploadMap $primaryName $primaryFile } } @@ -226,7 +252,7 @@ if ($SkipMac) { $warnings.Add("macOS: optional file missing (not uploaded): $name") } else { Write-Ok "optional: $($file.Name)" - Add-FileToUploadSet $uploadFiles $file + Add-Upload $uploadMap $name $file } } } @@ -245,9 +271,14 @@ if ($errors.Count -gt 0) { } Write-Host '' -Write-Host "Files to upload: $($uploadFiles.Count)" -ForegroundColor Green -foreach ($path in ($uploadFiles | Sort-Object)) { - Write-Host " - $([System.IO.Path]::GetFileName($path))" +Write-Host "Files to upload: $($uploadMap.Count)" -ForegroundColor Green +foreach ($remoteName in ($uploadMap.Keys | Sort-Object)) { + $localName = [System.IO.Path]::GetFileName($uploadMap[$remoteName]) + if ($localName -eq $remoteName) { + Write-Host " - $remoteName" + } else { + Write-Host " - $remoteName (from $localName)" + } } if ($CheckOnly) { @@ -260,12 +291,12 @@ Write-Title 'Upload' Write-Host "Target: ${sshTarget}:${remoteDir}" Write-Host "Feed: $feedUrl" -foreach ($path in ($uploadFiles | Sort-Object)) { - $name = [System.IO.Path]::GetFileName($path) - Write-Host " -> $name" - & scp -i $sshKey -q $path "${sshTarget}:${remoteDir}/" +foreach ($remoteName in ($uploadMap.Keys | Sort-Object)) { + $localPath = $uploadMap[$remoteName] + Write-Host " -> $remoteName" + & scp -i $sshKey -q $localPath "${sshTarget}:${remoteDir}/$remoteName" if ($LASTEXITCODE -ne 0) { - throw "scp failed for $name (exit $LASTEXITCODE)" + throw "scp failed for $remoteName (exit $LASTEXITCODE)" } }