Skip to content

Commit

Permalink
feat: added alias and update functionallities
Browse files Browse the repository at this point in the history
  • Loading branch information
Yingrjimsch committed Sep 9, 2024
1 parent 05da9f9 commit 6d8cfe8
Show file tree
Hide file tree
Showing 9 changed files with 661 additions and 21 deletions.
442 changes: 439 additions & 3 deletions Cargo.lock

Large diffs are not rendered by default.

7 changes: 6 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "grgry"
version = "1.0.2"
version = "1.0.3"
authors = ["[email protected]"]
repository = "https://github.com/yingrjimsch/grgry"
keywords = ["git", "mass", "regex", "repository-management"]
Expand Down Expand Up @@ -28,3 +28,8 @@ dirs = "5.0"
futures = "0.3"
colored = "2"
ignore = "0.4"
sys-info = "0.9.1"
flate2 = "1.0"
tar = "0.4"
self-replace = "1.5.0"
zip = "0.6"
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,13 @@ Contributions, improvements, and feature requests are welcome! Keep in mind that
## Tasks in progress
- [ ] Release pipeline for cargo
- [ ] Change the command about and long about to something clearer
- [ ] Create submodules to structure the code / refactoring
- [x] <del>Create submodules to structure the code / refactoring<del>
- [x] <del>`grgry show` with parameter `--all` showing the current activated profile or all profiles</del>
- [ ] add option to `pull --rebase` in `grgry quick`
- [ ] add an option to `--ammend` commits
- [x] <del>open issue with walkdir to stop at .git folder instead of traversing into the folder (speedup)</del>
- [ ] try to work with https://github.blog/open-source/git/get-up-to-speed-with-partial-clone-and-shallow-clone/ to speeden up the git clone
- [ ] quick default en mass instead of default singular
- [ ] try to work with https://github.blog/open-source/git/get-up-to-speed-with-partial-clone-and-shallow-clone/ to speeden up the git clone (minimal speedup)
- [x] <del>quick default en mass instead of default singular<del>
- [ ] try https://statics.teams.cdn.office.net/evergreen-assets/safelinks/1/atp-safelinks.html for a `grgry update` functionallity
- [ ] check if git is installed (if not assert break)
- [ ]
10 changes: 8 additions & 2 deletions src/cli/clone.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use std::{ops::ControlFlow, path::Path, process::{Command, Stdio}, sync::Arc};
use inquire::length;
use std::{ops::ControlFlow, path::Path, process::{Command, Stdio}, sync::Arc, time::Instant};

Check warning on line 1 in src/cli/clone.rs

View workflow job for this annotation

GitHub Actions / build

unused import: `time::Instant`
use regex::Regex;
use reqwest::Client;
use crate::{utils::cmd::{create_git_cmd, run_cmd_o, run_cmd_o_soft, run_cmd_s}, git_api::git_providers::{get_provider, GitProvider, Repo}, config::config::{Config, Profile}, utils::helper::{self, prntln, run_in_threads_default, MessageType}};

pub async fn clone(directory: &str, user: bool, branch: String, regex: &str, reverse: bool, dry_run: bool, config: Config, client: Arc<Client>
) {
// let now = Instant::now();
let active_profile: Profile = config.active_profile().clone();
let pat: Option<String> = Some(active_profile.clone().token);
let provider_type: &str = &active_profile.provider;
Expand All @@ -18,6 +18,9 @@ pub async fn clone(directory: &str, user: bool, branch: String, regex: &str, rev
.into_iter()
.filter(|repo: &Box<dyn Repo>| (re.is_match(&repo.http_url()) || re.is_match(&repo.ssh_url())) ^ reverse)
.collect();
// let elapsed = now.elapsed();
// println!("Elapsed: {:.2?}", elapsed);

prntln(&format!("\nCloning {} repositories from {}", repos_to_clone.len(), active_profile.baseaddress), MessageType::Neutral);
run_in_threads_default(
repos_to_clone,
Expand Down Expand Up @@ -48,6 +51,9 @@ pub async fn clone(directory: &str, user: bool, branch: String, regex: &str, rev
}
},
);

// let elapsed = now.elapsed();
// println!("Elapsed: {:.2?}", elapsed);
prntln("\n\nFinished to clone repositories", MessageType::Success);
}

Expand Down
12 changes: 10 additions & 2 deletions src/cli/commands.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use clap::Subcommand;
use clap::{Parser, Subcommand};

Check warning on line 1 in src/cli/commands.rs

View workflow job for this annotation

GitHub Actions / build

unused import: `Parser`

