Initial SyncVito release with installer updater

This commit is contained in:
Davito committed 2026-09-23 19:40:36 +02:00
commit b9e4c09f49
41 files changed
+4331

No files matched your search

+175
View File
@@ -0,0 +1,175 @@
param(
[switch]$NoVersionBump,
[switch]$SkipPublish,
[switch]$SkipGit,
[string]$TargetVersion = ""
)
$ErrorActionPreference = "Stop"
$Root = Split-Path -Parent $MyInvocation.MyCommand.Path
Set-Location -LiteralPath $Root
function Find-Python {
foreach ($candidate in @("python", "py")) {
try {
& $candidate --version 2>$null | Out-Null
if ($LASTEXITCODE -eq 0) { return $candidate }
}
catch { }
}
throw "Python wurde nicht gefunden."
}
function Find-InnoCompiler {
$candidates = @(
"$env:LOCALAPPDATA\Programs\Inno Setup 7\ISCC.exe",
"$env:LOCALAPPDATA\Programs\Inno Setup 6\ISCC.exe",
"$env:ProgramFiles\Inno Setup 7\ISCC.exe",
"${env:ProgramFiles(x86)}\Inno Setup 7\ISCC.exe",
"${env:ProgramFiles(x86)}\Inno Setup 6\ISCC.exe",
"$env:ProgramFiles\Inno Setup 6\ISCC.exe"
)
foreach ($candidate in $candidates) {
if (Test-Path -LiteralPath $candidate -PathType Leaf) { return $candidate }
}
throw "Inno Setup 6 oder 7 wurde nicht gefunden."
}
function Write-Utf8NoBom {
param([string]$LiteralPath, [string]$Content)
[System.IO.File]::WriteAllText($LiteralPath, $Content, [System.Text.UTF8Encoding]::new($false))
}
function Get-Sha256Hex {
param([string]$LiteralPath)
return (Get-FileHash -LiteralPath $LiteralPath -Algorithm SHA256).Hash.ToLowerInvariant()
}
$VersionPath = Join-Path $Root "version.txt"
$PyprojectPath = Join-Path $Root "pyproject.toml"
$ChangelogPath = Join-Path $Root "CHANGELOG.md"
$originalVersion = [System.IO.File]::ReadAllText($VersionPath, [System.Text.Encoding]::UTF8)
$originalPyproject = [System.IO.File]::ReadAllText($PyprojectPath, [System.Text.Encoding]::UTF8)
$currentVersion = $originalVersion.Trim()
if ($currentVersion -notmatch '^\d+\.\d+\.\d+$') {
throw "Ungültige Version in version.txt: $currentVersion"
}
$appVersion = $currentVersion
if (-not [string]::IsNullOrWhiteSpace($TargetVersion)) {
if ($NoVersionBump) { throw "TargetVersion und NoVersionBump dürfen nicht gemeinsam verwendet werden." }
if ($TargetVersion -notmatch '^\d+\.\d+\.\d+$') { throw "Ungültige Zielversion: $TargetVersion" }
if ([version]$TargetVersion -le [version]$currentVersion) {
throw "Die Zielversion $TargetVersion muss neuer als $currentVersion sein."
}
$appVersion = $TargetVersion
}
elseif (-not $NoVersionBump) {
$parts = @($currentVersion.Split('.') | ForEach-Object { [int]$_ })
$parts[2] += 1
$appVersion = "$($parts[0]).$($parts[1]).$($parts[2])"
}
$changelog = [System.IO.File]::ReadAllText($ChangelogPath, [System.Text.Encoding]::UTF8)
$sectionPattern = '(?ms)^##\s+' + [regex]::Escape($appVersion) + '\s+.*?(?=^##\s+\d+\.\d+\.\d+\s+|\z)'
$sectionMatch = [regex]::Match($changelog, $sectionPattern)
if (-not $sectionMatch.Success) {
throw "CHANGELOG.md enthält keinen Abschnitt für Version $appVersion."
}
$releaseNotes = "# SyncVito – Änderungsprotokoll`r`n`r`n$($sectionMatch.Value.Trim())`r`n"
$versionParts = @($appVersion.Split('.') | ForEach-Object { [int]$_ })
$fileVersion = "$($versionParts[0]).$($versionParts[1]).$($versionParts[2]).0"
$python = Find-Python
$iscc = Find-InnoCompiler
$sourceCommitted = $false
try {
Write-Utf8NoBom -LiteralPath $VersionPath -Content "$appVersion`n"
$versionRegex = [regex]::new('(?m)^(version\s*=\s*")[^"]+("\s*)$')
$updatedPyproject = $versionRegex.Replace($originalPyproject, "`${1}$appVersion`${2}", 1)
if ($updatedPyproject -eq $originalPyproject -and $appVersion -ne $currentVersion) {
throw "Die Projektversion in pyproject.toml konnte nicht aktualisiert werden."
}
Write-Utf8NoBom -LiteralPath $PyprojectPath -Content $updatedPyproject
$buildRoot = Join-Path $Root "build"
$appDist = Join-Path $buildRoot "app"
$workPath = Join-Path $buildRoot "pyinstaller"
New-Item -ItemType Directory -Force -Path $buildRoot, $appDist | Out-Null
$versionInfoPath = Join-Path $buildRoot "version_info.txt"
$versionInfo = @"
VSVersionInfo(
ffi=FixedFileInfo(filevers=($($versionParts[0]), $($versionParts[1]), $($versionParts[2]), 0), prodvers=($($versionParts[0]), $($versionParts[1]), $($versionParts[2]), 0), mask=0x3f, flags=0x0, OS=0x40004, fileType=0x1, subtype=0x0, date=(0, 0)),
kids=[StringFileInfo([StringTable('040704B0', [StringStruct('CompanyName', 'Davito'), StringStruct('FileDescription', 'SyncVito'), StringStruct('FileVersion', '$fileVersion'), StringStruct('InternalName', 'SyncVito'), StringStruct('OriginalFilename', 'SyncVito.exe'), StringStruct('ProductName', 'SyncVito'), StringStruct('ProductVersion', '$appVersion')])]), VarFileInfo([VarStruct('Translation', [1031, 1200])])]
)
"@
Write-Utf8NoBom -LiteralPath $versionInfoPath -Content $versionInfo
Write-Host "Tests werden ausgeführt …"
& $python -m pytest
if ($LASTEXITCODE -ne 0) { throw "Die Tests sind fehlgeschlagen." }
& $python -m ruff check .
if ($LASTEXITCODE -ne 0) { throw "Ruff hat Fehler gefunden." }
Write-Host "Programmsymbol wird erzeugt …"
& $python tools\generate_icon.py
if ($LASTEXITCODE -ne 0) { throw "Das Programmsymbol konnte nicht erzeugt werden." }
Write-Host "Baue SyncVito.exe mit PyInstaller …"
& $python -m PyInstaller --noconfirm --clean --distpath $appDist --workpath $workPath SyncVito.spec
if ($LASTEXITCODE -ne 0) { throw "Der PyInstaller-Build ist fehlgeschlagen." }
$appExe = Join-Path $appDist "SyncVito.exe"
if (-not (Test-Path -LiteralPath $appExe -PathType Leaf)) { throw "SyncVito.exe wurde nicht erzeugt." }
Write-Host "Prüfe den gebauten Programmstart …"
$smoke = Start-Process -FilePath $appExe -ArgumentList "--smoke-test" -PassThru -Wait -WindowStyle Hidden
if ($smoke.ExitCode -ne 0) { throw "Der EXE-Smoke-Test ist fehlgeschlagen (Exitcode $($smoke.ExitCode))." }
New-Item -ItemType Directory -Force -Path (Join-Path $Root "release") | Out-Null
Write-Host "Baue Windows-Installer …"
& $iscc "/DMyAppVersion=$appVersion" "/DMyAppFileVersion=$fileVersion" "/DMyAppExeSource=$appExe" (Join-Path $Root "installer\SyncVito.iss")
if ($LASTEXITCODE -ne 0) { throw "Der Inno-Setup-Build ist fehlgeschlagen." }
$installerName = "SyncVitoSetup-$appVersion.exe"
$installerPath = Join-Path $Root "release\$installerName"
if (-not (Test-Path -LiteralPath $installerPath -PathType Leaf)) { throw "Installer wurde nicht gefunden: $installerPath" }
$hash = Get-Sha256Hex -LiteralPath $installerPath
$checksumPath = "$installerPath.sha256"
Write-Utf8NoBom -LiteralPath $checksumPath -Content "$hash *$installerName`n"
$releaseChangelogPath = Join-Path $Root "release\CHANGELOG-$appVersion.md"
Write-Utf8NoBom -LiteralPath $releaseChangelogPath -Content $releaseNotes
if (-not $SkipGit) {
git add --all
if ($LASTEXITCODE -ne 0) { throw "Git konnte die Release-Änderungen nicht vormerken." }
git diff --cached --quiet
if ($LASTEXITCODE -ne 0) {
git commit -m "Release SyncVito $appVersion"
if ($LASTEXITCODE -ne 0) { throw "Der Release-Commit ist fehlgeschlagen." }
}
$sourceCommitted = $true
git push origin main
if ($LASTEXITCODE -ne 0) { throw "Der Quellstand konnte nicht nach Gitea gepusht werden." }
}
if (-not $SkipPublish) {
& (Join-Path $Root "publish_gitea_release.ps1") -Version $appVersion
if ($LASTEXITCODE -ne 0) { throw "Der Gitea-Release ist fehlgeschlagen." }
}
Write-Host ""
Write-Host "Release-Workflow abgeschlossen:"
Write-Host " Installer: $installerPath"
Write-Host " Prüfsumme: $checksumPath"
Write-Host " SHA256: $hash"
}
catch {
if (-not $sourceCommitted) {
Write-Utf8NoBom -LiteralPath $VersionPath -Content $originalVersion
Write-Utf8NoBom -LiteralPath $PyprojectPath -Content $originalPyproject
Write-Warning "Build fehlgeschlagen; Versionsdateien wurden zurückgesetzt."
}
throw
}