fe6427bfba
Secret-Scan im Fable-5-Review fand vier aktive Zugangsdaten im oeffentlich erreichbaren Gitea-Repo. Gefahrlos entfernbare Stellen: - scripts/release.ps1: Gitea-Token -> Umgebungsvariable PYRAMID_GITEA_TOKEN - docs/matrix-sdk/13-server-admin: 19x Admin-Token -> <ADMIN_TOKEN> Client-eingebettete Secrets (LiveKit apiSecret, Admin-Token im E2EE-Diagnose-Upload, Giphy-Key) brauchen Rotation + Server-Umbau und stehen als neue M0-ROADMAP-Punkte (nicht blind auf dem Pi fixbar). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
212 lines
10 KiB
PowerShell
212 lines
10 KiB
PowerShell
# Pyramid Release Script
|
||
# Usage: .\scripts\release.ps1
|
||
# Builds Windows + Android, creates Gitea release, uploads artifacts.
|
||
|
||
Set-StrictMode -Version Latest
|
||
$ErrorActionPreference = "Stop"
|
||
|
||
# Always run from the project root, regardless of where the script was called from.
|
||
Set-Location (Split-Path $PSScriptRoot -Parent)
|
||
|
||
# ── Config ────────────────────────────────────────────────────────────────────
|
||
$GiteaBase = "http://192.168.178.71:3000"
|
||
$GiteaRepo = "steggi/pyramid"
|
||
# Token NIE hier eintragen (CLAUDE.md: keine Secrets ins Repo). Er kommt aus der
|
||
# Umgebungsvariable PYRAMID_GITEA_TOKEN – einmalig pro PC setzen mit:
|
||
# [Environment]::SetEnvironmentVariable("PYRAMID_GITEA_TOKEN", "<token>", "User")
|
||
# (neues Terminal öffnen, damit die Variable sichtbar ist)
|
||
$GiteaToken = $env:PYRAMID_GITEA_TOKEN
|
||
if ([string]::IsNullOrWhiteSpace($GiteaToken)) {
|
||
Write-Error ("PYRAMID_GITEA_TOKEN ist nicht gesetzt. Einmalig setzen mit:`n" +
|
||
' [Environment]::SetEnvironmentVariable("PYRAMID_GITEA_TOKEN", "<token>", "User")' +
|
||
"`nDen Token in Gitea unter Einstellungen > Anwendungen erzeugen (Scope: repository).")
|
||
exit 1
|
||
}
|
||
$Headers = @{ Authorization = "token $GiteaToken"; "Content-Type" = "application/json" }
|
||
|
||
# ── Read and Update version from pubspec.yaml ─────────────────────────────────
|
||
$pubspecPath = "pubspec.yaml"
|
||
$pubspecLines = @(Get-Content $pubspecPath)
|
||
$versionIndex = -1
|
||
for ($i = 0; $i -lt $pubspecLines.Count; $i++) {
|
||
if ($pubspecLines[$i] -match "^version:") {
|
||
$versionIndex = $i
|
||
break
|
||
}
|
||
}
|
||
if ($versionIndex -lt 0) { Write-Error "version not found in pubspec.yaml"; exit 1 }
|
||
|
||
$currentVersionFull = ($pubspecLines[$versionIndex] -split ":")[1].Trim()
|
||
$currentVersion = $currentVersionFull.Split("+")[0]
|
||
$currentBuild = 0
|
||
if ($currentVersionFull -match "\+") {
|
||
$currentBuild = [int]$currentVersionFull.Split("+")[1]
|
||
}
|
||
|
||
$vParts = $currentVersion.Split(".")
|
||
$major = [int]$vParts[0]
|
||
$minor = [int]$vParts[1]
|
||
$patch = [int]$vParts[2]
|
||
$nextPatch = "$major.$minor.$($patch + 1)"
|
||
$nextBuild = $currentBuild + 1
|
||
|
||
Write-Host ""
|
||
Write-Host " Aktuelle Version : $currentVersion (Build $currentBuild)" -ForegroundColor Cyan
|
||
$newVersionInput = Read-Host " Neue Version eingeben (Leer lassen für $nextPatch)"
|
||
if ([string]::IsNullOrWhiteSpace($newVersionInput)) {
|
||
$newVersion = $nextPatch
|
||
} else {
|
||
$newVersion = $newVersionInput.Trim().TrimStart("v")
|
||
}
|
||
|
||
$newVersionFull = "$newVersion+$nextBuild"
|
||
if ($newVersionFull -ne $currentVersionFull) {
|
||
$pubspecLines[$versionIndex] = "version: $newVersionFull"
|
||
Set-Content -Path $pubspecPath -Value $pubspecLines
|
||
Write-Host " pubspec.yaml auf $newVersionFull aktualisiert." -ForegroundColor Green
|
||
}
|
||
|
||
$version = $newVersion
|
||
$tag = "v$version"
|
||
|
||
Write-Host ""
|
||
Write-Host " Pyramid Release Builder" -ForegroundColor Cyan
|
||
Write-Host " Version : $tag" -ForegroundColor White
|
||
Write-Host ""
|
||
|
||
# ── Check for existing release ────────────────────────────────────────────────
|
||
try {
|
||
$existing = Invoke-RestMethod "$GiteaBase/api/v1/repos/$GiteaRepo/releases/tags/$tag" `
|
||
-Headers $Headers -Method Get -ErrorAction Stop
|
||
Write-Host " [!] Release $tag existiert bereits auf Gitea." -ForegroundColor Yellow
|
||
$overwrite = Read-Host " Überschreiben? (j/N)"
|
||
if ($overwrite -notmatch "^[jJ]$") { Write-Host " Abgebrochen."; exit 0 }
|
||
# Delete existing release
|
||
Invoke-RestMethod "$GiteaBase/api/v1/repos/$GiteaRepo/releases/$($existing.id)" `
|
||
-Headers $Headers -Method Delete | Out-Null
|
||
Write-Host " Alter Release gelöscht." -ForegroundColor Gray
|
||
} catch {
|
||
# 404 = release doesn't exist, that's fine
|
||
}
|
||
|
||
# ── Release notes ─────────────────────────────────────────────────────────────
|
||
Write-Host " Release-Notizen (leer lassen = Changelog aus Git-Commits):" -ForegroundColor Cyan
|
||
$notes = Read-Host " > "
|
||
if ([string]::IsNullOrWhiteSpace($notes)) {
|
||
# Auto-generate from git log since last tag.
|
||
# git describe exits non-zero when no tags exist; catch the error explicitly
|
||
# because $ErrorActionPreference = "Stop" would otherwise abort the script.
|
||
$lastTag = $null
|
||
try { $lastTag = git describe --tags --abbrev=0 HEAD^ 2>$null } catch {}
|
||
if ($lastTag) {
|
||
$notes = git log "$lastTag..HEAD" --pretty=format:"- %s" 2>$null | Out-String
|
||
} else {
|
||
$notes = git log --pretty=format:"- %s" -20 2>$null | Out-String
|
||
}
|
||
Write-Host " (Commits als Release-Notizen verwendet)" -ForegroundColor Gray
|
||
}
|
||
|
||
# ── Build Windows ─────────────────────────────────────────────────────────────
|
||
Write-Host ""
|
||
Write-Host " [1/4] Windows Build..." -ForegroundColor Cyan
|
||
flutter build windows --release
|
||
if ($LASTEXITCODE -ne 0) { Write-Error "Windows build fehlgeschlagen"; exit 1 }
|
||
|
||
$winSrc = "build\windows\x64\runner\Release"
|
||
$winZip = "build\pyramid-windows-$tag.zip"
|
||
if (Test-Path $winZip) { Remove-Item $winZip -Force }
|
||
Compress-Archive -Path "$winSrc\*" -DestinationPath $winZip
|
||
Write-Host " Windows ZIP: $winZip" -ForegroundColor Green
|
||
|
||
# ── Build Android ─────────────────────────────────────────────────────────────
|
||
Write-Host ""
|
||
Write-Host " [2/4] Android Build (split per ABI)..." -ForegroundColor Cyan
|
||
flutter build apk --release --split-per-abi
|
||
if ($LASTEXITCODE -ne 0) { Write-Error "Android build fehlgeschlagen"; exit 1 }
|
||
|
||
# Order matters: arm64-v8a is uploaded first so app versions that still pick
|
||
# the first APK asset (pre-ABI-aware updater) get the variant that fits
|
||
# practically every real device.
|
||
$abiBuilds = @(
|
||
@{ Abi = "arm64-v8a"; Desc = "64-bit ARM — praktisch jedes Smartphone der letzten ~8 Jahre. Im Zweifel diese Datei." },
|
||
@{ Abi = "armeabi-v7a"; Desc = "32-bit ARM — sehr alte Geräte" },
|
||
@{ Abi = "x86_64"; Desc = "Emulatoren / x86-Geräte" }
|
||
)
|
||
$apkFiles = @()
|
||
foreach ($b in $abiBuilds) {
|
||
$abi = $b.Abi
|
||
$src = "build\app\outputs\flutter-apk\app-$abi-release.apk"
|
||
if (-not (Test-Path $src)) { Write-Error "APK fehlt: $src"; exit 1 }
|
||
$dst = "build\pyramid-android-$abi-$tag.apk"
|
||
Copy-Item $src $dst -Force
|
||
$apkFiles += @{ Path = $dst; Name = "pyramid-android-$abi-$tag.apk"; Desc = $b.Desc }
|
||
$sizeMb = [math]::Round((Get-Item $dst).Length / 1MB, 1)
|
||
Write-Host " Android APK ($abi, $sizeMb MB): $dst" -ForegroundColor Green
|
||
}
|
||
|
||
# Append a download guide to the release notes so every asset is explained.
|
||
$downloadGuide = "`n`n## Downloads`n`n| Datei | Für welches Gerät? |`n|---|---|`n"
|
||
foreach ($f in $apkFiles) {
|
||
$downloadGuide += "| ``$($f.Name)`` | $($f.Desc) |`n"
|
||
}
|
||
$downloadGuide += "| ``pyramid-windows-$tag.zip`` | Windows 10/11 |`n"
|
||
$downloadGuide += "`n*Die App lädt beim automatischen Update selbst die passende Variante.*"
|
||
$notes = $notes.Trim() + $downloadGuide
|
||
|
||
# ── Git tag ───────────────────────────────────────────────────────────────────
|
||
Write-Host ""
|
||
Write-Host " [3/4] Git Tag $tag..." -ForegroundColor Cyan
|
||
git tag -f $tag
|
||
# Token in die Push-URL einbetten, damit keine interaktive Passwort-Abfrage nötig ist
|
||
$pushUrl = $GiteaBase -replace "http://", "http://steggi:$GiteaToken@"
|
||
& git push "$pushUrl/steggi/pyramid.git" $tag --force
|
||
if ($LASTEXITCODE -ne 0) {
|
||
Write-Host " [!] Tag-Push fehlgeschlagen (Release wird trotzdem erstellt)." -ForegroundColor Yellow
|
||
} else {
|
||
Write-Host " Tag gepusht." -ForegroundColor Green
|
||
}
|
||
|
||
# ── Create Gitea release ──────────────────────────────────────────────────────
|
||
Write-Host ""
|
||
Write-Host " [4/4] Gitea Release erstellen..." -ForegroundColor Cyan
|
||
|
||
$body = @{
|
||
tag_name = $tag
|
||
name = "Pyramid $tag"
|
||
body = $notes.Trim()
|
||
draft = $false
|
||
prerelease = $false
|
||
target_commitish = "master"
|
||
} | ConvertTo-Json -Compress
|
||
|
||
$release = Invoke-RestMethod "$GiteaBase/api/v1/repos/$GiteaRepo/releases" `
|
||
-Headers $Headers -Method Post -Body $body
|
||
$releaseId = $release.id
|
||
Write-Host " Release ID: $releaseId" -ForegroundColor Gray
|
||
|
||
# ── Upload artifacts ──────────────────────────────────────────────────────────
|
||
function Upload-Asset($filePath, $fileName) {
|
||
# curl.exe (built into Windows 10+) handles large binary uploads reliably.
|
||
$uri = "$GiteaBase/api/v1/repos/$GiteaRepo/releases/$releaseId/assets?name=$fileName"
|
||
$result = & curl.exe -s --fail -w "%{http_code}" `
|
||
-X POST $uri `
|
||
-H "Authorization: token $GiteaToken" `
|
||
-F "attachment=@`"$filePath`""
|
||
if ($LASTEXITCODE -ne 0) {
|
||
Write-Error "Upload fehlgeschlagen ($result): $fileName"
|
||
}
|
||
Write-Host " Hochgeladen: $fileName" -ForegroundColor Green
|
||
}
|
||
|
||
# APKs first (arm64-v8a leads — see comment at $abiBuilds), then Windows.
|
||
foreach ($f in $apkFiles) {
|
||
Upload-Asset $f.Path $f.Name
|
||
}
|
||
Upload-Asset $winZip "pyramid-windows-$tag.zip"
|
||
|
||
# ── Done ──────────────────────────────────────────────────────────────────────
|
||
Write-Host ""
|
||
Write-Host " Release $tag erfolgreich erstellt!" -ForegroundColor Green
|
||
Write-Host " $GiteaBase/$GiteaRepo/releases/tag/$tag" -ForegroundColor Cyan
|
||
Write-Host ""
|