fix: sync config with state when do ya pack -i or -u

This commit is contained in:
zooeywm 2024-12-01 02:58:03 +08:00 committed by sxyazi
parent ea75fe2b5c
commit 14890b0ec1
No known key found for this signature in database
2 changed files with 130 additions and 14 deletions

View file

@ -1,34 +1,46 @@
use anyhow::{Context, Result, bail};
use tokio::fs;
use yazi_shared::{Xdg, fs::{copy_dir_all, maybe_exists, must_exists}};
use tokio::fs::{self, remove_dir_all};
use yazi_shared::{Xdg, fs::{copy_dir_all, maybe_exists, must_exists, remove_dir_clean}};
use super::Package;
use super::{DiffAction, Package};
use crate::package::Git;
const TRACKER: &str = "DO_NOT_MODIFY_ANYTHING_IN_THIS_DIRECTORY";
impl Package {
pub(super) async fn deploy(&mut self) -> Result<()> {
let Some(name) = self.name().map(ToOwned::to_owned) else { bail!("Invalid package url") };
let from = self.local().join(&self.child);
let origin = self.local().join(&self.child);
self.header("Deploying package `{name}`")?;
self.is_flavor = maybe_exists(&from.join("flavor.toml")).await;
let to = if self.is_flavor {
self.is_flavor = maybe_exists(&origin.join("flavor.toml")).await;
let dest = if self.is_flavor {
Xdg::config_dir().join(format!("flavors/{name}"))
} else {
Xdg::config_dir().join(format!("plugins/{name}"))
};
let tracker = to.join(TRACKER);
if maybe_exists(&to).await && !must_exists(&tracker).await {
let tracker = dest.join(TRACKER);
if maybe_exists(&dest).await && !must_exists(&tracker).await {
bail!(
"A user package with the same name `{name}` already exists.
For safety, please manually delete it from your plugin/flavor directory and re-run the command."
);
}
fs::create_dir_all(&to).await?;
fs::write(tracker, []).await?;
fs::create_dir_all(&dest).await?;
let mut dirs = &["assets"][..];
let mut diff_actions = vec![];
if tracker.exists() {
let prev_rev = String::from_utf8(fs::read(&tracker).await?)?;
if prev_rev != self.rev {
diff_actions = Git::diff(&self.local().join(&self.child), &prev_rev, dirs).await?;
}
}
fs::write(tracker, &self.rev.trim_start_matches('=')).await?;
let files = if self.is_flavor {
&["flavor.toml", "tmtheme.xml", "README.md", "preview.png", "LICENSE", "LICENSE-tmtheme"][..]
@ -36,20 +48,61 @@ For safety, please manually delete it from your plugin/flavor directory and re-r
&["init.lua", "README.md", "LICENSE"][..]
};
let dirs = &["assets"][..];
for file in files {
let (from, to) = (from.join(file), to.join(file));
let (from, to) = (origin.join(file), dest.join(file));
fs::copy(&from, &to)
.await
.with_context(|| format!("failed to copy `{}` to `{}`", from.display(), to.display()))?;
}
for action in diff_actions.as_slice() {
match action {
DiffAction::Add { file } => {
let (from, to) = (origin.join(file), dest.join(file));
fs::create_dir_all(&to).await?;
fs::copy(&from, &to).await.with_context(|| {
format!("failed to copy `{}` to `{}`", from.display(), to.display())
})?;
}
DiffAction::Delete { file } => {
let file = dest.join(file);
fs::remove_file(&file)
.await
.with_context(|| format!("failed to remove `{}`", file.display()))?;
}
DiffAction::Rename { old, new } => {
let (from, to) = (dest.join(old), dest.join(new));
fs::create_dir_all(&to).await?;
fs::rename(&from, &to).await.with_context(|| {
format!("failed to rename `{}` to `{}`", from.display(), to.display())
})?;
}
DiffAction::Copy { old, new } => {
let (from, to) = (dest.join(old), dest.join(new));
fs::create_dir_all(&to).await?;
fs::copy(&from, &to).await.with_context(|| {
format!("failed to copy `{}` to `{}`", from.display(), to.display())
})?;
}
};
}
remove_dir_clean(&dest).await;
if !diff_actions.is_empty() {
dirs = &[];
}
for dir in dirs {
let (from, to) = (from.join(dir), to.join(dir));
let (from, to) = (origin.join(dir), dest.join(dir));
if !from.exists() {
if to.exists() {
remove_dir_all(&to)
.await
.with_context(|| format!("failed to prune no-longer exist folder {}", to.display()))?;
}
continue;
}

View file

@ -6,6 +6,13 @@ use yazi_shared::strip_trailing_newline;
pub(super) struct Git;
pub(super) enum DiffAction {
Add { file: String },
Delete { file: String },
Rename { old: String, new: String },
Copy { old: String, new: String },
}
impl Git {
pub(super) async fn clone(url: &str, path: &Path) -> Result<()> {
Self::exec(|c| c.args(["clone", url]).arg(path)).await
@ -42,6 +49,48 @@ impl Git {
))
}
pub(super) async fn diff(path: &Path, rev: &str, filter: &[&str]) -> Result<Vec<DiffAction>> {
let mut command = Command::new("git");
command.current_dir(path).args(["diff", "-M100", "-C100", rev, "origin/HEAD", "--name-status"]);
if !filter.is_empty() {
command.arg("--");
}
for f in filter {
command.arg(*f);
}
let output = command.output().await.context("Failed to run git diff")?;
if !output.status.success() {
bail!("git diff failed: {}", output.status);
}
let stdout = String::from_utf8(output.stdout).context("Failed to parse git diff output")?;
let mut diff_actions = Vec::new();
for line in stdout.lines() {
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.is_empty() {
continue;
}
match parts[0] {
"A" | "M" if parts.len() == 2 => {
diff_actions.push(DiffAction::Add { file: parts[1].to_string() })
}
"D" if parts.len() == 2 => {
diff_actions.push(DiffAction::Delete { file: parts[1].to_string() })
}
"R100" if parts.len() == 3 => diff_actions
.push(DiffAction::Rename { old: parts[1].to_string(), new: parts[2].to_string() }),
"C100" if parts.len() == 3 => diff_actions
.push(DiffAction::Copy { old: parts[1].to_string(), new: parts[2].to_string() }),
_ => {}
}
}
Ok(diff_actions)
}
async fn exec(f: impl FnOnce(&mut Command) -> &mut Command) -> Result<()> {
let status =
f(&mut Command::new("git")).status().await.context("Failed to execute `git` command")?;
@ -53,3 +102,17 @@ impl Git {
Ok(())
}
}
impl DiffAction {
pub(super) fn is_in(&self, dirs: &[&str]) -> bool {
for dir in dirs {
match self {
DiffAction::Add { file } => return file.contains(dir),
DiffAction::Delete { file } => return file.contains(dir),
DiffAction::Rename { old: _, new } => return new.contains(dir),
DiffAction::Copy { old: _, new } => return new.contains(dir),
}
}
false
}
}