-
Notifications
You must be signed in to change notification settings - Fork 5
16 updater
Update checking, the Updater.exe flow, restart/reinstall. Related: 04 — Architecture · 15 — Development
SimpleLauncher\Services\CheckForUpdatesService.cs (WPF) and
SimpleLauncher.Avalonia\Services\AvaloniaCheckForUpdatesService.cs (Avalonia).
-
Source fallback chain (app + updater):
- GitHub API
https://api.github.com/repos/purelogiccode/SimpleLauncher/releases/latest(primary repo), - Secondary server
assets.purelogiccode.com/Simple Launcher/Simple Launcher/version.txt(Cloudflare-hosted) — builds the release/updater URLs from it (release_{version}_{rid}.zip/updater_{rid}.zip).
- GitHub API
- Silent check at startup. If every GitHub source is unreachable (offline, rate-limited, blocked), the check falls back to the secondary server.
- Manual check: About window "Check for Updates".
- Version comparison against the current
5.8.0; new version → prompts to download. -
Missing platform assets: a release is not required to ship every RID — when the latest release has no
release_{version}_{rid}.zip/updater_{rid}.zipfor the current platform, the check logs at Information and reports no update (LB-23) instead of an Error (which would also file a bug report).
Both apps read the same GitHub release, which ships the Windows and Linux bundles plus a standalone updater per RID:
-
release_{version}_{rid}.zip— the unified payload:SimpleLauncher.exe(WPF) andSimpleLauncher.Avalonia.exe(Avalonia) next to each other plus the shared content files (images/,tools/,samples/,appsettings.json, …). Both apps are framework-dependent single executables (managed assemblies bundled; native libraries such as SQLite/Skia ship beside the exe), so the .NET 10 Desktop Runtime is required. -
updater_{rid}.zip— the single standaloneUpdater.exe(framework-dependent single file) shared by both apps. It bundles the Avalonia native libraries it needs (libSkiaSharp.dll,libHarfBuzzSharp.dll,av_libglesv2.dll,IncludeNativeLibrariesForSelfExtract) and self-extracts them at launch, so the one file runs from either app folder and on legacy WPF-only installs that never had those natives (which is how the old WPF updater asset name is reused for the unified release). - On Linux (
rid=linux-x64/linux-arm64) the same asset names carry the self-contained single-fileSimpleLauncher.Avaloniapayload (managed assemblies bundled into the binary; native libraries such as Skia/HarfBuzz/SQLite ship beside it, same shape as the Windows exes) and the self-contained single-fileUpdater; the ZIP entries carry Unix permission bits (0755 for the executables) — see 15 — Development.
sequenceDiagram
participant A as App (WPF or Avalonia)
participant U as Updater.exe
A->>A: ShutdownForUpdateAsync (QuitSimpleLauncher)
A->>U: download fresh Updater.exe from GitHub/secondary server (if needed) + launch with current PID and target app
A->>A: app exits
U->>U: download the unified release zip, extract over the shared app folder
U->>A: relaunch the app that launched it with -whatsnew
A->>A: shows UpdateHistoryWindow (release notes)
-
QuitSimpleLauncher(WPF) /AvaloniaQuitSimpleLauncher(Avalonia):-
RestartApplicationAsync— spawns itself with--restarting, then shuts down; failed restart → "FailedToRestart" box, app stays alive; user-canceled launch (Win32 error 1223) → Information log + "FailedToRestart" box, app stays alive. -
ShutdownForUpdateAsync— downloads a freshUpdater.exefrom GitHub (fallback: secondary server), launches it with the current PID and the target executable name, kills the app.
-
-
ReinstallSimpleLauncher(WPF) /ReinstallAndShutdownAsync(Avalonia):-
StartUpdaterAndShutdownAsync/LaunchUpdaterAndShutdownAsync— launches the localUpdater.exeor downloads it from GitHub/secondary server, then hard-exits; access-denied (error 5) → correct message box.
-
-
--restartingskips single-instance enforcement during startup;-whatsnewshows the release-notes window. - The updater relaunches with
-whatsnew(not--restarting), so the restarted app still goes through the shared single-instance guard: if another instance is somehow still alive, the relaunch exits and restores that instance instead of running two copies.
The single updater (Updater.exe) shared by both apps. Responsibilities: fetch the latest
release (GitHub API primary → secondary server as fallback), download the unified release zip
for the current RID (retrying from the secondary server if the primary download fails), extract
over the application folder, relaunch the app that launched it. The launching app passes its
PID as the first argument and the target executable name (SimpleLauncher.exe or
SimpleLauncher.Avalonia.exe) as the second; older WPF releases pass only the PID, so the
updater detects the target from the process name. It was built from the Avalonia codebase
(cross-platform net10.0 / net10.0-windows) and replaced the former WPF-specific updater.
The updater excludes its own files (Updater*) during extraction so it can replace the
applications while running. Release-zip extraction is fail-closed against Zip-Slip: every entry
must resolve under the destination root, with a case-sensitive (Ordinal) prefix comparison on
Unix so a case-variant prefix (/app/ vs /APP/) cannot pass. On Unix, a replaced file keeps
its live mode and a file added by the update receives the mode carried by the release zip
(0755 for executables), so newly bundled tools stay executable.
- Locked files: extraction is staged and the live install is only swapped afterwards; before the swap the updater waits up to 30 s for any file still held open by another process (the application not fully exited, an antivirus scan, a second instance) to be released. A file that stays locked aborts the update with a manual-update prompt and an Information log — an expected user condition, never a bug report (#67442-67445).
Behavior parity with the dropped WPF updater is intentional: same Serilog setup (rolling warning
file + bug-report sink) and launch stats, same progress/log window with a Cancel button, the same
Dokan detection/install prompt after a successful update, the same retry/secondary-server
fallback, and the same global exception handling — including a UI-thread handler that logs,
reports the bug, shows the error dialog and exits with code 1 instead of vanishing. The app's
project reference uses ReferenceOutputAssembly=false; Private=false so the SDK does not copy
the updater's plain build sidecars into the publish output, and the publish step cleans the
updater's staging tree first because dotnet publish overwrites but never deletes
(Remove-UpdaterSidecars in scripts/package-release.ps1 remains as a guard).
- 04 — Architecture (startup/shutdown lifecycle)
- 15 — Development (release packaging)
- 17 — Release Notes
- Home
- 01 Overview
- 02 Projects And Solution
- 03 Quickstart
- 04 Architecture
- 05 Configuration
- 06 Systems And Launch
- 07 Core Services
- 08 Ui Layer
- 09 Retroachievements
- 10 Game Scanning
- 11 Bundled Tools
- 12 Data Formats
- 13 Logging And Debug
- 14 Testing
- 15 Development
- 16 Updater
- 17 Release Notes
- 18 Emulator Parameters
- Gui Test Harness
- Manual Tests
- Parameters