diff --git a/README.md b/README.md index 58a3c11e457de8c8b5d0db8287b15ebbb912834f..a6496d81151c285b28e079519a396514a48ee925 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ gitrepoman github pop-os clone gitrepoman github pop-os pull gitrepoman github pop-os checkout -gitrepoman gitlab gitlab.redox-os.org clone -gitrepoman gitlab gitlab.redox-os.org pull -gitrepoman gitlab gitlab.redox-os.org checkout +gitrepoman gitlab gitlab.redox-os.org redox-os clone +gitrepoman gitlab gitlab.redox-os.org redox-os pull +gitrepoman gitlab gitlab.redox-os.org redox-os checkout ``` diff --git a/src/actions.rs b/src/actions.rs index b49ff5a9447a50eb7a3c94248ed567fdf239415e..9343b842e0670956fcf12b18c20dc51cfa1285fd 100644 --- a/src/actions.rs +++ b/src/actions.rs @@ -1,6 +1,6 @@ use std::io; use std::path::Path; -use std::process::Command; +use std::process::{Command, Stdio}; use rayon::prelude::*; #[derive(Debug, Deserialize, Eq, Hash)] @@ -8,6 +8,7 @@ pub struct Repo { pub name: String, pub html_url: String, pub ssh_url: String, + pub namespace: String, } impl Repo { @@ -35,7 +36,7 @@ pub enum GitError { } fn git_cmd(args: &[&str], name: &str) -> Result<String, (String, GitError)> { - match Command::new("git").args(args).status() { + match Command::new("git").args(args).stdout(Stdio::null()).stderr(Stdio::null()).status() { Ok(status) => if status.success() { Ok(name.to_owned()) } else { @@ -48,16 +49,20 @@ fn git_cmd(args: &[&str], name: &str) -> Result<String, (String, GitError)> { pub trait GitAction { fn get_repos(&self) -> Vec<Repo>; - fn list(&self) { + fn list(&self, namespace: &str) { for repo in self.get_repos() { + if repo.namespace == namespace { + continue + } println!("{}: {}", repo.name, repo.ssh_url); } } - fn clone(&self, flags: u8) { + fn clone(&self, flags: u8, namespace: &str) { let results = self.get_repos() .par_iter() - .inspect(|repo| println!("cloning {}", repo.name)) + .filter(|repo| namespace == "" || repo.name == namespace) + .inspect(|repo| println!("cloning {} from {}", repo.name, repo.get_url())) .map(|repo| if !Path::new(&repo.name).exists() { let url = if flags & 0b01 != 0 { repo.get_ssh_url() } else { repo.get_url() }; git_cmd(&["clone", "--recursive", url, &repo.name], &repo.name) @@ -74,10 +79,11 @@ pub trait GitAction { } } - fn pull(&self, flags: u8) { + fn pull(&self, flags: u8, namespace: &str) { let results = self.get_repos() .par_iter() - .inspect(|repo| println!("pulling {}", repo.name)) + .filter(|repo| namespace == "" || repo.name == namespace) + .inspect(|repo| println!("pulling {} from {}", repo.name, repo.get_url())) .map(|repo| if !Path::new(&repo.name).exists() { let url = if flags & 0b01 != 0 { repo.get_ssh_url() } else { repo.get_url() }; git_cmd(&["clone", "--recursive", url, &repo.name], &repo.name) @@ -96,10 +102,11 @@ pub trait GitAction { } } - fn checkout(&self, flags: u8) { + fn checkout(&self, flags: u8, namespace: &str) { let results = self.get_repos() .par_iter() - .inspect(|repo| println!("checking out {}", repo.name)) + .filter(|repo| namespace == "" || repo.name == namespace) + .inspect(|repo| println!("checking out {} from {}", repo.name, repo.get_url())) .map(|repo| if !Path::new(&repo.name).exists() { let url = if flags & 0b01 != 0 { repo.get_ssh_url() } else { repo.get_url() }; git_cmd(&["clone", "--recursive", url, &repo.name], &repo.name) diff --git a/src/github_impl.rs b/src/github_impl.rs index a00c3e44a78a8dca73956c412ad74d7f7bf71158..3e8dcbb3504d7bab66dfcd3e7ae7223112e2aa3f 100644 --- a/src/github_impl.rs +++ b/src/github_impl.rs @@ -15,18 +15,30 @@ impl GitHub { } } +#[derive(Debug, Deserialize)] +pub struct GithubRepo { + pub name: String, + pub html_url: String, + pub ssh_url: String, +} + impl GitAction for GitHub { fn get_repos(&self) -> Vec<Repo> { let mut output = HashSet::new(); for page in 0.. { let request = self.client.get() .custom_endpoint(&format!("orgs/{}/repos?page={}", self.org, page)) - .execute::<Vec<Repo>>(); + .execute::<Vec<GithubRepo>>(); if let Ok((_, _, Some(repos))) = request { if repos.len() > 0 { for repo in repos { - output.insert(repo); + output.insert(Repo { + name: repo.name, + html_url: repo.html_url, + ssh_url: repo.ssh_url, + namespace: "".into() + }); } } else { break diff --git a/src/gitlab_impl.rs b/src/gitlab_impl.rs index f83c6848c58c7531c4ba8842b849b341ec5fc80b..6d3670637f569791f115ae5cb3cad3419d71cb48 100644 --- a/src/gitlab_impl.rs +++ b/src/gitlab_impl.rs @@ -10,7 +10,8 @@ impl GitAction for Gitlab { repos.push(Repo { name: project.name, html_url: project.http_url_to_repo, - ssh_url: project.ssh_url_to_repo + ssh_url: project.ssh_url_to_repo, + namespace: project.namespace.full_path, }) } } diff --git a/src/main.rs b/src/main.rs index 1e88799839253269817c1ddfc4d7c9c689a182df..b9beb6cbefc7d7044deaa77f3b937715bb319122 100644 --- a/src/main.rs +++ b/src/main.rs @@ -34,15 +34,12 @@ fn main() { .long("force") .short("f")) .subcommand(SubCommand::with_name("gitlab") - .arg(Arg::with_name("DOMAIN") - .required(true)) - .arg(Arg::with_name("ACTION") - .required(true))) + .arg(Arg::with_name("DOMAIN").required(true)) + .arg(Arg::with_name("NAMESPACE").required(true)) + .arg(Arg::with_name("ACTION").required(true))) .subcommand(SubCommand::with_name("github") - .arg(Arg::with_name("DOMAIN") - .required(true)) - .arg(Arg::with_name("ACTION") - .required(true))) + .arg(Arg::with_name("DOMAIN").required(true)) + .arg(Arg::with_name("ACTION").required(true))) .get_matches(); let config = match Config::new() { @@ -56,57 +53,57 @@ fn main() { let flags = if matches.occurrences_of("ssh") > 0 { 0b01 } else { 0b00 } + if matches.occurrences_of("force") > 0 { 0b10 } else { 0b00 }; - let (source, org, action) = if let Some(matches) = matches.subcommand_matches("gitlab") { - (GitService::GitLab, matches.value_of("DOMAIN").unwrap(), matches.value_of("ACTION").unwrap()) + let (source, org, action, ns) = if let Some(matches) = matches.subcommand_matches("gitlab") { + ( + GitService::GitLab, + matches.value_of("DOMAIN").unwrap(), + matches.value_of("ACTION").unwrap(), + matches.value_of("NAMESPACE").unwrap_or("") + ) } else if let Some(matches) = matches.subcommand_matches("github") { - (GitService::GitHub, matches.value_of("DOMAIN").unwrap(), matches.value_of("ACTION").unwrap()) + ( + GitService::GitHub, + matches.value_of("DOMAIN").unwrap(), + matches.value_of("ACTION").unwrap(), + "" + ) } else { eprintln!("no subcommand provided"); exit(1); }; - let authenticated: Box<GitAction> = match source { - GitService::GitHub => { - let token = match config.github { + macro_rules! client { + ($name:tt, $token:expr) => {{ + let token = match $token { Some(token) => token, None => { - eprintln!("no GitHub token provided"); + eprintln!("no {} token provided", stringify!($name)); exit(1); } }; - match GitHub::new(org.to_owned(), token) { + match $name::new(org.to_owned(), token) { Ok(client) => Box::new(client), Err(why) => { eprintln!("unable to authenticate client: {}", why); exit(1); } } - }, - GitService::GitLab => { - let token = match config.gitlab { - Some(token) => token, - None => { - eprintln!("no GitLab token provided"); - exit(1); - } - }; - match Gitlab::new(org.to_owned(), token) { - Ok(client) => Box::new(client), - Err(why) => { - eprintln!("unable to authenticate client: {}", why); - exit(1); - } - } - } + + }}; + } + + let authenticated: Box<GitAction> = match source { + GitService::GitHub => client!(GitHub, config.github), + GitService::GitLab => client!(Gitlab, config.gitlab), }; match Action::from(action) { - Ok(Action::List) => authenticated.list(), - Ok(Action::Clone) => authenticated.clone(flags), - Ok(Action::Pull) => authenticated.pull(flags), - Ok(Action::Checkout) => authenticated.checkout(flags), + Ok(Action::List) => authenticated.list(ns), + Ok(Action::Clone) => authenticated.clone(flags, ns), + Ok(Action::Pull) => authenticated.pull(flags, ns), + Ok(Action::Checkout) => authenticated.checkout(flags, ns), Err(cmd) => { eprintln!("{} is not a valid command", cmd); exit(1);