Release SyncVito 1.0.8

This commit is contained in:
Davito committed 2026-09-24 16:13:36 +02:00
1 parent a04db4ee96
commit bb5bfda4fe
7 files changed
+268 -17

No files matched your search

+11
View File
@@ -114,6 +114,7 @@ class MainWindow(QMainWindow):
self.update_retry_timer.setInterval(UPDATE_RETRY_INTERVAL_MS)
self.update_retry_timer.timeout.connect(self._automatic_update_check)
QTimer.singleShot(3000, self._automatic_update_check)
QTimer.singleShot(1000, self._show_previous_update_error)
cleanup_attempts = 6 if "--updated" in sys.argv else 1
QTimer.singleShot(3000, lambda: self._cleanup_update_installers(cleanup_attempts))
if "--updated" in sys.argv:
@@ -666,6 +667,16 @@ class MainWindow(QMainWindow):
self._append_log("Update", "Erfolg", message)
self.tray.showMessage("SyncVito aktualisiert", message, QSystemTrayIcon.MessageIcon.Information, 5000)
def _show_previous_update_error(self) -> None:
message = self.updater.service.take_install_error()
if message:
self._append_log("Update", "Fehler", message)
QMessageBox.warning(
self,
"Update nicht abgeschlossen",
f"{message}\n\nGestartete Version: {__version__}\nProtokolle: {self.updater.service.update_directory}",
)
def _cleanup_update_installers(self, attempts_remaining: int = 1) -> None:
_removed, pending = self.updater.service.cleanup_installers()
if pending and attempts_remaining > 1:
+59 -12
View File
@@ -116,6 +116,7 @@ def _start_installer_after_exit(
arguments: list[str],
wait_pids: Iterable[int],
bootstrap_log_path: Path,
application_path: Path,
) -> None:
"""Startet Inno Setup erst nach dem Ende aller PyInstaller-Prozesse."""
if os.name != "nt":
@@ -129,43 +130,71 @@ def _start_installer_after_exit(
normalized_pids = sorted({int(pid) for pid in wait_pids if int(pid) > 0})
pid_list = ", ".join(str(pid) for pid in normalized_pids)
setup_arguments = ", ".join(_powershell_literal(argument) for argument in arguments)
setup_arguments = _powershell_literal(subprocess.list2cmdline(arguments))
result_path = bootstrap_log_path.parent / "update-result.json"
script = f"""
$ErrorActionPreference = 'Stop'
$bootstrapLog = {_powershell_literal(bootstrap_log_path)}
$applicationPath = {_powershell_literal(application_path)}
$resultPath = {_powershell_literal(result_path)}
$setupExitCode = $null
function Write-UpdateResult([bool]$success, [string]$message) {{
$result = @{{ success = $success; message = $message; exit_code = $setupExitCode }} | ConvertTo-Json
[System.IO.File]::WriteAllText($resultPath, $result, [System.Text.UTF8Encoding]::new($false))
}}
try {{
[System.IO.File]::WriteAllText(
$bootstrapLog,
((Get-Date).ToString('o') + ' Warte auf SyncVito-Prozesse: {pid_list}' + [Environment]::NewLine)
)
foreach ($processId in @({pid_list})) {{
$deadline = [DateTime]::UtcNow.AddMinutes(2)
while (Get-Process -Id $processId -ErrorAction SilentlyContinue) {{
if ([DateTime]::UtcNow -ge $deadline) {{
throw "SyncVito-Prozess $processId wurde nicht innerhalb von zwei Minuten beendet."
$deadline = [DateTime]::UtcNow.AddMinutes(2)
$lastWaiting = ''
while ($true) {{
$waiting = @(
foreach ($processId in @({pid_list})) {{
Get-Process -Id $processId -ErrorAction SilentlyContinue
}}
Start-Sleep -Milliseconds 200
Get-Process -Name ([System.IO.Path]::GetFileNameWithoutExtension($applicationPath)) -ErrorAction SilentlyContinue |
Where-Object {{ -not $_.Path -or $_.Path -eq $applicationPath }}
)
if ($waiting.Count -eq 0) {{ break }}
$waitingIds = ($waiting.Id | Sort-Object -Unique) -join ', '
if ($waitingIds -ne $lastWaiting) {{
[System.IO.File]::AppendAllText($bootstrapLog, 'Noch aktiv: ' + $waitingIds + [Environment]::NewLine)
$lastWaiting = $waitingIds
}}
if ([DateTime]::UtcNow -ge $deadline) {{
throw "SyncVito ist noch aktiv (Prozesse: $waitingIds). Bitte alle Instanzen im Infobereich ueber Beenden schliessen und das Update erneut starten."
}}
Start-Sleep -Milliseconds 200
}}
[System.IO.File]::AppendAllText(
$bootstrapLog,
((Get-Date).ToString('o') + ' Starte verifizierten Installer.' + [Environment]::NewLine)
)
$env:PYINSTALLER_RESET_ENVIRONMENT = '1'
$setupArguments = @({setup_arguments})
& {_powershell_literal(installer_path)} @setupArguments
$setupExitCode = $LASTEXITCODE
$setupArguments = {setup_arguments}
$setup = Start-Process -FilePath {_powershell_literal(installer_path)} -ArgumentList $setupArguments -PassThru -WindowStyle Hidden
# Nur auf Setup warten: -Wait wuerde auch den neu gestarteten Client abwarten.
$setup.WaitForExit()
$setupExitCode = $setup.ExitCode
[System.IO.File]::AppendAllText(
$bootstrapLog,
((Get-Date).ToString('o') + ' Installer-Exitcode: ' + $setupExitCode + [Environment]::NewLine)
)
if ($null -eq $setupExitCode -or $setupExitCode -ne 0) {{
throw "Installation abgebrochen (Exitcode: $setupExitCode). Details stehen im Inno-Setup-Protokoll im Update-Verzeichnis."
}}
Write-UpdateResult $true ''
exit $setupExitCode
}}
catch {{
$failureMessage = $_.Exception.Message
[System.IO.File]::AppendAllText(
$bootstrapLog,
((Get-Date).ToString('o') + ' Bootstrap-Fehler: ' + $_.Exception.Message + [Environment]::NewLine)
)
Write-UpdateResult $false $failureMessage
exit 1
}}
"""
@@ -297,7 +326,11 @@ class GiteaUpdateService:
raise UpdateInstallError("Updates können nur aus der installierten Windows-Version gestartet werden.")
installer = installer_path.resolve()
expected_name = f"SyncVitoSetup-{version}.exe"
if installer.parent != self.update_directory.resolve() or installer.name != expected_name or not installer.is_file():
if (
installer.parent != self.update_directory.resolve()
or installer.name != expected_name
or not installer.is_file()
):
raise UpdateInstallError("Der geprüfte Installer wurde nicht gefunden.")
safe_version = re.sub(r"[^0-9A-Za-z._-]+", "_", version).strip("._") or "unknown"
@@ -317,10 +350,24 @@ class GiteaUpdateService:
f"/LOG={setup_log}",
]
try:
_start_installer_after_exit(installer, arguments, wait_pids, bootstrap_log)
_start_installer_after_exit(installer, arguments, wait_pids, bootstrap_log, current_executable)
except (OSError, ValueError, UpdateError) as exc:
raise UpdateInstallError(f"Die Installation konnte nicht gestartet werden: {exc}") from exc
def take_install_error(self) -> str | None:
"""Liest das Ergebnis des externen Wächters einmalig; Protokolle bleiben erhalten."""
result_path = self.update_directory / "update-result.json"
try:
result = json.loads(result_path.read_text(encoding="utf-8"))
if not isinstance(result, dict) or not isinstance(result.get("success"), bool):
return None
result_path.unlink()
except (OSError, ValueError):
return None
if result["success"]:
return None
return str(result.get("message") or "Die Installation wurde nicht abgeschlossen.")
def cleanup_installers(self) -> tuple[int, int]:
"""Entfernt ausschließlich von SyncVito verwaltete Setup-Dateien."""
try: