std/update
Updates: a manifest published over HTTPS names the newest version of each channel, with a file for each system; the update is downloaded, with progress, checked against its SHA-256 and its Ed25519 signature, and installed; the program restarts into it, and when the new version fails to start, the next start goes back to the version before.
import "std/update"
import "std/publickey"
PublicKey key = public_key_from_pem(UPDATE_KEY)?
Updater u = updater("https://example.com/notes/updates.json", key)
if check_rollback(u)? then
restart_app(u, [])
end
match check_for_update(u)? do
when SOME(update) then
str file = download_update(u, update, func(i64 done, i64 total) => show_progress(done, total))?
install_update(u, update, file)?
restart_app(u, [])
when NONE then
end
confirm_update_started(u)The manifest is JSON:
{"channels": {
"stable": {"version": "1.3.0", "notes": "What changed",
"files": {"windows-x86_64": {"url": "https://example.com/notes-1.3.0.zip",
"kind": "zip", "size": 1234567, "sha256": "...", "signature": "..."}}},
"beta": {"version": "1.4.0-beta.1", "files": {}}}}Each file is for a system as platform() and arch() name it, and of a
kind of UPDATE_KINDS: "zip" or "tar.gz" of the program's folder (on
macOS, of its .app); "appimage", which replaces the AppImage that
runs; or "exe" (NSIS's installer, run with /S) or "msi" (run by
msiexec /qn).
| Function | Result |
|---|---|
updater(manifest_url, key, state_dir) |
an Updater for the running program: its version is build_version(), its channel "stable", what it replaces is the program's folder, or its .app, or the AppImage it runs from, and its state is kept in state_dir, beside what it replaces unless given |
check_for_update(u) |
SOME of the UpdateInfo the channel offers when it is newer, by compare_versions; an ERROR of kind "http" or "invalid_manifest" |
download_update(u, info, progress) |
the downloaded file, progress told the bytes that came and the size, checked; an ERROR of kind "invalid_update" when its hash or signature is wrong, which deletes it |
verify_update(u, info, path) |
whether a file is the update: its SHA-256, and its signature by the updater's key |
install_update(u, info, path) |
the update put in place, what it replaced kept to go back to; a running program's own file is moved aside, as Windows allows, not overwritten |
restart_app(u, arguments) |
starts the new version and ends this one |
check_rollback(u) |
at each start: true when the last update was started and never confirmed, so it failed, and the version before was put back |
confirm_update_started(u), update_pending(u) |
the update kept, and what it replaced deleted; whether one waits for that |
compare_versions(a, b) |
-1, 0, or 1, by semantic versioning: 1.0.0-beta.2 before 1.0.0 |
make_update_entry(key, path, url, kind) |
the manifest's entry for a file, signed with the private key, for publishing |
Downloads use curl, which reads https:// and file:// URLs; the
signatures are std/publickey's, so a program that imports std/update
links with libcrypto.