Files
SyncVito/build_installer.ps1
T
2026-09-27 23:48:19 +02:00

192 lines
8.8 KiB
PowerShell
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
$pushTarget = "origin"
$tokenPath = @(
(Join-Path $env:LOCALAPPDATA "SyncVito\developer\gitea-token.dpapi"),
(Join-Path $env:LOCALAPPDATA "MusicBrain\developer\gitea-token.dpapi")
) | Where-Object { Test-Path -LiteralPath $_ -PathType Leaf } | Select-Object -First 1
if (-not [string]::IsNullOrWhiteSpace($env:GITEA_TOKEN)) {
$pushTarget = "https://Davito:$($env:GITEA_TOKEN.Trim())@gitea.davito.synology.me/Davito/SyncVito.git"
}
elseif ($tokenPath) {
try {
$sec = (Get-Content $tokenPath -Raw).Trim() | ConvertTo-SecureString
$bstr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($sec)
$t = [Runtime.InteropServices.Marshal]::PtrToStringBSTR($bstr)
$pushTarget = "https://Davito:$t@gitea.davito.synology.me/Davito/SyncVito.git"
} catch { }
}
git push $pushTarget 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
}