Windows PowerShell (.NET Framework) writes form-data names without quotes and a quoted
boundary, which the keymaster's parser rejected ("Send multipart/form-data"). Saved with
BOM so PowerShell 5.1 reads the umlauts correctly.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
89 lines
4.6 KiB
PowerShell
89 lines
4.6 KiB
PowerShell
# Neue Version automatisch auf den Keymaster hochladen (msco.mbm.wtf -> /download, Launcher-Updates).
|
|
# Läuft nach dem Release-Build von selbst (MSCO.csproj bzw. MSCO.Launcher.csproj) - oder von Hand:
|
|
# powershell -ExecutionPolicy Bypass -File release.ps1 -Product mod -File bin\Release\MSCO.dll
|
|
#
|
|
# Braucht den Release-Token des Keymasters (RELEASE_TOKEN in dessen .env), lokal als Umgebungsvariable
|
|
# MSCO_RELEASE_TOKEN oder in %AppData%\MSCO\release-token.txt. Ohne Token wird nichts hochgeladen.
|
|
# Gibt es die Version auf dem Keymaster schon, passiert nichts - also einfach die Version erhöhen und bauen.
|
|
param(
|
|
[Parameter(Mandatory = $true)][ValidateSet("mod", "launcher", "installer")][string]$Product,
|
|
[Parameter(Mandatory = $true)][string]$File,
|
|
[string]$Version = "",
|
|
[string]$Changelog = ""
|
|
)
|
|
$ErrorActionPreference = "Stop"
|
|
$here = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
|
|
$token = $env:MSCO_RELEASE_TOKEN
|
|
$tokenFile = Join-Path $env:APPDATA "MSCO\release-token.txt"
|
|
if (-not $token -and (Test-Path $tokenFile)) { $token = (Get-Content $tokenFile -Raw).Trim() }
|
|
if (-not $token) {
|
|
Write-Host "MSCO release: no release token (MSCO_RELEASE_TOKEN or $tokenFile) - not uploading."
|
|
exit 0
|
|
}
|
|
$keymaster = if ($env:MSCO_KEYMASTER) { $env:MSCO_KEYMASTER.TrimEnd("/") } else { "https://msco.mbm.wtf" }
|
|
|
|
if (-not (Test-Path $File)) { Write-Host "MSCO release: $File not found"; exit 1 }
|
|
|
|
# Version: aus MSCO.cs (Mod) bzw. aus der Datei (Produktversion)
|
|
if (-not $Version) {
|
|
$source = Join-Path $here "MSCO.cs"
|
|
if ($Product -eq "mod" -and (Test-Path $source)) {
|
|
$m = Select-String -Path $source -Pattern 'ModVersion\s*=\s*"([^"]+)"' | Select-Object -First 1
|
|
if ($m) { $Version = $m.Matches[0].Groups[1].Value }
|
|
}
|
|
if (-not $Version) { $Version = ((Get-Item $File).VersionInfo.ProductVersion -split '\+')[0] }
|
|
}
|
|
|
|
# Changelog: Abschnitt "## <Version>" aus CHANGELOG.md (Text ohne Markdown, gekürzt)
|
|
if (-not $Changelog) {
|
|
$log = Join-Path $here "CHANGELOG.md"
|
|
if (Test-Path $log) {
|
|
$lines = Get-Content $log -Encoding UTF8
|
|
$start = [Array]::FindIndex($lines, [Predicate[string]] { param($l) $l -match "^##\s+$([regex]::Escape($Version))\b" })
|
|
if ($start -ge 0) {
|
|
$text = @()
|
|
for ($i = $start + 1; $i -lt $lines.Count -and $lines[$i] -notmatch '^##\s'; $i++) {
|
|
$l = $lines[$i].Trim() -replace '\*\*', '' -replace '`', ''
|
|
if ($l) { $text += $l }
|
|
}
|
|
$Changelog = ($text -join " ")
|
|
if ($Changelog.Length -gt 3500) { $Changelog = $Changelog.Substring(0, 3500) + " …" }
|
|
}
|
|
}
|
|
}
|
|
|
|
Add-Type -AssemblyName System.Net.Http
|
|
$client = New-Object System.Net.Http.HttpClient
|
|
$client.Timeout = [TimeSpan]::FromMinutes(10)
|
|
$client.DefaultRequestHeaders.Authorization = New-Object System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", $token)
|
|
# Multipart von Hand: Windows PowerShell (.NET Framework) schreibt name=... ohne Anführungszeichen und die Boundary in
|
|
# Anführungszeichen - das lehnt der Parser des Keymasters (Node/undici) ab.
|
|
$boundary = "----MSCO" + [Guid]::NewGuid().ToString("N")
|
|
$utf8 = New-Object System.Text.UTF8Encoding($false)
|
|
$body = New-Object System.IO.MemoryStream
|
|
function Write-Text([string]$text) { $b = $utf8.GetBytes($text); $body.Write($b, 0, $b.Length) }
|
|
foreach ($field in @(@("product", $Product), @("version", $Version), @("changelog", $Changelog))) {
|
|
Write-Text "--$boundary`r`nContent-Disposition: form-data; name=`"$($field[0])`"`r`nContent-Type: text/plain; charset=utf-8`r`n`r`n$($field[1])`r`n"
|
|
}
|
|
$bytes = [System.IO.File]::ReadAllBytes((Resolve-Path $File))
|
|
Write-Text "--$boundary`r`nContent-Disposition: form-data; name=`"file`"; filename=`"$([System.IO.Path]::GetFileName($File))`"`r`nContent-Type: application/octet-stream`r`n`r`n"
|
|
$body.Write($bytes, 0, $bytes.Length)
|
|
Write-Text "`r`n--$boundary--`r`n"
|
|
$form = New-Object System.Net.Http.ByteArrayContent(,$body.ToArray())
|
|
$form.Headers.TryAddWithoutValidation("Content-Type", "multipart/form-data; boundary=$boundary") | Out-Null
|
|
|
|
try {
|
|
$res = $client.PostAsync("$keymaster/api/releases/upload", $form).GetAwaiter().GetResult()
|
|
$body = $res.Content.ReadAsStringAsync().GetAwaiter().GetResult()
|
|
} catch {
|
|
Write-Host "MSCO release: keymaster not reachable ($($_.Exception.Message)) - not uploaded"
|
|
exit 0
|
|
}
|
|
switch ([int]$res.StatusCode) {
|
|
200 { Write-Host "MSCO release: $Product $Version uploaded to $keymaster" }
|
|
409 { Write-Host "MSCO release: $Product $Version is already on the keymaster - raise the version to publish a new one" }
|
|
default { Write-Host "MSCO release: upload failed (HTTP $([int]$res.StatusCode)): $body" }
|
|
}
|
|
exit 0
|