forked from mirrors/kingfisher
163 lines
5.1 KiB
Rust
163 lines
5.1 KiB
Rust
use std::{env, sync::Arc, time::Duration};
|
|
|
|
use anyhow::{Context, Result};
|
|
use indicatif::{ProgressBar, ProgressStyle};
|
|
use octorust::{
|
|
auth::Credentials,
|
|
types::{Order, ReposListOrgSort, ReposListOrgType, ReposListUserType},
|
|
Client,
|
|
};
|
|
use url::Url;
|
|
|
|
#[derive(Debug)]
|
|
pub struct RepoSpecifiers {
|
|
pub user: Vec<String>,
|
|
pub organization: Vec<String>,
|
|
pub all_organizations: bool,
|
|
pub repo_filter: RepoType,
|
|
}
|
|
impl RepoSpecifiers {
|
|
pub fn is_empty(&self) -> bool {
|
|
self.user.is_empty() && self.organization.is_empty() && !self.all_organizations
|
|
}
|
|
}
|
|
#[derive(Debug, Clone)]
|
|
pub enum RepoType {
|
|
All,
|
|
Source,
|
|
Fork,
|
|
}
|
|
impl From<RepoType> for ReposListUserType {
|
|
fn from(repo_type: RepoType) -> Self {
|
|
match repo_type {
|
|
RepoType::All => ReposListUserType::All,
|
|
RepoType::Source => ReposListUserType::Owner,
|
|
RepoType::Fork => ReposListUserType::Member,
|
|
}
|
|
}
|
|
}
|
|
impl From<RepoType> for ReposListOrgType {
|
|
fn from(repo_type: RepoType) -> Self {
|
|
match repo_type {
|
|
RepoType::All => ReposListOrgType::All,
|
|
RepoType::Source => ReposListOrgType::Sources,
|
|
RepoType::Fork => ReposListOrgType::Forks,
|
|
}
|
|
}
|
|
}
|
|
fn create_github_client(github_url: &url::Url, ignore_certs: bool) -> Result<Arc<Client>> {
|
|
// Try personal access token
|
|
let credentials = if let Ok(token) = env::var("KF_GITHUB_TOKEN") {
|
|
Credentials::Token(token)
|
|
} else {
|
|
Credentials::Token("".to_string()) // Anonymous access
|
|
};
|
|
|
|
let mut client_builder = reqwest::Client::builder();
|
|
if ignore_certs {
|
|
client_builder = client_builder.danger_accept_invalid_certs(ignore_certs);
|
|
}
|
|
|
|
let reqwest_client = client_builder.build().context("Failed to build HTTP client")?;
|
|
|
|
let http_client = reqwest_middleware::ClientBuilder::new(reqwest_client).build();
|
|
|
|
let mut client = Client::custom(
|
|
concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION")),
|
|
credentials,
|
|
http_client,
|
|
);
|
|
|
|
// Override host if not using api.github.com
|
|
if github_url.host_str() != Some("api.github.com") {
|
|
client.with_host_override(github_url.as_str());
|
|
}
|
|
Ok(Arc::new(client))
|
|
}
|
|
pub async fn enumerate_repo_urls(
|
|
repo_specifiers: &RepoSpecifiers,
|
|
github_url: url::Url,
|
|
ignore_certs: bool,
|
|
mut progress: Option<&mut ProgressBar>,
|
|
) -> Result<Vec<String>> {
|
|
let client = create_github_client(&github_url, ignore_certs)?;
|
|
let mut repo_urls = Vec::new();
|
|
let user_repo_type: ReposListUserType = repo_specifiers.repo_filter.clone().into();
|
|
let org_repo_type: ReposListOrgType = repo_specifiers.repo_filter.clone().into();
|
|
for username in &repo_specifiers.user {
|
|
let repos = client
|
|
.repos()
|
|
.list_all_for_user(
|
|
username,
|
|
user_repo_type.clone(),
|
|
ReposListOrgSort::Created,
|
|
Order::Desc,
|
|
)
|
|
.await?;
|
|
repo_urls.extend(repos.body.into_iter().filter_map(|repo| Some(repo.clone_url)));
|
|
if let Some(progress) = progress.as_mut() {
|
|
progress.inc(1);
|
|
}
|
|
}
|
|
let orgs = if repo_specifiers.all_organizations {
|
|
let mut all_orgs = Vec::new();
|
|
let org_list = client.orgs().list_all(100).await?;
|
|
all_orgs.extend(org_list.body.into_iter().map(|org| org.login));
|
|
all_orgs
|
|
} else {
|
|
repo_specifiers.organization.clone()
|
|
};
|
|
for org_name in orgs {
|
|
let repos = client
|
|
.repos()
|
|
.list_all_for_org(
|
|
&org_name,
|
|
org_repo_type.clone(),
|
|
ReposListOrgSort::Created,
|
|
Order::Desc,
|
|
)
|
|
.await?;
|
|
repo_urls.extend(repos.body.into_iter().filter_map(|repo| Some(repo.clone_url)));
|
|
if let Some(progress) = progress.as_mut() {
|
|
progress.inc(1);
|
|
}
|
|
}
|
|
repo_urls.sort();
|
|
repo_urls.dedup();
|
|
Ok(repo_urls)
|
|
}
|
|
pub async fn list_repositories(
|
|
api_url: Url,
|
|
ignore_certs: bool,
|
|
progress_enabled: bool,
|
|
users: &[String],
|
|
orgs: &[String],
|
|
all_orgs: bool,
|
|
repo_filter: RepoType,
|
|
) -> Result<()> {
|
|
let repo_specifiers = RepoSpecifiers {
|
|
user: users.to_vec(),
|
|
organization: orgs.to_vec(),
|
|
all_organizations: all_orgs,
|
|
repo_filter,
|
|
};
|
|
// Create a progress bar just for displaying status
|
|
// let mut progress = ProgressBar::new_spinner("Fetching repositories...",
|
|
// true,);
|
|
let mut progress = if progress_enabled {
|
|
let style = ProgressStyle::with_template("{spinner} {msg} [{elapsed_precise}]")
|
|
.expect("progress bar style template should compile");
|
|
let pb = ProgressBar::new_spinner().with_style(style).with_message("Fetching repositories");
|
|
pb.enable_steady_tick(Duration::from_millis(500));
|
|
pb
|
|
} else {
|
|
ProgressBar::hidden()
|
|
};
|
|
let repo_urls =
|
|
enumerate_repo_urls(&repo_specifiers, api_url, ignore_certs, Some(&mut progress)).await?;
|
|
// Print repositories
|
|
for url in repo_urls {
|
|
println!("{}", url);
|
|
}
|
|
Ok(())
|
|
}
|