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: the
.exeinstaller in the Release; when multiple candidates exist, the unique filename containingworkswarmis preferred - macOS: the
.dmgimage in the Release; when multiple candidates exist, the unique filename containingworkswarmis preferred - Linux: still matched exactly as
JiuwenSwarm-<version>.tar.gz
- Windows: the
- 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
Windows and macOS installers for the same version are released together.
The Windows and macOS desktop updater finds the Release that corresponds to the installed version in the paginated Releases list, then compares its publication timestamp with the newest publication timestamp. If the list does not contain the installed version, its Release is fetched by tag. Timestamps are normalized to UTC, and an update is offered only when the remote timestamp is newer. Linux keeps the existing version comparison behavior.
The version string is used only to locate the installed Release and display status. It does not determine desktop release ordering or Windows/macOS installer matching. Each Release should contain exactly one .exe and one .dmg; as a temporary transition rule, if multiple same-platform installers exist, the updater selects the unique filename containing workswarm (case-insensitive). Other attachments may use arbitrary names. The pip install mode keeps its existing version comparison 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)
- Select the newest Release by publication time and compare it with the Release time of the installed 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 publication time. 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[]— installers matched by platform suffix (.exeon Windows,.dmgon macOS), with a uniqueworkswarmfilename preferred when multiple candidates exist
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_linux: "JiuwenSwarm-{version}.tar.gz"
timeout_seconds: 20Windows/macOS no longer read installer filename patterns. Legacy fields remain accepted for configuration compatibility but do not affect selection. Pip 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