# Signiert ein Release und veroeffentlicht es als Gitea-Release auf git.mbm.wtf (Launcher und Website holen es von dort). # .\release.ps1 -Product mod|launcher -Version 0.5.0 -File dist\BSO-Mod.zip [-Changelog "..."] # Laeuft auch automatisch bei "dotnet publish -p:BsoRelease=true" (Launcher) bzw. "build.ps1 -Release" (Mod). # - Repo: mod -> MrMaho/BSO-Mod, launcher -> MrMaho/BSO-Launcher; Tag v (wird auf dem Standard-Branch angelegt). # - Ohne -Changelog: der Abschnitt "## " aus CHANGELOG.md neben diesem Skript. # - Dateien im Release: die Datei (Launcher als BSO-Launcher-Setup.exe) und bso-release.json mit Version, SHA-256 und # Signatur. Signiert wird "BSO-RELEASE|produkt|version|sha256" - der Launcher prueft das mit dem eingebauten # oeffentlichen Schluessel, egal woher die Datei kommt. # Braucht: Release-Schluessel (BSO-Launcher\new-release-key.ps1) und BSO_GITEA_TOKEN (Gitea: Einstellungen -> Anwendungen # -> Token mit "repository: Lesen und Schreiben"), auch als Windows-Benutzervariable. Optional BSO_GITEA_URL. param( [Parameter(Mandatory)][ValidateSet("mod", "launcher")][string]$Product, [Parameter(Mandatory)][string]$Version, [Parameter(Mandatory)][string]$File, [string]$Changelog = "" ) $ErrorActionPreference = "Stop" Add-Type -AssemblyName System.Security Add-Type -AssemblyName System.Net.Http # Aus der Umgebung - sonst die Windows-Benutzervariable (z.B. wenn Visual Studio vor dem Setzen gestartet wurde) function Setting([string]$name) { $v = [Environment]::GetEnvironmentVariable($name) if (-not $v) { $v = [Environment]::GetEnvironmentVariable($name, "User") } return $v } $gitea = ((Setting "BSO_GITEA_URL"), "https://git.mbm.wtf" | Where-Object { $_ } | Select-Object -First 1).TrimEnd('/') $token = Setting "BSO_GITEA_TOKEN" if (-not $token) { Write-Host "BSO_GITEA_TOKEN nicht gesetzt - kein Release angelegt"; return } $keyFile = Join-Path $env:APPDATA "BSO\release-key.dpapi" if (-not (Test-Path $keyFile)) { throw "Kein Release-Schluessel ($keyFile) - erst new-release-key.ps1 im BSO-Launcher-Repo" } $Version = $Version.Split('+')[0] $repo = @{ mod = "BSO-Mod"; launcher = "BSO-Launcher" }[$Product] $api = "$gitea/api/v1/repos/MrMaho/$repo" # Changelog aus CHANGELOG.md: alles unter "## " bis zur naechsten "## "-Zeile if (-not $Changelog) { $md = Join-Path $PSScriptRoot "CHANGELOG.md" if (Test-Path $md) { $lines = [IO.File]::ReadAllLines($md) $start = [Array]::FindIndex($lines, [Predicate[string]] { param($l) $l -match "^##\s+v?$([regex]::Escape($Version))(\s|$)" }) if ($start -ge 0) { $section = @() for ($i = $start + 1; $i -lt $lines.Length -and $lines[$i] -notmatch '^##\s'; $i++) { $section += $lines[$i] } $Changelog = ($section -join "`n").Trim() } } if ($Changelog) { Write-Host "Changelog aus CHANGELOG.md ($Version)" } else { Write-Host "Kein Changelog fuer $Version in CHANGELOG.md" } } $path = (Resolve-Path $File).Path $fileName = if ($Product -eq "launcher") { "BSO-Launcher-Setup.exe" } else { Split-Path $path -Leaf } $data = [IO.File]::ReadAllBytes($path) $sha = ([System.Security.Cryptography.SHA256]::Create().ComputeHash($data) | ForEach-Object { $_.ToString("x2") }) -join "" $private = [System.Security.Cryptography.ProtectedData]::Unprotect([IO.File]::ReadAllBytes($keyFile), $null, [System.Security.Cryptography.DataProtectionScope]::CurrentUser) $rsa = [System.Security.Cryptography.RSA]::Create() $rsa.FromXmlString([Text.Encoding]::UTF8.GetString($private)) $message = [Text.Encoding]::UTF8.GetBytes("BSO-RELEASE|$Product|$Version|$sha") $signature = [Convert]::ToBase64String($rsa.SignData($message, [System.Security.Cryptography.HashAlgorithmName]::SHA256, [System.Security.Cryptography.RSASignaturePadding]::Pkcs1)) $manifest = [ordered]@{ product = $Product; version = $Version; sha256 = $sha; size = $data.Length; file = $fileName; signature = $signature } | ConvertTo-Json -Compress $http = New-Object System.Net.Http.HttpClient $http.Timeout = [TimeSpan]::FromMinutes(10) $http.DefaultRequestHeaders.Authorization = New-Object System.Net.Http.Headers.AuthenticationHeaderValue("token", $token) # Release anlegen (Tag v) $json = @{ tag_name = "v$Version"; name = "$Product $Version"; body = $Changelog; draft = $false; prerelease = $false } | ConvertTo-Json -Compress $res = $http.PostAsync("$api/releases", (New-Object System.Net.Http.StringContent($json, [Text.Encoding]::UTF8, "application/json"))).Result $body = $res.Content.ReadAsStringAsync().Result if ($res.StatusCode -eq [Net.HttpStatusCode]::Conflict) { Write-Host "$repo v$Version gibt es schon - Version erhoehen"; return } if (-not $res.IsSuccessStatusCode) { throw "Release anlegen fehlgeschlagen ($([int]$res.StatusCode)): $body" } $id = ($body | ConvertFrom-Json).id function Upload([string]$name, [byte[]]$bytes) { $form = New-Object System.Net.Http.MultipartFormDataContent $form.Add((New-Object System.Net.Http.ByteArrayContent(, $bytes)), "`"attachment`"", "`"$name`"") $r = $http.PostAsync("$api/releases/$id/assets?name=$([Uri]::EscapeDataString($name))", $form).Result if (-not $r.IsSuccessStatusCode) { throw "Hochladen von $name fehlgeschlagen ($([int]$r.StatusCode)): $($r.Content.ReadAsStringAsync().Result)" } } Upload $fileName $data Upload "bso-release.json" ([Text.Encoding]::UTF8.GetBytes($manifest)) Write-Host "$Product $Version veroeffentlicht: $gitea/MrMaho/$repo/releases/tag/v$Version (sha256 $sha)"