Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
0f30aaadf5 | |||
84941de931
|
|||
aa1ef7041e
|
|||
2f501c0efd
|
|||
962425c515
|
|||
708f8d6388
|
|||
|
5e97f68a77 | ||
eb684ef8e9 |
20
CHANGELOG.md
20
CHANGELOG.md
@@ -6,6 +6,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [0.3.4] - 2025-01-08
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- feat/add-post-clone-command
|
||||||
|
|
||||||
|
- add ability to specify custom command
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- use correct post clone command
|
||||||
|
- tests for config
|
||||||
|
|
||||||
|
### Other
|
||||||
|
- add ability to specify multiple commands
|
||||||
|
|
||||||
|
|
||||||
|
## [0.3.3] - 2025-01-07
|
||||||
|
|
||||||
|
### Other
|
||||||
|
- replace dotenv with dotenvy, a slightly more maintained version of the same library (#50)
|
||||||
|
|
||||||
## [0.3.2] - 2025-01-07
|
## [0.3.2] - 2025-01-07
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
14
Cargo.lock
generated
14
Cargo.lock
generated
@@ -532,10 +532,10 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dotenv"
|
name = "dotenvy"
|
||||||
version = "0.15.0"
|
version = "0.15.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
|
checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
@@ -786,7 +786,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gitnow"
|
name = "gitnow"
|
||||||
version = "0.3.2"
|
version = "0.3.3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -794,7 +794,7 @@ dependencies = [
|
|||||||
"clap",
|
"clap",
|
||||||
"crossterm",
|
"crossterm",
|
||||||
"dirs 5.0.1",
|
"dirs 5.0.1",
|
||||||
"dotenv",
|
"dotenvy",
|
||||||
"futures",
|
"futures",
|
||||||
"gitea-client",
|
"gitea-client",
|
||||||
"nucleo-matcher",
|
"nucleo-matcher",
|
||||||
@@ -2051,9 +2051,9 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "0.38.42"
|
version = "0.38.43"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85"
|
checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.6.0",
|
||||||
"errno",
|
"errno",
|
||||||
|
@@ -3,7 +3,7 @@ members = ["crates/*"]
|
|||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.3.3"
|
version = "0.3.4"
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
|
|
||||||
@@ -12,4 +12,4 @@ tokio = { version = "1", features = ["full"] }
|
|||||||
tracing = { version = "0.1", features = ["log"] }
|
tracing = { version = "0.1", features = ["log"] }
|
||||||
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
||||||
clap = { version = "4", features = ["derive", "env"] }
|
clap = { version = "4", features = ["derive", "env"] }
|
||||||
dotenv = { version = "0.15" }
|
dotenvy = { version = "0.15.7" }
|
||||||
|
@@ -17,7 +17,7 @@ tokio.workspace = true
|
|||||||
tracing.workspace = true
|
tracing.workspace = true
|
||||||
tracing-subscriber.workspace = true
|
tracing-subscriber.workspace = true
|
||||||
clap.workspace = true
|
clap.workspace = true
|
||||||
dotenv.workspace = true
|
dotenvy.workspace = true
|
||||||
|
|
||||||
serde = { version = "1.0.197", features = ["derive"] }
|
serde = { version = "1.0.197", features = ["derive"] }
|
||||||
uuid = { version = "1.7.0", features = ["v4"] }
|
uuid = { version = "1.7.0", features = ["v4"] }
|
||||||
|
@@ -4,6 +4,7 @@ use crate::{
|
|||||||
app::App,
|
app::App,
|
||||||
cache::CacheApp,
|
cache::CacheApp,
|
||||||
components::inline_command::InlineCommand,
|
components::inline_command::InlineCommand,
|
||||||
|
custom_command::CustomCommandApp,
|
||||||
fuzzy_matcher::{FuzzyMatcher, FuzzyMatcherApp},
|
fuzzy_matcher::{FuzzyMatcher, FuzzyMatcherApp},
|
||||||
git_clone::GitCloneApp,
|
git_clone::GitCloneApp,
|
||||||
git_provider::Repository,
|
git_provider::Repository,
|
||||||
@@ -110,12 +111,22 @@ impl RootCommand {
|
|||||||
} else {
|
} else {
|
||||||
eprintln!("cloning repository...");
|
eprintln!("cloning repository...");
|
||||||
git_clone.clone_repo(&repo, force_refresh).await?;
|
git_clone.clone_repo(&repo, force_refresh).await?;
|
||||||
|
|
||||||
|
self.app
|
||||||
|
.custom_command()
|
||||||
|
.execute_post_clone_command(&project_path)
|
||||||
|
.await?;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
tracing::info!("skipping clone for repo: {}", &repo.to_rel_path().display());
|
tracing::info!("skipping clone for repo: {}", &repo.to_rel_path().display());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
tracing::info!("repository already exists");
|
tracing::info!("repository already exists");
|
||||||
|
|
||||||
|
self.app
|
||||||
|
.custom_command()
|
||||||
|
.execute_post_update_command(&project_path)
|
||||||
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if shell {
|
if shell {
|
||||||
|
@@ -23,7 +23,7 @@ impl<'a> Spinner<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> StatefulWidget for Spinner<'a> {
|
impl StatefulWidget for Spinner<'_> {
|
||||||
type State = SpinnerState;
|
type State = SpinnerState;
|
||||||
|
|
||||||
fn render(
|
fn render(
|
||||||
|
@@ -3,7 +3,7 @@ use std::path::{Path, PathBuf};
|
|||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub settings: Settings,
|
pub settings: Settings,
|
||||||
@@ -12,16 +12,51 @@ pub struct Config {
|
|||||||
pub providers: Providers,
|
pub providers: Providers,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Serialize, Deserialize, PartialEq)]
|
#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Clone)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub projects: Projects,
|
pub projects: Projects,
|
||||||
|
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub cache: Cache,
|
pub cache: Cache,
|
||||||
|
|
||||||
|
pub post_clone_command: Option<PostCloneCommand>,
|
||||||
|
pub post_update_command: Option<PostUpdateCommand>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Serialize, Deserialize, PartialEq)]
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum PostUpdateCommand {
|
||||||
|
Single(String),
|
||||||
|
Multiple(Vec<String>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PostUpdateCommand {
|
||||||
|
pub fn get_commands(&self) -> Vec<String> {
|
||||||
|
match self.clone() {
|
||||||
|
PostUpdateCommand::Single(item) => vec![item],
|
||||||
|
PostUpdateCommand::Multiple(items) => items,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum PostCloneCommand {
|
||||||
|
Single(String),
|
||||||
|
Multiple(Vec<String>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PostCloneCommand {
|
||||||
|
pub fn get_commands(&self) -> Vec<String> {
|
||||||
|
match self.clone() {
|
||||||
|
PostCloneCommand::Single(item) => vec![item],
|
||||||
|
PostCloneCommand::Multiple(items) => items,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Clone)]
|
||||||
pub struct Projects {
|
pub struct Projects {
|
||||||
pub directory: ProjectLocation,
|
pub directory: ProjectLocation,
|
||||||
}
|
}
|
||||||
@@ -57,7 +92,7 @@ impl std::ops::Deref for ProjectLocation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Serialize, Deserialize, PartialEq)]
|
#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Clone)]
|
||||||
pub struct Cache {
|
pub struct Cache {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub location: CacheLocation,
|
pub location: CacheLocation,
|
||||||
@@ -89,7 +124,7 @@ impl Default for CacheLocation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
pub enum CacheDuration {
|
pub enum CacheDuration {
|
||||||
Enabled(bool),
|
Enabled(bool),
|
||||||
@@ -131,7 +166,7 @@ impl Default for CacheDuration {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Serialize, Deserialize, PartialEq)]
|
#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Clone)]
|
||||||
pub struct Providers {
|
pub struct Providers {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub github: Vec<GitHub>,
|
pub github: Vec<GitHub>,
|
||||||
@@ -139,7 +174,7 @@ pub struct Providers {
|
|||||||
pub gitea: Vec<Gitea>,
|
pub gitea: Vec<Gitea>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
||||||
pub struct GitHub {
|
pub struct GitHub {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub url: Option<String>,
|
pub url: Option<String>,
|
||||||
@@ -155,7 +190,7 @@ pub struct GitHub {
|
|||||||
pub organisations: Vec<GitHubOrganisation>,
|
pub organisations: Vec<GitHubOrganisation>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
||||||
pub struct GitHubUser(String);
|
pub struct GitHubUser(String);
|
||||||
|
|
||||||
impl From<GitHubUser> for String {
|
impl From<GitHubUser> for String {
|
||||||
@@ -170,7 +205,7 @@ impl<'a> From<&'a GitHubUser> for &'a str {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
||||||
pub struct GitHubOrganisation(String);
|
pub struct GitHubOrganisation(String);
|
||||||
|
|
||||||
impl From<GitHubOrganisation> for String {
|
impl From<GitHubOrganisation> for String {
|
||||||
@@ -185,7 +220,7 @@ impl<'a> From<&'a GitHubOrganisation> for &'a str {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
||||||
pub struct Gitea {
|
pub struct Gitea {
|
||||||
pub url: String,
|
pub url: String,
|
||||||
|
|
||||||
@@ -201,21 +236,21 @@ pub struct Gitea {
|
|||||||
pub organisations: Vec<GiteaOrganisation>,
|
pub organisations: Vec<GiteaOrganisation>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
pub enum GiteaAccessToken {
|
pub enum GiteaAccessToken {
|
||||||
Direct(String),
|
Direct(String),
|
||||||
Env { env: String },
|
Env { env: String },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
pub enum GitHubAccessToken {
|
pub enum GitHubAccessToken {
|
||||||
Direct(String),
|
Direct(String),
|
||||||
Env { env: String },
|
Env { env: String },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
||||||
pub struct GiteaUser(String);
|
pub struct GiteaUser(String);
|
||||||
|
|
||||||
impl From<GiteaUser> for String {
|
impl From<GiteaUser> for String {
|
||||||
@@ -230,7 +265,7 @@ impl<'a> From<&'a GiteaUser> for &'a str {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
||||||
pub struct GiteaOrganisation(String);
|
pub struct GiteaOrganisation(String);
|
||||||
impl From<GiteaOrganisation> for String {
|
impl From<GiteaOrganisation> for String {
|
||||||
fn from(value: GiteaOrganisation) -> Self {
|
fn from(value: GiteaOrganisation) -> Self {
|
||||||
@@ -361,7 +396,9 @@ mod test {
|
|||||||
},
|
},
|
||||||
projects: Projects {
|
projects: Projects {
|
||||||
directory: PathBuf::from("git").into()
|
directory: PathBuf::from("git").into()
|
||||||
}
|
},
|
||||||
|
post_update_command: None,
|
||||||
|
post_clone_command: None
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
config
|
config
|
||||||
@@ -386,7 +423,9 @@ mod test {
|
|||||||
},
|
},
|
||||||
settings: Settings {
|
settings: Settings {
|
||||||
cache: Cache::default(),
|
cache: Cache::default(),
|
||||||
projects: Projects::default()
|
projects: Projects::default(),
|
||||||
|
post_update_command: None,
|
||||||
|
post_clone_command: None
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
config
|
config
|
||||||
|
83
crates/gitnow/src/custom_command.rs
Normal file
83
crates/gitnow/src/custom_command.rs
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use crate::{app::App, config::Config};
|
||||||
|
|
||||||
|
pub struct CustomCommand {
|
||||||
|
config: Config,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CustomCommand {
|
||||||
|
pub fn new(config: Config) -> Self {
|
||||||
|
Self { config }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn execute_post_clone_command(&self, path: &Path) -> anyhow::Result<()> {
|
||||||
|
if let Some(post_clone_command) = &self.config.settings.post_clone_command {
|
||||||
|
for command in post_clone_command.get_commands() {
|
||||||
|
let command_parts = command.split(' ').collect::<Vec<_>>();
|
||||||
|
let Some((first, rest)) = command_parts.split_first() else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut cmd = tokio::process::Command::new(first);
|
||||||
|
cmd.args(rest).current_dir(path);
|
||||||
|
|
||||||
|
eprintln!("running command: {}", command);
|
||||||
|
|
||||||
|
tracing::info!(
|
||||||
|
path = path.display().to_string(),
|
||||||
|
cmd = command,
|
||||||
|
"running custom post clone command"
|
||||||
|
);
|
||||||
|
let output = cmd.output().await?;
|
||||||
|
let stdout = std::str::from_utf8(&output.stdout)?;
|
||||||
|
tracing::info!(
|
||||||
|
stdout = stdout,
|
||||||
|
"finished running custom post clone command"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn execute_post_update_command(&self, path: &Path) -> anyhow::Result<()> {
|
||||||
|
if let Some(post_update_command) = &self.config.settings.post_update_command {
|
||||||
|
for command in post_update_command.get_commands() {
|
||||||
|
let command_parts = command.split(' ').collect::<Vec<_>>();
|
||||||
|
let Some((first, rest)) = command_parts.split_first() else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut cmd = tokio::process::Command::new(first);
|
||||||
|
cmd.args(rest).current_dir(path);
|
||||||
|
|
||||||
|
eprintln!("running command: {}", command);
|
||||||
|
|
||||||
|
tracing::info!(
|
||||||
|
path = path.display().to_string(),
|
||||||
|
cmd = command,
|
||||||
|
"running custom post update command"
|
||||||
|
);
|
||||||
|
let output = cmd.output().await?;
|
||||||
|
let stdout = std::str::from_utf8(&output.stdout)?;
|
||||||
|
tracing::info!(
|
||||||
|
stdout = stdout,
|
||||||
|
"finished running custom post update command"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait CustomCommandApp {
|
||||||
|
fn custom_command(&self) -> CustomCommand;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CustomCommandApp for App {
|
||||||
|
fn custom_command(&self) -> CustomCommand {
|
||||||
|
CustomCommand::new(self.config.clone())
|
||||||
|
}
|
||||||
|
}
|
@@ -15,6 +15,7 @@ mod cache_codec;
|
|||||||
mod commands;
|
mod commands;
|
||||||
mod components;
|
mod components;
|
||||||
mod config;
|
mod config;
|
||||||
|
mod custom_command;
|
||||||
mod fuzzy_matcher;
|
mod fuzzy_matcher;
|
||||||
mod git_clone;
|
mod git_clone;
|
||||||
mod git_provider;
|
mod git_provider;
|
||||||
@@ -57,7 +58,7 @@ const DEFAULT_CONFIG_PATH: &str = ".config/gitnow/gitnow.toml";
|
|||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> anyhow::Result<()> {
|
async fn main() -> anyhow::Result<()> {
|
||||||
dotenv::dotenv().ok();
|
dotenvy::dotenv().ok();
|
||||||
tracing_subscriber::fmt()
|
tracing_subscriber::fmt()
|
||||||
.with_env_filter(
|
.with_env_filter(
|
||||||
EnvFilter::builder()
|
EnvFilter::builder()
|
||||||
|
Reference in New Issue
Block a user