This document describes the auto-update solution for JiuwenSwarm desktop (Windows and macOS). The goal is to prioritize stability while covering both stable and pre-release (beta) upgrade flows.
- Supports Windows and macOS desktop (Linux desktop also applies)
- Automatic update check on startup
- Manual update check via the sidebar "Update" page
- Desktop update source defaults to GitCode Releases, switchable to GitHub Releases; pip install mode uses PyPI
- Download artifacts differ per platform:
- Windows: Inno Setup installer
JiuwenSwarm-setup-<version>.exe - macOS: DMG image
JiuwenSwarm-<version>.dmg - Linux:
JiuwenSwarm-<version>.tar.gz
- Windows: Inno Setup installer
- After download, an external helper completes installation and restart: Windows via an interactive install wizard, macOS / Linux via a silent helper script that installs and restarts
- Pre-release support: stable and pre-release releases share the same update channel, so stable users also receive beta pushes
- No incremental/delta updates
- No in-process self-replacement
- No version-skip, canary releases, or multi-channel distribution
- No forced updates
Installer naming examples:
| Type | Windows | macOS |
|---|---|---|
| Stable | JiuwenSwarm-setup-0.2.2.exe |
JiuwenSwarm-0.2.2.dmg |
| Pre-release | JiuwenSwarm-setup-0.2.3.beta1.exe |
JiuwenSwarm-0.2.3.beta1.dmg |
Windows and macOS installers for the same version are released together.
Version comparison uses a total-order key release_sort_key with these rules:
- Base version compared numerically first (segment by segment):
0.2.3>0.2.2 - At the same base, a stable release ranks above any pre-release:
0.2.3>0.2.3.beta1 - Among pre-releases at the same base, the type decides:
dev<alpha<beta<rc<pre - Within the same type, a larger number is newer:
0.2.3.beta2>0.2.3.beta1
Because stable and pre-release releases share a channel, a user running stable 0.2.2 is prompted to update to 0.2.3.beta1 (a higher base version); this is the intended behavior.
- After app launch, the frontend asynchronously calls
updater.check - The backend requests the Releases list endpoint and fetches all published releases (including pre-releases, skipping drafts)
- The newest version is selected by
release_sort_keyand compared against the current__version__ - If a newer version is found, the latest version, publish date, release notes, and the platform-matched installer download URL are recorded
- The user clicks "Download Update" on the Update page
- The backend downloads the installer to the
.updatesdirectory under the user workspace in the background - After download completes, the frontend calls the pywebview API
install_updateto trigger installation - The desktop process launches the platform-specific helper, which waits for the current process and ports to release, then performs installation (interactive on Windows, silent on macOS / Linux) and restarts the app
Desktop defaults to the GitCode Releases list endpoint:
https://api.gitcode.com/api/v5/repos/{owner}/{repo}/releases
It can also be switched to GitHub Releases. To discover pre-releases, the backend fetches the full releases list (not the /latest endpoint, which excludes pre-releases), skips drafts, keeps prereleases, and picks the newest by version sort. It falls back to /latest when the list endpoint is unavailable.
Fields read from the release:
tag_name— version number (pre-release suffix preserved, e.g.0.2.3.beta1)body— release notespublished_at— publish dateassets[]— the platform-matched installer
Update settings are in the updater section of config.yaml:
updater:
enabled: true
desktop_release_api_type: gitcode # gitcode | github
repo_owner: openJiuwen
repo_name: jiuwenswarm
release_api_url: ""
asset_name_pattern_windows: "JiuwenSwarm-setup-{version}.exe"
asset_name_pattern_macos: "JiuwenSwarm-{version}.dmg"
asset_name_pattern_linux: "JiuwenSwarm-{version}.tar.gz"
timeout_seconds: 20Pip install mode additionally supports a pypi_mirror field.
The following WebSocket RPC methods are registered:
updater.get_status— query current update statusupdater.check— check for updatesupdater.download— download the installer (desktop mode) / perform pip upgrade (pip mode)updater.upgrade— pip mode only, perform upgrade and restartupdater.set_conf— save update configuration
In desktop mode, installation is triggered by the frontend via the pywebview API install_update(installer_path), executed directly by the desktop process (it owns the window and can close it before installation).
Status values:
idlecheckingup_to_dateupdate_availabledownloadingdownloadedinstallingupgrading(pip mode)restart_pending/restarting(pip mode)errorunsupporteddisabled
To avoid replacing files while the main process is running, installation is not performed within the current process. When the desktop process receives an install request from the frontend, it launches a platform-specific helper process/script that completes installation and restart after the main process exits.
The desktop process launches an independent update-helper subprocess via the update-helper subcommand, passing the installer path, app executable path, and parent PID. The helper flow:
- Wait for the parent process to exit
- Wait for backend / frontend ports to release (up to 15 seconds)
- Launch the installer interactively (no silent arguments), showing the Inno Setup wizard
The installer handles elevation (UAC prompt) and file replacement itself via Inno Setup. After the user completes the wizard, the installer is responsible for relaunching the app (Inno Setup's [Run] section can be configured to launch the app after install). The helper exits right after launching the installer; the installation is left to the user and the installer.
The desktop process generates a bash helper script and launches it independently. The script flow:
- Wait for the parent process to exit
- Wait for backend / frontend ports to release (up to 15 seconds)
hdiutil attachmounts the DMG at a controlled mount point- Find the
.appbundle inside the mount point dittocopies the.appto a temp target<install_target>.new- Atomic swap: move the old bundle aside as
<install_target>.old, move the new one into place, then remove the old one hdiutil detachunmounts the DMG and cleans up the mount pointxattr -dr com.apple.quarantineremoves the quarantine attributeopenlaunches the new app
The install target is fixed to /Applications/JiuwenSwarm.app (derived by walking up from the executable path to the .app bundle name).
The desktop process generates a bash helper script that, after the parent process exits, backs up the current install directory, extracts the tar.gz into it, removes the backup, and relaunches jiuwenswarm.
- All external paths are escaped with
shlex.quotein helper scripts to prevent shell injection if the release API serves a malicious asset name - Helper scripts are written to the
.updatesdirectory under the user workspace, with write permission checked before writing - The macOS helper writes full execution logs to
update_helper.login the logs directory