Skip to content

Commit

Permalink
fix: sync config with state when do ya pack -i or -u
Browse files Browse the repository at this point in the history
  • Loading branch information
zooeywm committed Nov 30, 2024
1 parent 14ac5b8 commit 5ef2e81
Show file tree
Hide file tree
Showing 2 changed files with 130 additions and 14 deletions.
81 changes: 67 additions & 14 deletions yazi-cli/src/package/deploy.rs
Original file line number Diff line number Diff line change
@@ -1,55 +1,108 @@
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"][..]
} else {
&["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;
}

Expand Down
63 changes: 63 additions & 0 deletions yazi-cli/src/package/git.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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")?;
Expand All @@ -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
}
}

0 comments on commit 5ef2e81

Please sign in to comment.