启动后不久检查更新时,下好的新版本在重启时找不到
更新程序启动 30 秒后清理旧的暂存,原来会删掉所有 .ShellRS.app.update-*(Linux 是 .<AppImage>.update-*),包括启动后手动检查、切换渠道刚下载暂存好的新版本,于是「重启并 安装」报找不到文件,重试才从下载目录重新暂存。现在清理只删不比当前版本新的暂存,和下载 目录的规则一致。 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
db0a0faf05
commit
dc5adddd06
6 files changed
+83
-20
No files matched your search
+12
-3
@@ -207,8 +207,9 @@ pub trait Installer: Send + Sync {
|
|||||||
|
|
||||||
/// Remove what earlier updates left next to the installed copy. Called a
|
/// Remove what earlier updates left next to the installed copy. Called a
|
||||||
/// while after start, so a version that crashes right away leaves the
|
/// while after start, so a version that crashes right away leaves the
|
||||||
/// previous one's backup behind.
|
/// previous one's backup behind. An update newer than `current` stays:
|
||||||
fn clean_up(&self);
|
/// it may have been staged since start and be waiting for the restart.
|
||||||
|
fn clean_up(&self, current: &Version);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// For copies that cannot replace themselves.
|
/// For copies that cannot replace themselves.
|
||||||
@@ -237,7 +238,7 @@ impl Installer for NoInstaller {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clean_up(&self) {}
|
fn clean_up(&self, _: &Version) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The installer for this copy of ShellRS. `bundle` is `cx.app_path()`.
|
/// The installer for this copy of ShellRS. `bundle` is `cx.app_path()`.
|
||||||
@@ -296,6 +297,14 @@ pub(crate) fn sibling(path: &Path, name: &str) -> PathBuf {
|
|||||||
path.with_file_name(name)
|
path.with_file_name(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether `name` is an update staged as `<prefix><version>` that this copy
|
||||||
|
/// no longer needs: one not newer than `current`, or not named by a version.
|
||||||
|
#[cfg(any(target_os = "macos", target_os = "linux", test))]
|
||||||
|
pub(crate) fn is_stale_update(name: &str, prefix: &str, current: &Version) -> bool {
|
||||||
|
name.strip_prefix(prefix)
|
||||||
|
.is_some_and(|version| Version::parse(version).map_or(true, |version| version <= *current))
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|||||||
@@ -10,7 +10,9 @@ use std::os::unix::fs::PermissionsExt as _;
|
|||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use super::error::UpdateError;
|
use super::error::UpdateError;
|
||||||
use super::install::{InstallKind, Installer, Relaunch, Staged, sibling};
|
use semver::Version;
|
||||||
|
|
||||||
|
use super::install::{InstallKind, Installer, Relaunch, Staged, is_stale_update, sibling};
|
||||||
use super::manifest::Release;
|
use super::manifest::Release;
|
||||||
|
|
||||||
pub struct AppImageInstaller {
|
pub struct AppImageInstaller {
|
||||||
@@ -59,13 +61,13 @@ impl Installer for AppImageInstaller {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clean_up(&self) {
|
fn clean_up(&self, current: &Version) {
|
||||||
let Some(folder) = self.file.parent() else {
|
let Some(folder) = self.file.parent() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let prefix = self.prefix();
|
let prefix = self.prefix();
|
||||||
for entry in fs::read_dir(folder).into_iter().flatten().flatten() {
|
for entry in fs::read_dir(folder).into_iter().flatten().flatten() {
|
||||||
if entry.file_name().to_string_lossy().starts_with(&prefix) {
|
if is_stale_update(&entry.file_name().to_string_lossy(), &prefix, current) {
|
||||||
let _ = fs::remove_file(entry.path());
|
let _ = fs::remove_file(entry.path());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -115,13 +117,26 @@ mod tests {
|
|||||||
"stale",
|
"stale",
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
installer.clean_up();
|
// Staged since start and waiting for the restart: kept.
|
||||||
let left: Vec<_> = fs::read_dir(folder.path())
|
fs::write(
|
||||||
|
sibling(&file, ".ShellRS-x86_64.AppImage.update-0.3.0"),
|
||||||
|
"next",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
installer.clean_up(&Version::new(0, 2, 0));
|
||||||
|
let mut left: Vec<_> = fs::read_dir(folder.path())
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.flatten()
|
.flatten()
|
||||||
.map(|entry| entry.file_name().to_string_lossy().into_owned())
|
.map(|entry| entry.file_name().to_string_lossy().into_owned())
|
||||||
.collect();
|
.collect();
|
||||||
assert_eq!(left.len(), 2, "{left:?}");
|
left.sort();
|
||||||
assert!(left.contains(&"ShellRS-x86_64.AppImage".to_string()));
|
assert_eq!(
|
||||||
|
left,
|
||||||
|
[
|
||||||
|
".ShellRS-x86_64.AppImage.update-0.3.0",
|
||||||
|
"ShellRS-x86_64.AppImage",
|
||||||
|
"downloaded"
|
||||||
|
]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -14,9 +14,11 @@ use std::path::{Path, PathBuf};
|
|||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use semver::Version;
|
||||||
|
|
||||||
use super::build_info::APPLE_TEAM_ID;
|
use super::build_info::APPLE_TEAM_ID;
|
||||||
use super::error::UpdateError;
|
use super::error::UpdateError;
|
||||||
use super::install::{InstallKind, Installer, Relaunch, Staged, sibling};
|
use super::install::{InstallKind, Installer, Relaunch, Staged, is_stale_update, sibling};
|
||||||
use super::manifest::Release;
|
use super::manifest::Release;
|
||||||
use crate::i18n::t;
|
use crate::i18n::t;
|
||||||
|
|
||||||
@@ -217,7 +219,7 @@ impl Installer for MacInstaller {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clean_up(&self) {
|
fn clean_up(&self, current: &Version) {
|
||||||
let Some(folder) = self.bundle.parent() else {
|
let Some(folder) = self.bundle.parent() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -227,7 +229,7 @@ impl Installer for MacInstaller {
|
|||||||
);
|
);
|
||||||
for entry in fs::read_dir(folder).into_iter().flatten().flatten() {
|
for entry in fs::read_dir(folder).into_iter().flatten().flatten() {
|
||||||
let name = entry.file_name().to_string_lossy().into_owned();
|
let name = entry.file_name().to_string_lossy().into_owned();
|
||||||
if name.starts_with(&old) || name.starts_with(&update) {
|
if name.starts_with(&old) || is_stale_update(&name, &update, current) {
|
||||||
let _ = fs::remove_dir_all(entry.path());
|
let _ = fs::remove_dir_all(entry.path());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -349,10 +351,40 @@ mod tests {
|
|||||||
assert_eq!(names.len(), 2, "{names:?}");
|
assert_eq!(names.len(), 2, "{names:?}");
|
||||||
assert!(names[0].starts_with(".ShellRS.app.old-"), "{names:?}");
|
assert!(names[0].starts_with(".ShellRS.app.old-"), "{names:?}");
|
||||||
|
|
||||||
installer.clean_up();
|
// Run by 0.2.0, once it has started.
|
||||||
|
installer.clean_up(&Version::new(0, 2, 0));
|
||||||
assert_eq!(names_in(folder.path()), ["ShellRS.app"]);
|
assert_eq!(names_in(folder.path()), ["ShellRS.app"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The clean-up runs a while after start; an update downloaded and
|
||||||
|
/// staged before then is still there to install on restart.
|
||||||
|
#[test]
|
||||||
|
fn the_clean_up_keeps_an_update_staged_for_the_restart() {
|
||||||
|
let (folder, bundle) = installed();
|
||||||
|
let installer = MacInstaller::new(bundle.clone(), FakeTools::new("0.2.0", None));
|
||||||
|
for stale in [
|
||||||
|
".ShellRS.app.update-0.1.0",
|
||||||
|
".ShellRS.app.update-later",
|
||||||
|
".ShellRS.app.old-1",
|
||||||
|
] {
|
||||||
|
fs::create_dir(folder.path().join(stale)).unwrap();
|
||||||
|
}
|
||||||
|
let staged = installer
|
||||||
|
.stage(Path::new("package.zip"), &release("0.2.0"))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
installer.clean_up(&Version::new(0, 1, 0));
|
||||||
|
assert_eq!(
|
||||||
|
names_in(folder.path()),
|
||||||
|
[".ShellRS.app.update-0.2.0", "ShellRS.app"]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
installer.apply(&staged, true),
|
||||||
|
Ok(Relaunch::Restart(bundle.clone()))
|
||||||
|
);
|
||||||
|
assert_eq!(version_at(&bundle), "0.2.0");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn a_failed_swap_puts_the_old_bundle_back() {
|
fn a_failed_swap_puts_the_old_bundle_back() {
|
||||||
let (folder, bundle) = installed();
|
let (folder, bundle) = installed();
|
||||||
@@ -367,8 +399,12 @@ mod tests {
|
|||||||
Err(UpdateError::Install(_))
|
Err(UpdateError::Install(_))
|
||||||
));
|
));
|
||||||
assert_eq!(version_at(&bundle), "0.1.0");
|
assert_eq!(version_at(&bundle), "0.1.0");
|
||||||
installer.clean_up();
|
installer.clean_up(&Version::new(0, 1, 0));
|
||||||
assert_eq!(names_in(folder.path()), ["ShellRS.app"]);
|
assert!(
|
||||||
|
!names_in(folder.path())
|
||||||
|
.iter()
|
||||||
|
.any(|name| name.starts_with(".ShellRS.app.old-"))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -484,7 +520,7 @@ mod tests {
|
|||||||
.check_signature(&bundle, Some("SHELLRS"))
|
.check_signature(&bundle, Some("SHELLRS"))
|
||||||
.is_err()
|
.is_err()
|
||||||
);
|
);
|
||||||
installer.clean_up();
|
installer.clean_up(&Version::new(0, 2, 0));
|
||||||
assert_eq!(names_in(&installed), ["ShellRS.app"]);
|
assert_eq!(names_in(&installed), ["ShellRS.app"]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11,6 +11,8 @@
|
|||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
use semver::Version;
|
||||||
|
|
||||||
use super::error::UpdateError;
|
use super::error::UpdateError;
|
||||||
use super::install::{InstallKind, Installer, Launch, Relaunch, Staged};
|
use super::install::{InstallKind, Installer, Launch, Relaunch, Staged};
|
||||||
use super::manifest::Release;
|
use super::manifest::Release;
|
||||||
@@ -99,7 +101,8 @@ impl Installer for SetupInstaller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The setup program removes what the previous version left.
|
/// The setup program removes what the previous version left.
|
||||||
fn clean_up(&self) {}
|
// The package waits in the downloads folder, which the updater tidies.
|
||||||
|
fn clean_up(&self, _: &Version) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -507,7 +507,7 @@ impl Updater {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
cx.background_spawn(async move {
|
cx.background_spawn(async move {
|
||||||
services.installer.clean_up();
|
services.installer.clean_up(&services.current);
|
||||||
clean_downloads(&services.folder, &services.current);
|
clean_downloads(&services.folder, &services.current);
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
|
|||||||
+1
-1
@@ -99,7 +99,7 @@ impl Installer for FakeInstaller {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clean_up(&self) {}
|
fn clean_up(&self, _: &Version) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A copy of ShellRS that can install updates itself.
|
/// A copy of ShellRS that can install updates itself.
|
||||||
|
|||||||
Reference in new issue
Block a user