EuerChat 0.1.0: Tauri-Client mit Direktverbindung, ZNC, Livestream, Updater und Installer-Build

This commit is contained in:
Davito committed 2026-10-14 10:21:15 +02:00
commit df88f8abaa
27 files changed
+13167

No files matched your search

+178
View File
@@ -0,0 +1,178 @@
param(
[string]$Version = "",
[string]$Notes = "",
[switch]$ValidateOnly
)
# Veroeffentlicht den Installer aus dist\ als Gitea-Release (Tag v<Version>) samt SHA-256-Datei.
# Token: Umgebungsvariable GITEA_TOKEN oder DPAPI-Datei %LOCALAPPDATA%\EuerChat\developer\gitea-token.dpapi
# (ersatzweise die MusicBrain-Token-Datei).
$ErrorActionPreference = "Stop"
Add-Type -AssemblyName System.Net.Http
$Root = Split-Path -Parent $MyInvocation.MyCommand.Path
$BaseUrl = "https://gitea.davito.synology.me"
$Owner = "Davito"
$Repository = "EuerChat"
$LocalAppData = [Environment]::GetFolderPath("LocalApplicationData")
$TokenPaths = @(
(Join-Path $LocalAppData "EuerChat\developer\gitea-token.dpapi"),
(Join-Path $LocalAppData "MusicBrain\developer\gitea-token.dpapi")
)
if ([string]::IsNullOrWhiteSpace($Version)) {
$Version = (Get-Content -LiteralPath (Join-Path $Root "src-tauri\tauri.conf.json") -Raw -Encoding UTF8 | ConvertFrom-Json).version
}
if ($Version -notmatch '^\d+\.\d+\.\d+$') {
throw "Ungueltige Release-Version: $Version"
}
$Tag = "v$Version"
$InstallerName = "EuerChat_${Version}_x64-setup.exe"
$ChecksumName = "$InstallerName.sha256"
$InstallerPath = Join-Path $Root "dist\$InstallerName"
$ChecksumPath = Join-Path $Root "dist\$ChecksumName"
if (-not (Test-Path -LiteralPath $InstallerPath -PathType Leaf)) {
throw "Installer fehlt: $InstallerPath (zuerst build.bat ausfuehren)"
}
$sha256 = [System.Security.Cryptography.SHA256]::Create()
$stream = [System.IO.File]::OpenRead($InstallerPath)
try {
$hash = -join ($sha256.ComputeHash($stream) | ForEach-Object { $_.ToString("x2") })
}
finally {
$stream.Dispose()
$sha256.Dispose()
}
[System.IO.File]::WriteAllText($ChecksumPath, "$hash $InstallerName`n", [System.Text.UTF8Encoding]::new($false))
if ([string]::IsNullOrWhiteSpace($Notes)) {
$notesFile = Join-Path $Root "RELEASE_NOTES.md"
if (Test-Path -LiteralPath $notesFile) {
$Notes = (Get-Content -LiteralPath $notesFile -Raw -Encoding UTF8).Trim()
}
}
$plainToken = $env:GITEA_TOKEN
if ([string]::IsNullOrWhiteSpace($plainToken)) {
$tokenPath = $TokenPaths | Where-Object { Test-Path -LiteralPath $_ -PathType Leaf } | Select-Object -First 1
if (-not $tokenPath) {
throw "Gitea-Token fehlt. Erwartet: GITEA_TOKEN oder $($TokenPaths[0])"
}
$secure = (Get-Content -LiteralPath $tokenPath -Raw -Encoding UTF8).Trim() | ConvertTo-SecureString
$pointer = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($secure)
try { $plainToken = [Runtime.InteropServices.Marshal]::PtrToStringBSTR($pointer) }
finally { [Runtime.InteropServices.Marshal]::ZeroFreeBSTR($pointer) }
}
$client = [System.Net.Http.HttpClient]::new()
$client.Timeout = [TimeSpan]::FromMinutes(20)
$client.DefaultRequestHeaders.Authorization = [System.Net.Http.Headers.AuthenticationHeaderValue]::new("token", $plainToken.Trim())
$client.DefaultRequestHeaders.UserAgent.ParseAdd("EuerChat-Release-Publisher/$Version")
$client.DefaultRequestHeaders.Accept.ParseAdd("application/json")
function Send-Gitea {
param([string]$Method, [string]$Path, [object]$Body, [string]$Action, [switch]$AllowNotFound)
$request = [System.Net.Http.HttpRequestMessage]::new([System.Net.Http.HttpMethod]::new($Method), "$BaseUrl/api/v1$Path")
try {
if ($null -ne $Body) {
$request.Content = [System.Net.Http.StringContent]::new(
($Body | ConvertTo-Json -Depth 8 -Compress), [System.Text.Encoding]::UTF8, "application/json")
}
$response = $client.SendAsync($request).GetAwaiter().GetResult()
try {
$text = $response.Content.ReadAsStringAsync().GetAwaiter().GetResult()
if ($AllowNotFound -and [int]$response.StatusCode -eq 404) { return $null }
if (-not $response.IsSuccessStatusCode) {
throw "$Action fehlgeschlagen (HTTP $([int]$response.StatusCode)): $text"
}
if ([string]::IsNullOrWhiteSpace($text)) { return $null }
return $text | ConvertFrom-Json
}
finally { $response.Dispose() }
}
finally { $request.Dispose() }
}
try {
$repo = Send-Gitea -Method GET -Path "/repos/$Owner/$Repository" -Action "Repository-Zugriff pruefen"
if (-not $repo.permissions.push) {
throw "Der Token besitzt keine Schreibrechte fuer $Owner/$Repository."
}
Write-Host "Repository: $($repo.full_name) Version: $Version SHA256: $hash"
if ($ValidateOnly) { return }
$encodedTag = [Uri]::EscapeDataString($Tag)
$release = Send-Gitea -Method GET -Path "/repos/$Owner/$Repository/releases/tags/$encodedTag" -Action "Release lesen" -AllowNotFound
if ($null -eq $release) {
# Gitea datiert Releases nach dem Commit von main; ohne frischen Commit bleibt /releases/latest alt.
$marker = Send-Gitea -Method GET -Path "/repos/$Owner/$Repository/contents/RELEASES.md?ref=main" -Action "Release-Marker lesen" -AllowNotFound
$markerText = "# EuerChat Releases`n`nLetzter veroeffentlichter Release: v$Version ($([DateTime]::UtcNow.ToString('yyyy-MM-dd HH:mm:ss')) UTC)`n"
$markerBody = @{
message = "Release v$Version"
content = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($markerText))
branch = "main"
}
if ($marker) { $markerBody.sha = $marker.sha }
$markerMethod = if ($marker) { "PUT" } else { "POST" }
Send-Gitea -Method $markerMethod -Path "/repos/$Owner/$Repository/contents/RELEASES.md" -Body $markerBody -Action "Release-Marker-Commit anlegen" | Out-Null
Write-Host "Lege Gitea-Release $Tag an..."
$release = Send-Gitea -Method POST -Path "/repos/$Owner/$Repository/releases" -Action "Release anlegen" -Body @{
tag_name = $Tag
target_commitish = "main"
name = "EuerChat $Version"
body = $Notes
draft = $false
prerelease = $false
}
}
else {
Write-Host "Setze vorhandenen Release $Tag fort."
}
$existing = @($release.assets | ForEach-Object { $_.name })
foreach ($assetPath in @($InstallerPath, $ChecksumPath)) {
$assetName = [System.IO.Path]::GetFileName($assetPath)
if ($assetName -in $existing) {
Write-Host "Bereits vorhanden: $assetName"
continue
}
Write-Host "Lade hoch: $assetName"
$uploadUrl = "$BaseUrl/api/v1/repos/$Owner/$Repository/releases/$($release.id)/assets?name=$([Uri]::EscapeDataString($assetName))"
$multipart = [System.Net.Http.MultipartFormDataContent]::new()
$fileStream = [System.IO.File]::OpenRead($assetPath)
$fileContent = [System.Net.Http.StreamContent]::new($fileStream)
$fileContent.Headers.ContentType = [System.Net.Http.Headers.MediaTypeHeaderValue]::new("application/octet-stream")
$multipart.Add($fileContent, "attachment", $assetName)
$request = [System.Net.Http.HttpRequestMessage]::new([System.Net.Http.HttpMethod]::Post, $uploadUrl)
$request.Content = $multipart
try {
$response = $client.SendAsync($request).GetAwaiter().GetResult()
try {
if (-not $response.IsSuccessStatusCode) {
throw "Upload von $assetName fehlgeschlagen (HTTP $([int]$response.StatusCode)): $($response.Content.ReadAsStringAsync().GetAwaiter().GetResult())"
}
}
finally { $response.Dispose() }
}
finally {
$request.Dispose(); $multipart.Dispose(); $fileContent.Dispose(); $fileStream.Dispose()
}
}
$published = Send-Gitea -Method GET -Path "/repos/$Owner/$Repository/releases/tags/$encodedTag" -Action "Veroeffentlichten Release pruefen"
$names = @($published.assets | ForEach-Object { $_.name })
foreach ($required in @($InstallerName, $ChecksumName)) {
if ($required -notin $names) { throw "Release wurde angelegt, aber das Asset fehlt: $required" }
}
Write-Host "Fertig: $BaseUrl/$Owner/$Repository/releases/tag/$Tag"
}
finally {
$client.Dispose()
}