desktop dev update channel: every push to main builds dev bundles (<next patch>-dev.<UTC>), the VDS signs and publishes dev/latest.json (newest 5); app channel setting stable|dev, background updater at start / 15 min / resume / channel switch, deferred while in use, no downgrade, autostart rewritten on every start, card shows version and channel

This commit is contained in:
CRTkafa 2026-10-23 23:14:08 +08:00
parent 4dff7d9b2e
commit 39b2f73ad7
25 changed files with 732 additions and 47 deletions

View file

@ -41,10 +41,13 @@ fn updater_has_a_real_minisign_key_and_only_storebound_dev() {
assert_eq!(c["plugins"]["updater"]["endpoints"], serde_json::json!([updates::STABLE]));
// updater bundles are signed by tools/desktop-release.ts on the release Mac, never by a CI runner
assert_eq!(c["bundle"]["createUpdaterArtifacts"], false);
for ch in ["stable", "beta", "nightly", ""] {
assert_eq!(c["plugins"]["updater"]["windows"]["installMode"], "quiet");
for ch in ["stable", "beta", "dev", "nightly", ""] {
assert!(updates::endpoint_allowed(&Url::parse(updates::endpoint(ch)).unwrap()), "{ch}");
}
assert_eq!(updates::endpoint("beta"), updates::BETA);
assert_eq!(updates::endpoint("dev"), updates::DEV);
assert_eq!(updates::DEV, "https://storebound.dev/desktop/dev/latest.json");
assert_eq!(updates::endpoint("anything"), updates::STABLE);
for bad in ["http://storebound.dev/desktop/latest.json", "https://storebound.dev:8443/desktop/latest.json", "https://evil.test/desktop/latest.json", "https://storebound.dev/latest.json"] {
assert!(!updates::endpoint_allowed(&Url::parse(bad).unwrap()), "{bad}");
@ -103,3 +106,39 @@ fn deep_links_to_folders_ask_before_adding() {
let body = &cmds[at..];
assert!(body.find("confirm_dialog_with(").unwrap() < body.find("download_and_install(").unwrap());
}
#[test]
fn dev_versions_sort_and_never_downgrade() {
// a dev build of the next patch is above the current stable, below that next stable, and later stamps go up
assert!(updates::newer("0.8.48-dev.20261023120000", "0.8.47"));
assert!(updates::newer("0.8.48", "0.8.48-dev.20261023120000"));
assert!(updates::newer("0.8.48-dev.20261023120001", "0.8.48-dev.20261023120000"));
assert!(updates::newer("0.8.48-dev.20261024000000", "0.8.48-dev.9"), "numeric identifiers compare as numbers");
assert!(updates::newer("0.8.49-dev.1", "0.8.48"));
assert!(updates::newer("0.10.0", "0.9.9"));
assert!(!updates::newer("0.8.48", "0.8.48"));
assert!(!updates::newer("nonsense", "0.8.48"));
// switched from dev back to stable: stays on the dev build until a stable release passes it
assert!(!updates::should_install("0.8.48-dev.20261023120000", "0.8.47"));
assert!(updates::should_install("0.8.48-dev.20261023120000", "0.8.48"));
assert!(updates::should_install("0.8.47", "0.8.48-dev.20261023120000"));
assert!(!updates::should_install("0.8.48-dev.20261023120000", "0.8.48-dev.20261023120000"));
}
#[test]
fn background_updater_schedule_and_defer() {
assert!(updates::check_due(true, false, 0, 0), "at start");
assert!(updates::check_due(false, true, 0, 0), "channel switched");
assert!(updates::check_due(false, false, 3600, 5), "resume from sleep");
assert!(updates::check_due(false, false, 5, updates::CHECK_EVERY_SECS));
assert!(!updates::check_due(false, false, 5, 60));
assert_eq!(updates::CHECK_EVERY_SECS, 900);
assert!(updates::defer_install(true, true, 0));
assert!(!updates::defer_install(true, true, updates::MAX_DEFERS), "an open window still updates after a few checks");
assert!(!updates::defer_install(false, true, 0), "a hidden window can report focus");
assert!(!updates::defer_install(true, false, 0));
let _ = updates::take_kick();
updates::kick();
assert!(updates::take_kick());
assert!(!updates::take_kick());
}