Files
aza/AzA march 2026/backup_finish_aza_rc_20260610_072458/publish_update.ps1
2026-06-10 22:55:03 +02:00

268 lines
10 KiB
PowerShell

# publish_update.ps1 - Upload Installer + Manifest nach Hetzner
# Laedt in release/ UND release/downloads/ hoch (beide Pfade).
# Aktualisiert zusaetzlich das Stable-Manifest fuer den Auto-Updater.
#
# ROOT CAUSE v1.3.0 (2026-05-31):
# Der oeffentliche Installer war korrekt, aber das Stable-Manifest unter
# /root/aza-app/release/downloads/updates/manifest.json zeigte noch die
# alte Version, weil dieses Skript das Manifest dort nicht mitaktualisiert
# hatte. Der Auto-Updater las das Stable-Manifest und bot kein Update an.
# Fix: [5/7] aktualisiert das Stable-Manifest zwingend bei jedem Release.
$ErrorActionPreference = "Stop"
$projectRoot = $PSScriptRoot
$remoteHost = "root@178.104.51.177"
$remotePath = "/root/aza-app/release"
$remoteDownloadsPath = "/root/aza-app/release/downloads"
$remoteStableManifest = "/root/aza-app/release/downloads/updates/manifest.json"
$publicInstallerUrl = "https://api.aza-medwork.ch/downloads/aza_desktop_setup.exe"
$publicStableManifestUrl = "https://api.aza-medwork.ch/downloads/updates/manifest.json"
$installerPath = Join-Path $projectRoot "dist\installer\aza_desktop_setup.exe"
$manifestPath = Join-Path $projectRoot "release\version.json"
Write-Host ""
Write-Host "==================================================="
Write-Host " AZA Release Upload + Verifikation"
Write-Host "==================================================="
Write-Host ""
# --- [1/7] Lokale Dateien + Version lesen ---
Write-Host "[1/7] Lokale Dateien pruefen + Version lesen..."
if (-not (Test-Path $installerPath)) {
Write-Error "ABBRUCH: $installerPath nicht gefunden."
exit 1
}
if (-not (Test-Path $manifestPath)) {
Write-Error "ABBRUCH: $manifestPath nicht gefunden."
exit 1
}
if (-not (Test-Path (Join-Path $projectRoot "aza_version.py"))) {
Write-Error "ABBRUCH: aza_version.py nicht gefunden."
exit 1
}
# Version + Build aus aza_version.py und _build_info.py lesen
$versionContent = Get-Content (Join-Path $projectRoot "aza_version.py") -Raw
if ($versionContent -match 'APP_VERSION\s*=\s*"([^"]+)"') {
$appVersion = $matches[1].Trim()
} else {
Write-Error "ABBRUCH: APP_VERSION nicht in aza_version.py."
exit 1
}
if ($versionContent -match 'APP_CHANNEL\s*=\s*"([^"]+)"') {
$appChannel = $matches[1].Trim()
} else {
$appChannel = "stable"
}
$buildStamp = ""
$buildInfoPath = Join-Path $projectRoot "_build_info.py"
if (Test-Path $buildInfoPath) {
$buildContent = Get-Content $buildInfoPath -Raw
if ($buildContent -match 'BUILD_TIMESTAMP\s*=\s*"([^"]+)"') {
$buildStamp = $matches[1].Trim()
}
}
if (-not $buildStamp) {
$buildStamp = (Get-Item $installerPath).LastWriteTime.ToString("yyyyMMdd_HHmmss")
}
# Lokaler Installer-Hash + Groesse (einmalig berechnen)
$localHash = (Get-FileHash -Path $installerPath -Algorithm SHA256).Hash.ToLower()
$localSizeBytes = (Get-Item $installerPath).Length
$localSizeMB = [math]::Round($localSizeBytes / 1MB, 2)
Write-Host " Installer: $installerPath ($localSizeMB MB)"
Write-Host " Version: $appVersion"
Write-Host " Build: $buildStamp"
Write-Host " Channel: $appChannel"
Write-Host " SHA256: $localHash"
# --- [2/7] Installer hochladen ---
Write-Host ""
Write-Host "[2/7] Installer hochladen (release/ und release/downloads/)..."
scp "$installerPath" "${remoteHost}:${remotePath}/aza_desktop_setup.exe"
if ($LASTEXITCODE -ne 0) {
Write-Error "ABBRUCH: Installer-Upload nach release/ fehlgeschlagen."
exit 1
}
Write-Host " release/ hochgeladen"
ssh $remoteHost "cp ${remotePath}/aza_desktop_setup.exe ${remoteDownloadsPath}/aza_desktop_setup.exe"
if ($LASTEXITCODE -ne 0) {
Write-Error "ABBRUCH: Kopieren nach release/downloads/ fehlgeschlagen."
exit 1
}
Write-Host " release/downloads/ kopiert"
# --- [3/7] version.json hochladen ---
Write-Host ""
Write-Host "[3/7] release/version.json hochladen..."
scp "$manifestPath" "${remoteHost}:${remotePath}/version.json"
if ($LASTEXITCODE -ne 0) {
Write-Error "ABBRUCH: version.json-Upload fehlgeschlagen."
exit 1
}
Write-Host " version.json OK"
# --- [4/7] Server-Installer SHA256 pruefen ---
Write-Host ""
Write-Host "[4/7] Server-SHA256-Abgleich (release/ vs. downloads/)..."
$hashOutput = ssh $remoteHost "sha256sum ${remotePath}/aza_desktop_setup.exe ${remoteDownloadsPath}/aza_desktop_setup.exe 2>/dev/null"
Write-Host $hashOutput
$hashes = ($hashOutput -split "`n") | Where-Object { $_ -match "^[0-9a-f]{64}" } | ForEach-Object { ($_ -split "\s+")[0] }
if ($hashes.Count -lt 2) {
Write-Error "ABBRUCH: SHA256 konnte nicht fuer beide Server-Pfade gelesen werden."
exit 1
}
if ($hashes[0] -ne $hashes[1]) {
Write-Error "ABBRUCH: SHA256 release/ != release/downloads/ -- Installer-Pfade sind inkonsistent!"
exit 1
}
if ($hashes[0].ToLower() -ne $localHash) {
Write-Error "ABBRUCH: Server-SHA256 ($($hashes[0])) != lokaler SHA256 ($localHash) -- Upload korrumpiert!"
exit 1
}
Write-Host " SHA256 identisch: release/ = downloads/ = lokal OK"
# --- [5/7] Stable-Manifest aktualisieren ---
# PFLICHTSCHRITT -- verhindert Wiederholung des v1.3.0-Fehlers.
# Das Stable-Manifest liegt in einem anderen Pfad als version.json:
# /root/aza-app/release/downloads/updates/manifest.json
# Es wird bei jedem Release zwingend auf die neue Version gesetzt.
Write-Host ""
Write-Host "[5/7] Stable-Manifest aktualisieren ($remoteStableManifest)..."
$stableManifestContent = @"
{
"product": "AZA Desktop",
"channel": "$appChannel",
"latest_version": "$appVersion",
"latest_build": "$buildStamp",
"min_supported_version": "1.2.0",
"mandatory": false,
"release_date": "$(Get-Date -Format 'yyyy-MM-dd')",
"notes_de": [
"AZA $appVersion"
],
"files": [
{
"name": "aza_desktop_setup.exe",
"url": "$publicInstallerUrl",
"sha256": "$localHash",
"size_bytes": $localSizeBytes
}
]
}
"@
# Temporaere Datei lokal schreiben, dann hochladen
$tmpManifest = Join-Path $env:TEMP "aza_stable_manifest_tmp.json"
# UTF8 ohne BOM (Out-File -Encoding utf8 erzeugt auf Windows einen BOM)
[System.IO.File]::WriteAllText($tmpManifest, $stableManifestContent, [System.Text.UTF8Encoding]::new($false))
scp "$tmpManifest" "${remoteHost}:${remoteStableManifest}"
if ($LASTEXITCODE -ne 0) {
Remove-Item $tmpManifest -ErrorAction SilentlyContinue
Write-Error "ABBRUCH: Stable-Manifest-Upload fehlgeschlagen."
exit 1
}
Remove-Item $tmpManifest -ErrorAction SilentlyContinue
Write-Host " Stable-Manifest hochgeladen"
# Gegenpruefen: Manifest-SHA256 auf Server muss mit lokalem Hash uebereinstimmen
$manifestHashCheck = ssh $remoteHost "python3 -c `"import json; d=json.load(open('$remoteStableManifest')); print(d['files'][0]['sha256'])`""
$manifestHashCheck = ($manifestHashCheck -split "`n")[0].Trim().ToLower()
if ($manifestHashCheck -ne $localHash) {
Write-Error "ABBRUCH: Manifest SHA256 ($manifestHashCheck) != lokaler Installer SHA256 ($localHash)!"
exit 1
}
Write-Host " Manifest-SHA256 stimmt mit Installer ueberein"
$manifestVersionCheck = ssh $remoteHost "python3 -c `"import json; d=json.load(open('$remoteStableManifest')); print(d['latest_version'])`""
$manifestVersionCheck = ($manifestVersionCheck -split "`n")[0].Trim()
if ($manifestVersionCheck -ne $appVersion) {
Write-Error "ABBRUCH: Manifest version ($manifestVersionCheck) != Release-Version ($appVersion)!"
exit 1
}
Write-Host " Manifest-Version korrekt: $manifestVersionCheck"
# --- [6/7] Public-URL-Verifikation ---
Write-Host ""
Write-Host "[6/7] Public-URL-Verifikation..."
# Public Installer
$installerStatus = ssh $remoteHost "curl -sI $publicInstallerUrl | head -1"
$installerStatus = ($installerStatus -split "`n")[0].Trim()
Write-Host " Public Installer: $installerStatus"
if ($installerStatus -notmatch "200") {
Write-Error "ABBRUCH: Public Installer ist nicht erreichbar (Status: $installerStatus)!"
exit 1
}
# Public Stable-Manifest
$stableManifestStatus = ssh $remoteHost "curl -sI $publicStableManifestUrl | head -1"
$stableManifestStatus = ($stableManifestStatus -split "`n")[0].Trim()
Write-Host " Public Manifest: $stableManifestStatus"
if ($stableManifestStatus -notmatch "200") {
Write-Error "ABBRUCH: Public Stable-Manifest ist nicht erreichbar (Status: $stableManifestStatus)!"
exit 1
}
# Public Manifest zeigt neue Version
$publicVersion = ssh $remoteHost "curl -s $publicStableManifestUrl | python3 -c `"import json,sys; d=json.load(sys.stdin); print(d.get('latest_version',''))`"" 2>/dev/null
$publicVersion = ($publicVersion -split "`n")[0].Trim()
Write-Host " Public Manifest latest_version: $publicVersion"
if ($publicVersion -ne $appVersion) {
Write-Error "ABBRUCH: Public Manifest zeigt Version $publicVersion statt $appVersion!"
exit 1
}
# --- [7/7] Abschlussbericht ---
Write-Host ""
Write-Host "==================================================="
Write-Host " RELEASE-ABSCHLUSSBERICHT"
Write-Host "==================================================="
Write-Host " Version: $appVersion"
Write-Host " Build: $buildStamp"
Write-Host " Channel: $appChannel"
Write-Host ""
Write-Host " Lokaler Installer SHA256: $localHash"
$serverHash = $hashes[0].ToLower()
Write-Host " Server release/ SHA256: $serverHash"
$serverDlHash = $hashes[1].ToLower()
Write-Host " Server downloads/ SHA256: $serverDlHash"
Write-Host " Manifest SHA256: $manifestHashCheck"
Write-Host ""
if ($localHash -eq $serverHash -and $localHash -eq $serverDlHash -and $localHash -eq $manifestHashCheck) {
Write-Host " SHA256 ALLE IDENTISCH" -ForegroundColor Green
} else {
Write-Error "ABBRUCH: SHA256-Divergenz im Abschlussbericht!"
exit 1
}
Write-Host ""
Write-Host " Public Installer: $installerStatus"
Write-Host " Public Manifest: $stableManifestStatus"
Write-Host " Public Manifest Version: $publicVersion"
Write-Host ""
Write-Host " Auto-Updater sieht neue Version: JA" -ForegroundColor Green
Write-Host ""
Write-Host "==================================================="
Write-Host " UPLOAD FERTIG"
Write-Host "==================================================="
Write-Host ""