#[derive(Subcommand)]
pub enum Commands {
Expand Down Expand Up @@ -99,7 +99,15 @@ pub enum Commands {
#[clap(subcommand)]
sub: ProfileCommands,
},
// Test
#[command(about = "EXPERIMENTAL: add this as git alias to simply use git for mass commands.")]
Alias {
// This will collect all trailing arguments that are not part of options like `regex_args`.
#[arg(trailing_var_arg = true, value_name = "COMMAND", required = true, help = "This is the command to execute as if it was a git command without git prefix.")]
command: Vec<String>,
},
#[command(about = "EXPERIMENTAL: Update the grgry version in itself simply by calling grgry update.")]
Update,
Test,
}

#[derive(Subcommand)]
Expand Down
4 changes: 1 addition & 3 deletions src/cli/mass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ use walkdir::WalkDir;
use crate::{utils::cmd::{create_git_cmd, run_cmd_s}, utils::helper::{prntln, MessageType}};

pub fn mass(command: &str, regex: &str, reverse: bool, skip_interactive: bool, dry_run: bool) {
// let repos = find_git_repos_parallel(None, regex, reverse);

process_repos(
regex,
reverse,
Expand All @@ -32,7 +30,7 @@ pub fn mass(command: &str, regex: &str, reverse: bool, skip_interactive: bool, d
}
},
|repo| {
let repo_path = repo.to_string_lossy();
let repo_path: std::borrow::Cow<'_, str> = repo.to_string_lossy();
let args: Vec<&str> = ["-C", &repo_path]
.iter()
.copied()
Expand Down
1 change: 1 addition & 0 deletions src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pub mod commands;
pub mod profile;
pub mod mass;
pub mod quick;
pub mod update;

pub use clone::clone as clone;
pub use mass::mass as mass;
Expand Down
136 changes: 136 additions & 0 deletions src/cli/update.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
use colored::Colorize;
use rayon::str::Bytes;

Check warning on line 2 in src/cli/update.rs

View workflow job for this annotation

GitHub Actions / build

unused import: `rayon::str::Bytes`
use reqwest::Client;
use serde::Deserialize;
use zip::ZipArchive;

Check warning on line 5 in src/cli/update.rs

View workflow job for this annotation

GitHub Actions / build

unused import: `zip::ZipArchive`
use std::error::Error;
use std::fs::File;

Check warning on line 7 in src/cli/update.rs

View workflow job for this annotation

GitHub Actions / build

unused import: `std::fs::File`
use std::io::{self, copy, Seek};

Check warning on line 8 in src/cli/update.rs

View workflow job for this annotation

GitHub Actions / build

unused import: `copy`
use std::path::{Path, PathBuf};
use std::{env, fs};

Check warning on line 10 in src/cli/update.rs

View workflow job for this annotation

GitHub Actions / build

unused import: `fs`
use flate2::read::GzDecoder;
use tar::Archive;
use sys_info;
use tokio;

Check warning on line 14 in src/cli/update.rs

View workflow job for this annotation

GitHub Actions / build

unused import: `tokio`

#[derive(Debug, Deserialize)]
struct Asset {
browser_download_url: String,
name: String,
}

#[derive(Debug, Deserialize)]
struct Release {
assets: Vec<Asset>,
}

async fn determine_asset_pattern() -> Result<(String, String), Box<dyn Error>> {
let os = sys_info::os_type()?;
let arch = std::env::consts::ARCH;
println!("{} {}", os, arch);
let os_str = match os.as_str() {

Check warning on line 31 in src/cli/update.rs

View workflow job for this annotation

GitHub Actions / build

unused variable: `os_str`
"Linux" => "linux-gnu",
"Darwin" => "apple-darwin",
"Windows" => "pc-windows-msvc",
_ => return Err("Unsupported OS".into()),
};

let arch_str = match arch {

Check warning on line 38 in src/cli/update.rs

View workflow job for this annotation

GitHub Actions / build

unused variable: `arch_str`
"x86_64" => "x86_64",
"aarch64" => "aarch64",
_ => return Err("Unsupported architecture".into()),
};

// Ok((arch_str.to_string(), os_str.to_string()))
Ok(("x86_64".to_string(), "pc-windows-msvc".to_string()))
}

pub async fn download_latest_release() -> Result<(), Box<dyn Error>> {
let client = Client::new();

let api_url = format!("https://api.github.com/repos/Yingrjimsch/grgry/releases/latest");
let response = client
.get(&api_url)
.header("User-Agent", "grgry")
.send()
.await?
.json::<Release>()
.await?;

let asset_pattern: (String, String) = determine_asset_pattern().await?;
let matching_asset = response.assets.iter()
.find(|asset| asset.name.to_lowercase().contains(&asset_pattern.0.to_lowercase()) && asset.name.to_lowercase().contains(&asset_pattern.1.to_lowercase()))
.ok_or("No matching asset found for the current system".red())?;

let download_url = &matching_asset.browser_download_url;
let tmp_dir = env::temp_dir();

println!("Downloading from: {}", download_url);
let response = client
.get(download_url)
.header("User-Agent", "grgry")
.send()
.await?
.error_for_status()?
.bytes()
.await?;

extract(io::Cursor::new(response), &tmp_dir)?;
let binary_file_name = tmp_dir.join("grgry");
self_replace::self_replace(&binary_file_name)?;


Ok(())
}

#[cfg(target_family = "unix")]
fn extract<R: io::Read + Seek>(reader: R, target_dir: &Path) -> Result<(), Box<dyn std::error::Error>> {
let tar = GzDecoder::new(reader);
let mut archive = Archive::new(tar);

for entry in archive.entries()? {
let mut entry = entry?;
let entry_path = entry.path()?.to_path_buf();
let file_name = entry_path.file_name().ok_or("Failed to get file name")?;
let target_path: PathBuf = target_dir.join(file_name);

if file_name.to_string_lossy() != "grgry" {
continue;
}

println!("Unpacking into {}", target_path.display());

let _ = entry.unpack(&target_path);
}

Ok(())
}

#[cfg(target_family = "windows")]
fn extract<R: io::Read + Seek>(reader: R, target_dir: &Path) -> Result<(), Box<dyn std::error::Error>> {
let mut archive = ZipArchive::new(reader)?;

for i in 0..archive.len() {
let mut entry = archive.by_index(i)?;
let entry_path = entry.mangled_name();
let file_name = entry_path.file_name().ok_or("Failed to get file name")?;
let target_path: PathBuf = target_dir.join(file_name);

println!("{:?}", entry_path);
if file_name.to_string_lossy() != "grgry.exe" {
continue;
}

// Construct target path
// let target_path = target_dir.join(entry_path.strip_prefix("/")?);

// Print paths for debugging
println!("Unpacking {} to {:?}", entry.name(), target_path);

// Unpack the entry
let mut outfile = File::create(&target_path)?;
copy(&mut entry, &mut outfile)?;
}

Ok(())
}
62 changes: 55 additions & 7 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::sync::Arc;
use std::{process::Command, sync::Arc};

use clap::Parser;
use grgry::{cli::{clone, commands::{Commands, ProfileCommands}, mass, profile::{activate_profile_prompt, add_profile_prompt, delete_profile_prompt, show_profile}, quick}, config::config::Config};
use clap::{CommandFactory, Parser};
use grgry::{cli::{clone, commands::{Commands, ProfileCommands}, mass, profile::{activate_profile_prompt, add_profile_prompt, delete_profile_prompt, show_profile}, quick, update::download_latest_release}, config::config::Config, utils::cmd::{run_cmd_o, run_cmd_o_soft}};
use reqwest::Client;

#[derive(Parser)]
Expand Down Expand Up @@ -59,6 +59,7 @@ async fn main() {
skip_interactive,
dry_run,
} => {
println!("Stuck3");
let (regex, reverse) = regex_args.get_regex_args(".*");
mass(command, &regex, reverse, *skip_interactive, *dry_run)
}
Expand All @@ -68,9 +69,56 @@ async fn main() {
ProfileCommands::Delete => delete_profile_prompt(&mut config),
ProfileCommands::Show { all }=> show_profile(*all, config),
},
// Commands::Test { } => {
// // let reops = find_git_repos_parallel(None, ".*", false);
// // println!("{:?}", reops);
// }
Commands::Alias { command } => {
let mut command_vec: Vec<String> = vec!["grgry".to_string(), "mass".to_string()];
let mut mass_command: String = String::new();
let mut command = command.into_iter();
while let Some(arg) = command.next() {
match arg.as_str() {
// Check for recognized arguments
"-s" | "--skip-interactive" | "--dry-run" => {
command_vec.push(arg.to_string())
},
"--regex" | "--rev-regex" => {
command_vec.push(arg.to_string());
let regex_arg = command.next();
match regex_arg {
Some(argument) => command_vec.push(argument.to_string()),
_ => {}
};
},
_ => {
if !mass_command.is_empty() {
mass_command.push(' ');
}
mass_command.push_str(&arg);
}
}
}
command_vec.insert(2, mass_command);
let cli = Cli::parse_from(command_vec);
if let Commands::Mass {
command,
regex_args,
skip_interactive,
dry_run,
} = cli.command
{
let (regex, reverse) = regex_args.get_regex_args(".*");
mass(&command, &regex, reverse, skip_interactive, dry_run)
}
},
Commands::Update { } => {
match download_latest_release().await {
Ok(_) => println!("Successfully updated grgry, check new version with grgry --version."),
Err(e) => eprintln!("Error: {}", e),
}
}
Commands::Test { } => {
match download_latest_release().await {
Ok(_) => println!("Success!"),
Err(e) => eprintln!("Error: {}", e),
}
}
}
}

0 comments on commit 6d8cfe8

Please sign in to comment.