diff --git a/assets/gifs/example.gif b/assets/gifs/example.gif index c4d8975..ee6a69e 100644 Binary files a/assets/gifs/example.gif and b/assets/gifs/example.gif differ diff --git a/crates/gitnow/src/commands/root.rs b/crates/gitnow/src/commands/root.rs index 67e76b8..85e3a0c 100644 --- a/crates/gitnow/src/commands/root.rs +++ b/crates/gitnow/src/commands/root.rs @@ -8,6 +8,7 @@ use crate::{ git_provider::Repository, interactive::InteractiveApp, projects_list::ProjectsListApp, + shell::ShellApp, }; #[derive(Debug, Clone)] @@ -25,6 +26,8 @@ impl RootCommand { search: Option>, cache: bool, clone: bool, + shell: bool, + force_refresh: bool, ) -> anyhow::Result<()> { tracing::debug!("executing"); @@ -72,7 +75,28 @@ impl RootCommand { }; if clone { - self.app.git_clone().clone_repo(&repo).await?; + self.app + .git_clone() + .clone_repo(&repo, force_refresh) + .await?; + } else { + tracing::info!("skipping clone for repo: {}", &repo.to_rel_path().display()); + } + + if shell { + self.app.shell().spawn_shell(&repo).await?; + } else { + tracing::info!("skipping shell for repo: {}", &repo.to_rel_path().display()); + println!( + "{}", + self.app + .config + .settings + .projects + .directory + .join(repo.to_rel_path()) + .display() + ); } Ok(()) diff --git a/crates/gitnow/src/git_clone.rs b/crates/gitnow/src/git_clone.rs index f187faf..94a7ba3 100644 --- a/crates/gitnow/src/git_clone.rs +++ b/crates/gitnow/src/git_clone.rs @@ -9,7 +9,11 @@ impl GitClone { Self { app } } - pub async fn clone_repo(&self, repository: &Repository) -> anyhow::Result<()> { + pub async fn clone_repo( + &self, + repository: &Repository, + force_refresh: bool, + ) -> anyhow::Result<()> { let project_path = self .app .config @@ -18,6 +22,10 @@ impl GitClone { .directory .join(repository.to_rel_path()); + if force_refresh { + tokio::fs::remove_dir_all(&project_path).await?; + } + if project_path.exists() { tracing::info!( "project: {} already exists, skipping clone", diff --git a/crates/gitnow/src/main.rs b/crates/gitnow/src/main.rs index a3f926c..89ff0da 100644 --- a/crates/gitnow/src/main.rs +++ b/crates/gitnow/src/main.rs @@ -17,6 +17,7 @@ mod git_clone; mod git_provider; mod interactive; mod projects_list; +mod shell; #[derive(Parser)] #[command(author, version, about, long_about = Some("Navigate git projects at the speed of thought"))] @@ -32,6 +33,12 @@ struct Command { #[arg(long = "no-clone", default_value = "false")] no_clone: bool, + + #[arg(long = "no-shell", default_value = "false")] + no_shell: bool, + + #[arg(long = "force-refresh", default_value = "false")] + force_refresh: bool, } #[derive(Subcommand)] @@ -64,7 +71,13 @@ async fn main() -> anyhow::Result<()> { Some(_) => todo!(), None => { RootCommand::new(app) - .execute(cli.search.as_ref(), !cli.no_cache, !cli.no_clone) + .execute( + cli.search.as_ref(), + !cli.no_cache, + !cli.no_clone, + !cli.no_shell, + cli.force_refresh, + ) .await?; } } diff --git a/crates/gitnow/src/shell.rs b/crates/gitnow/src/shell.rs new file mode 100644 index 0000000..986d45c --- /dev/null +++ b/crates/gitnow/src/shell.rs @@ -0,0 +1,65 @@ +use anyhow::Context; + +use crate::{app::App, git_provider::Repository}; + +pub struct Shell { + app: &'static App, +} + +impl Shell { + pub fn new(app: &'static App) -> Self { + Self { app } + } + + pub async fn spawn_shell(&self, repository: &Repository) -> anyhow::Result<()> { + let project_path = self + .app + .config + .settings + .projects + .directory + .join(repository.to_rel_path()); + + if !project_path.exists() { + anyhow::bail!( + "project path: {} does not exists, it is either a file, or hasn't been cloned", + project_path.display() + ); + } + + let shell = std::env::var("SHELL") + .context("failed to find SHELL variable, required for spawning embedded shells")?; + + let mut shell_cmd = tokio::process::Command::new(shell); + shell_cmd.current_dir(project_path); + + let mut process = shell_cmd.spawn().context("failed to spawn child session")?; + + let status = process.wait().await?; + + if !status.success() { + tracing::warn!( + "child session returned non-zero, or missing return code: {}", + status.code().unwrap_or_default() + ); + anyhow::bail!( + "child shell session failed with exit: {}", + status.code().unwrap_or(-1) + ); + } else { + tracing::debug!("child session returned 0 exit code"); + } + + Ok(()) + } +} + +pub trait ShellApp { + fn shell(&self) -> Shell; +} + +impl ShellApp for &'static App { + fn shell(&self) -> Shell { + Shell::new(self) + } +} diff --git a/vhs/example.vhs b/vhs/example.vhs index 3b0198d..27ee6ed 100644 --- a/vhs/example.vhs +++ b/vhs/example.vhs @@ -3,11 +3,15 @@ Set Theme "Dracula" Set Width 1200 Set Height 1000 Hide -Type "./target/debug/gitnow --no-cache" +Type "./target/debug/gitnow --no-cache --force-refresh" Enter Show Sleep 2s Type@500ms "bevy" Sleep 1s Enter +Sleep 10s +Type "echo 'I am now in bevy!'" +Enter Sleep 5s +Sleep 2s