Compare commits
1 Commits
main
...
push-moqzl
Author | SHA1 | Date | |
---|---|---|---|
383ef39cd7 |
58
CHANGELOG.md
58
CHANGELOG.md
@ -6,64 +6,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [0.3.5] - 2025-03-04
|
||||
|
||||
### Added
|
||||
- allow clone all
|
||||
|
||||
### Fixed
|
||||
- *(deps)* update rust crate serde to v1.0.218
|
||||
- *(deps)* update tokio-prost monorepo to v0.13.5
|
||||
- *(deps)* update rust crate termwiz to 0.23.0
|
||||
- *(deps)* update all dependencies
|
||||
- *(deps)* update rust crate uuid to v1.13.0
|
||||
- *(deps)* update all dependencies
|
||||
- *(deps)* update rust crate async-trait to v0.1.86
|
||||
- *(deps)* update rust crate uuid to v1.12.1
|
||||
- *(deps)* update rust crate octocrab to 0.43.0
|
||||
- *(deps)* update rust crate uuid to v1.12.0
|
||||
- *(deps)* update rust crate dirs to v6
|
||||
- *(deps)* update rust crate uuid to v1.11.1
|
||||
|
||||
### Other
|
||||
- *(deps)* update all dependencies
|
||||
- *(deps)* update all dependencies
|
||||
- *(deps)* update rust crate clap to v4.5.29
|
||||
- *(deps)* update rust crate clap to v4.5.27
|
||||
- *(deps)* update rust crate clap to v4.5.26
|
||||
- *(deps)* update rust crate tokio to v1.43.0
|
||||
- update example
|
||||
|
||||
## [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
|
||||
|
||||
### Fixed
|
||||
- *(deps)* update rust crate async-trait to v0.1.85
|
||||
|
||||
## [0.3.1] - 2025-01-02
|
||||
|
||||
### Other
|
||||
- enable publish
|
||||
|
||||
## [0.3.0] - 2025-01-01
|
||||
|
||||
### Added
|
||||
|
1011
Cargo.lock
generated
1011
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -3,7 +3,7 @@ members = ["crates/*"]
|
||||
resolver = "2"
|
||||
|
||||
[workspace.package]
|
||||
version = "0.3.5"
|
||||
version = "0.3.0"
|
||||
|
||||
[workspace.dependencies]
|
||||
|
||||
@ -12,4 +12,4 @@ tokio = { version = "1", features = ["full"] }
|
||||
tracing = { version = "0.1", features = ["log"] }
|
||||
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
||||
clap = { version = "4", features = ["derive", "env"] }
|
||||
dotenvy = { version = "0.15.7" }
|
||||
dotenv = { version = "0.15" }
|
||||
|
@ -17,7 +17,7 @@ tokio.workspace = true
|
||||
tracing.workspace = true
|
||||
tracing-subscriber.workspace = true
|
||||
clap.workspace = true
|
||||
dotenvy.workspace = true
|
||||
dotenv.workspace = true
|
||||
|
||||
serde = { version = "1.0.197", features = ["derive"] }
|
||||
uuid = { version = "1.7.0", features = ["v4"] }
|
||||
@ -26,8 +26,8 @@ toml = "0.8.19"
|
||||
|
||||
gitea-client = { version = "1.22.1" }
|
||||
url = "2.5.2"
|
||||
octocrab = "0.43.0"
|
||||
dirs = "6.0.0"
|
||||
octocrab = "0.42.0"
|
||||
dirs = "5.0.1"
|
||||
prost = "0.13.2"
|
||||
prost-types = "0.13.2"
|
||||
bytes = "1.7.1"
|
||||
@ -35,8 +35,7 @@ nucleo-matcher = "0.3.1"
|
||||
ratatui = { version = "0.29.0", features = ["termwiz"] }
|
||||
crossterm = { version = "0.28.0", features = ["event-stream"] }
|
||||
futures = "0.3.30"
|
||||
termwiz = "0.23.0"
|
||||
regex = "1.11.1"
|
||||
termwiz = "0.22.0"
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "1.4.0"
|
||||
|
@ -1,83 +1,3 @@
|
||||
pub mod root;
|
||||
pub mod shell;
|
||||
pub mod update;
|
||||
pub mod clone {
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use futures::future::join_all;
|
||||
use regex::Regex;
|
||||
|
||||
use crate::{
|
||||
app::App, cache::CacheApp, custom_command::CustomCommandApp, git_clone::GitCloneApp,
|
||||
};
|
||||
|
||||
#[derive(clap::Parser)]
|
||||
pub struct CloneCommand {
|
||||
#[arg(long = "search")]
|
||||
search: String,
|
||||
}
|
||||
|
||||
impl CloneCommand {
|
||||
pub async fn execute(&mut self, app: &'static App) -> anyhow::Result<()> {
|
||||
let repos = app.cache().get().await?.ok_or(anyhow::anyhow!(
|
||||
"failed to get cache, do a gitnow update first"
|
||||
))?;
|
||||
|
||||
let search = Regex::new(&self.search)?;
|
||||
|
||||
let filtered_repos = repos
|
||||
.iter()
|
||||
.filter(|r| search.is_match(&r.to_rel_path().display().to_string()))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let concurrency_limit = Arc::new(tokio::sync::Semaphore::new(5));
|
||||
let mut handles = Vec::default();
|
||||
for repo in filtered_repos {
|
||||
let config = app.config.clone();
|
||||
let custom_command = app.custom_command();
|
||||
let git_clone = app.git_clone();
|
||||
let repo = repo.clone();
|
||||
let concurrency = Arc::clone(&concurrency_limit);
|
||||
|
||||
let handle = tokio::spawn(async move {
|
||||
let permit = concurrency.acquire().await?;
|
||||
|
||||
let project_path = config.settings.projects.directory.join(repo.to_rel_path());
|
||||
if !project_path.exists() {
|
||||
eprintln!("cloning repository: {}", repo.to_rel_path().display());
|
||||
git_clone.clone_repo(&repo, false).await?;
|
||||
|
||||
custom_command
|
||||
.execute_post_clone_command(&project_path)
|
||||
.await?;
|
||||
}
|
||||
|
||||
drop(permit);
|
||||
|
||||
Ok::<(), anyhow::Error>(())
|
||||
});
|
||||
|
||||
handles.push(handle);
|
||||
}
|
||||
|
||||
let res = join_all(handles).await;
|
||||
|
||||
for res in res {
|
||||
match res {
|
||||
Ok(Ok(())) => {}
|
||||
Ok(Err(e)) => {
|
||||
tracing::error!("failed to clone repo: {}", e);
|
||||
anyhow::bail!(e)
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::error!("failed to clone repo: {}", e);
|
||||
anyhow::bail!(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ use crate::{
|
||||
app::App,
|
||||
cache::CacheApp,
|
||||
components::inline_command::InlineCommand,
|
||||
custom_command::CustomCommandApp,
|
||||
fuzzy_matcher::{FuzzyMatcher, FuzzyMatcherApp},
|
||||
git_clone::GitCloneApp,
|
||||
git_provider::Repository,
|
||||
@ -111,22 +110,12 @@ impl RootCommand {
|
||||
} else {
|
||||
eprintln!("cloning repository...");
|
||||
git_clone.clone_repo(&repo, force_refresh).await?;
|
||||
|
||||
self.app
|
||||
.custom_command()
|
||||
.execute_post_clone_command(&project_path)
|
||||
.await?;
|
||||
}
|
||||
} else {
|
||||
tracing::info!("skipping clone for repo: {}", &repo.to_rel_path().display());
|
||||
}
|
||||
} else {
|
||||
tracing::info!("repository already exists");
|
||||
|
||||
self.app
|
||||
.custom_command()
|
||||
.execute_post_update_command(&project_path)
|
||||
.await?;
|
||||
}
|
||||
|
||||
if shell {
|
||||
|
@ -23,7 +23,7 @@ impl<'a> Spinner<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl StatefulWidget for Spinner<'_> {
|
||||
impl<'a> StatefulWidget for Spinner<'a> {
|
||||
type State = SpinnerState;
|
||||
|
||||
fn render(
|
||||
|
@ -3,7 +3,7 @@ use std::path::{Path, PathBuf};
|
||||
use anyhow::Context;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct Config {
|
||||
#[serde(default)]
|
||||
pub settings: Settings,
|
||||
@ -12,51 +12,16 @@ pub struct Config {
|
||||
pub providers: Providers,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Clone)]
|
||||
#[derive(Debug, Default, Serialize, Deserialize, PartialEq)]
|
||||
pub struct Settings {
|
||||
#[serde(default)]
|
||||
pub projects: Projects,
|
||||
|
||||
#[serde(default)]
|
||||
pub cache: Cache,
|
||||
|
||||
pub post_clone_command: Option<PostCloneCommand>,
|
||||
pub post_update_command: Option<PostUpdateCommand>,
|
||||
}
|
||||
|
||||
#[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)]
|
||||
#[derive(Debug, Default, Serialize, Deserialize, PartialEq)]
|
||||
pub struct Projects {
|
||||
pub directory: ProjectLocation,
|
||||
}
|
||||
@ -92,7 +57,7 @@ impl std::ops::Deref for ProjectLocation {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Clone)]
|
||||
#[derive(Debug, Default, Serialize, Deserialize, PartialEq)]
|
||||
pub struct Cache {
|
||||
#[serde(default)]
|
||||
pub location: CacheLocation,
|
||||
@ -124,7 +89,7 @@ impl Default for CacheLocation {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(untagged)]
|
||||
pub enum CacheDuration {
|
||||
Enabled(bool),
|
||||
@ -166,7 +131,7 @@ impl Default for CacheDuration {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Clone)]
|
||||
#[derive(Debug, Default, Serialize, Deserialize, PartialEq)]
|
||||
pub struct Providers {
|
||||
#[serde(default)]
|
||||
pub github: Vec<GitHub>,
|
||||
@ -174,7 +139,7 @@ pub struct Providers {
|
||||
pub gitea: Vec<Gitea>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct GitHub {
|
||||
#[serde(default)]
|
||||
pub url: Option<String>,
|
||||
@ -190,7 +155,7 @@ pub struct GitHub {
|
||||
pub organisations: Vec<GitHubOrganisation>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct GitHubUser(String);
|
||||
|
||||
impl From<GitHubUser> for String {
|
||||
@ -205,7 +170,7 @@ impl<'a> From<&'a GitHubUser> for &'a str {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct GitHubOrganisation(String);
|
||||
|
||||
impl From<GitHubOrganisation> for String {
|
||||
@ -220,7 +185,7 @@ impl<'a> From<&'a GitHubOrganisation> for &'a str {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct Gitea {
|
||||
pub url: String,
|
||||
|
||||
@ -236,21 +201,21 @@ pub struct Gitea {
|
||||
pub organisations: Vec<GiteaOrganisation>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(untagged)]
|
||||
pub enum GiteaAccessToken {
|
||||
Direct(String),
|
||||
Env { env: String },
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(untagged)]
|
||||
pub enum GitHubAccessToken {
|
||||
Direct(String),
|
||||
Env { env: String },
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct GiteaUser(String);
|
||||
|
||||
impl From<GiteaUser> for String {
|
||||
@ -265,7 +230,7 @@ impl<'a> From<&'a GiteaUser> for &'a str {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct GiteaOrganisation(String);
|
||||
impl From<GiteaOrganisation> for String {
|
||||
fn from(value: GiteaOrganisation) -> Self {
|
||||
@ -396,9 +361,7 @@ mod test {
|
||||
},
|
||||
projects: Projects {
|
||||
directory: PathBuf::from("git").into()
|
||||
},
|
||||
post_update_command: None,
|
||||
post_clone_command: None
|
||||
}
|
||||
}
|
||||
},
|
||||
config
|
||||
@ -423,9 +386,7 @@ mod test {
|
||||
},
|
||||
settings: Settings {
|
||||
cache: Cache::default(),
|
||||
projects: Projects::default(),
|
||||
post_update_command: None,
|
||||
post_clone_command: None
|
||||
projects: Projects::default()
|
||||
}
|
||||
},
|
||||
config
|
||||
|
@ -1,83 +0,0 @@
|
||||
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())
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@ use std::path::PathBuf;
|
||||
|
||||
use anyhow::Context;
|
||||
use clap::{Parser, Subcommand};
|
||||
use commands::{clone::CloneCommand, root::RootCommand, shell::Shell, update::Update};
|
||||
use commands::{root::RootCommand, shell::Shell, update::Update};
|
||||
use config::Config;
|
||||
use tracing::level_filters::LevelFilter;
|
||||
use tracing_subscriber::EnvFilter;
|
||||
@ -15,7 +15,6 @@ mod cache_codec;
|
||||
mod commands;
|
||||
mod components;
|
||||
mod config;
|
||||
mod custom_command;
|
||||
mod fuzzy_matcher;
|
||||
mod git_clone;
|
||||
mod git_provider;
|
||||
@ -52,14 +51,13 @@ struct Command {
|
||||
enum Commands {
|
||||
Init(Shell),
|
||||
Update(Update),
|
||||
Clone(CloneCommand),
|
||||
}
|
||||
|
||||
const DEFAULT_CONFIG_PATH: &str = ".config/gitnow/gitnow.toml";
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
dotenvy::dotenv().ok();
|
||||
dotenv::dotenv().ok();
|
||||
tracing_subscriber::fmt()
|
||||
.with_env_filter(
|
||||
EnvFilter::builder()
|
||||
@ -90,9 +88,6 @@ async fn main() -> anyhow::Result<()> {
|
||||
Commands::Update(mut update) => {
|
||||
update.execute(app).await?;
|
||||
}
|
||||
Commands::Clone(mut clone) => {
|
||||
clone.execute(app).await?;
|
||||
}
|
||||
},
|
||||
None => {
|
||||
RootCommand::new(app)
|
||||
|
@ -1,31 +1,7 @@
|
||||
[settings]
|
||||
# Runs after a project is fetched for the first time, either a single string, or multiple in an array
|
||||
post_clone_command = "jj git init --colocate"
|
||||
# Runs after a project is jumped to if it already exists.
|
||||
post_update_command = ["git pull", "jj git fetch"]
|
||||
|
||||
[[providers.github]]
|
||||
# Who is the user running the clone command
|
||||
current_user = "kjuulh"
|
||||
# How to authenticate to github
|
||||
access_token = { env = "GITHUB_ACCESS_TOKEN" }
|
||||
|
||||
# Which users to index
|
||||
users = ["kjuulh"]
|
||||
# Which organisations to index
|
||||
organisations = ["lunarway"]
|
||||
|
||||
[[providers.gitea]]
|
||||
# WHich gitea instance to authenticate against
|
||||
url = "https://git.front.kjuulh.io/api/v1"
|
||||
|
||||
# How to authenticate to gitea
|
||||
current_user = "kjuulh"
|
||||
|
||||
# WHich token to use to authenticate
|
||||
access_token = { env = "GITEA_ACCESS_TOKEN" }
|
||||
|
||||
# Which users to index
|
||||
users = ["kjuulh"]
|
||||
# Which organisations to index
|
||||
organisation = ["noorgplease"]
|
||||
|
Loading…
x
Reference in New Issue
Block a user