From d2cc73c9277662f05be12e1cdd1c9659dd1d6268 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20=C2=B7=20Misaki=20Masa?= Date: Sun, 13 Apr 2025 14:46:16 +0800 Subject: [PATCH] fix: ensure state synchronization even when the package update fails midway (#2613) --- yazi-cli/src/package/add.rs | 8 ++-- yazi-cli/src/package/delete.rs | 58 ++++++++++++++--------------- yazi-cli/src/package/deploy.rs | 65 ++++++++++++++++----------------- yazi-cli/src/package/git.rs | 2 +- yazi-cli/src/package/hash.rs | 13 +++++++ yazi-cli/src/package/install.rs | 2 +- yazi-cli/src/package/package.rs | 43 ++++++++++------------ 7 files changed, 98 insertions(+), 93 deletions(-) diff --git a/yazi-cli/src/package/add.rs b/yazi-cli/src/package/add.rs index 803e8543..a2ec1941 100644 --- a/yazi-cli/src/package/add.rs +++ b/yazi-cli/src/package/add.rs @@ -8,13 +8,13 @@ impl Dependency { self.header("Upgrading package `{name}`")?; let path = self.local(); - if !must_exists(&path).await { - Git::clone(&self.remote(), &path).await?; - } else { + if must_exists(&path).await { Git::pull(&path).await?; + } else { + Git::clone(&self.remote(), &path).await?; }; - self.rev = Git::hash(&path).await?; + self.rev = Git::revision(&path).await?; self.deploy().await } } diff --git a/yazi-cli/src/package/delete.rs b/yazi-cli/src/package/delete.rs index 76bbe2cf..17ac7588 100644 --- a/yazi-cli/src/package/delete.rs +++ b/yazi-cli/src/package/delete.rs @@ -1,4 +1,4 @@ -use anyhow::{Context, Result, bail}; +use anyhow::{Context, Result}; use tokio::fs; use yazi_fs::{maybe_exists, ok_or_not_found, remove_dir_clean, remove_sealed}; use yazi_macro::outln; @@ -14,36 +14,9 @@ impl Dependency { return Ok(outln!("Not found, skipping")?); } - if self.hash != self.hash().await? { - bail!( - "You have modified the contents of the `{}` {}. For safety, the operation has been aborted. -Please manually delete it from: {}", - self.name, - if self.is_flavor { "flavor" } else { "plugin" }, - dir.display() - ); - } - - let files = if self.is_flavor { - &["flavor.toml", "tmtheme.xml", "README.md", "preview.png", "LICENSE", "LICENSE-tmtheme"][..] - } else { - &["main.lua", "README.md", "LICENSE"][..] - }; - for p in files.iter().map(|&f| dir.join(f)) { - ok_or_not_found(remove_sealed(&p).await) - .with_context(|| format!("failed to delete `{}`", p.display()))?; - } - + self.hash_check().await?; self.delete_assets().await?; - if ok_or_not_found(fs::remove_dir(&dir).await).is_ok() { - outln!("Done!")?; - } else { - outln!( - "Done! -For safety, user data has been preserved, please manually delete them within: {}", - dir.display() - )?; - } + self.delete_sources().await?; Ok(()) } @@ -65,4 +38,29 @@ For safety, user data has been preserved, please manually delete them within: {} remove_dir_clean(&assets).await; Ok(()) } + + pub(super) async fn delete_sources(&self) -> Result<()> { + let dir = self.target(); + let files = if self.is_flavor { + &["flavor.toml", "tmtheme.xml", "README.md", "preview.png", "LICENSE", "LICENSE-tmtheme"][..] + } else { + &["main.lua", "README.md", "LICENSE"][..] + }; + + for p in files.iter().map(|&f| dir.join(f)) { + ok_or_not_found(remove_sealed(&p).await) + .with_context(|| format!("failed to delete `{}`", p.display()))?; + } + + if ok_or_not_found(fs::remove_dir(&dir).await).is_ok() { + outln!("Done!")?; + } else { + outln!( + "Done! +For safety, user data has been preserved, please manually delete them within: {}", + dir.display() + )?; + } + Ok(()) + } } diff --git a/yazi-cli/src/package/deploy.rs b/yazi-cli/src/package/deploy.rs index 663477dd..a5b75bd2 100644 --- a/yazi-cli/src/package/deploy.rs +++ b/yazi-cli/src/package/deploy.rs @@ -1,6 +1,6 @@ use std::path::{Path, PathBuf}; -use anyhow::{Context, Result, bail}; +use anyhow::{Context, Result}; use tokio::fs; use yazi_fs::{copy_and_seal, maybe_exists, remove_dir_clean}; use yazi_macro::outln; @@ -15,44 +15,27 @@ impl Dependency { self.is_flavor = maybe_exists(&from.join("flavor.toml")).await; let to = self.target(); - if maybe_exists(&to).await && self.hash != self.hash().await? { - bail!( - "You have modified the contents of the `{}` {}. For safety, the operation has been aborted. -Please manually delete it from `{}` and re-run the command.", - self.name, - if self.is_flavor { "flavor" } else { "plugin" }, - to.display() - ); + let exists = maybe_exists(&to).await; + if exists { + self.hash_check().await?; } fs::create_dir_all(&to).await?; - if let Err(e) = Self::deploy_sources(&from, &to, self.is_flavor).await { - remove_dir_clean(&to).await; - return Err(e); - } - self.delete_assets().await?; - Self::deploy_assets(from.join("assets"), to.join("assets")).await?; - self.hash = self.hash().await?; - outln!("Done!")?; - - Ok(()) - } - - async fn deploy_sources(from: &Path, to: &Path, is_flavor: bool) -> Result<()> { - let files = if is_flavor { - &["flavor.toml", "tmtheme.xml", "README.md", "preview.png", "LICENSE", "LICENSE-tmtheme"][..] - } else { - &["main.lua", "README.md", "LICENSE"][..] - }; - - for file in files { - let (from, to) = (from.join(file), to.join(file)); - copy_and_seal(&from, &to) - .await - .with_context(|| format!("failed to copy `{}` to `{}`", from.display(), to.display()))?; + let res1 = Self::deploy_assets(from.join("assets"), to.join("assets")).await; + let res2 = Self::deploy_sources(&from, &to, self.is_flavor).await; + if !exists && (res2.is_err() || res1.is_err()) { + self.delete_assets().await?; + self.delete_sources().await?; } + + remove_dir_clean(&to).await; + self.hash = self.hash().await?; + res2?; + res1?; + + outln!("Done!")?; Ok(()) } @@ -72,4 +55,20 @@ Please manually delete it from `{}` and re-run the command.", } Ok(()) } + + async fn deploy_sources(from: &Path, to: &Path, is_flavor: bool) -> Result<()> { + let files = if is_flavor { + &["flavor.toml", "tmtheme.xml", "README.md", "preview.png", "LICENSE", "LICENSE-tmtheme"][..] + } else { + &["main.lua", "README.md", "LICENSE"][..] + }; + + for file in files { + let (from, to) = (from.join(file), to.join(file)); + copy_and_seal(&from, &to) + .await + .with_context(|| format!("failed to copy `{}` to `{}`", from.display(), to.display()))?; + } + Ok(()) + } } diff --git a/yazi-cli/src/package/git.rs b/yazi-cli/src/package/git.rs index 5da090bf..58004792 100644 --- a/yazi-cli/src/package/git.rs +++ b/yazi-cli/src/package/git.rs @@ -25,7 +25,7 @@ impl Git { Ok(()) } - pub(super) async fn hash(path: &Path) -> Result { + pub(super) async fn revision(path: &Path) -> Result { let output = Command::new("git") .args(["rev-parse", "--short", "HEAD"]) .current_dir(path) diff --git a/yazi-cli/src/package/hash.rs b/yazi-cli/src/package/hash.rs index 42e83ba6..841f112a 100644 --- a/yazi-cli/src/package/hash.rs +++ b/yazi-cli/src/package/hash.rs @@ -52,4 +52,17 @@ impl Dependency { Ok(format!("{:x}", h.finish_128())) } + + pub(super) async fn hash_check(&self) -> Result<()> { + if self.hash != self.hash().await? { + bail!( + "You have modified the contents of the `{}` {}. For safety, the operation has been aborted. +Please manually delete it from `{}` and re-run the command.", + self.name, + if self.is_flavor { "flavor" } else { "plugin" }, + self.target().display() + ); + } + Ok(()) + } } diff --git a/yazi-cli/src/package/install.rs b/yazi-cli/src/package/install.rs index c1860c95..ae25cbf6 100644 --- a/yazi-cli/src/package/install.rs +++ b/yazi-cli/src/package/install.rs @@ -15,7 +15,7 @@ impl Dependency { }; if self.rev.is_empty() { - self.rev = Git::hash(&path).await?; + self.rev = Git::revision(&path).await?; } else { Git::checkout(&path, self.rev.trim_start_matches('=')).await?; } diff --git a/yazi-cli/src/package/package.rs b/yazi-cli/src/package/package.rs index 05ab7812..6e9aae07 100644 --- a/yazi-cli/src/package/package.rs +++ b/yazi-cli/src/package/package.rs @@ -25,41 +25,36 @@ impl Package { pub(crate) async fn add_many(&mut self, uses: &[String]) -> Result<()> { for u in uses { - if let Err(e) = self.add(u).await { - self.save().await?; - return Err(e); - } + let r = self.add(u).await; + self.save().await?; + r?; } - self.save().await + Ok(()) } pub(crate) async fn delete_many(&mut self, uses: &[String]) -> Result<()> { for u in uses { - if let Err(e) = self.delete(u).await { - self.save().await?; - return Err(e); - } + let r = self.delete(u).await; + self.save().await?; + r?; } - self.save().await + Ok(()) } pub(crate) async fn install(&mut self, upgrade: bool) -> Result<()> { - for d in &mut self.plugins { - if upgrade { - d.upgrade().await?; - } else { - d.install().await?; - } + for i in 0..self.plugins.len() { + let r = + if upgrade { self.plugins[i].upgrade().await } else { self.plugins[i].install().await }; + self.save().await?; + r?; } - for d in &mut self.flavors { - if upgrade { - d.upgrade().await?; - } else { - d.install().await?; - } + for i in 0..self.flavors.len() { + let r = + if upgrade { self.flavors[i].upgrade().await } else { self.flavors[i].install().await }; + self.save().await?; + r?; } - - self.save().await + Ok(()) } pub(crate) fn print(&self) -> Result<()> {