feat: use local package instead of octocrab
Some checks failed
Release / release (push) Failing after 2m54s
Some checks failed
Release / release (push) Failing after 2m54s
This commit is contained in:
@@ -24,9 +24,10 @@ uuid = { version = "1.7.0", features = ["v4"] }
|
||||
async-trait = "0.1.82"
|
||||
toml = "0.8.19"
|
||||
|
||||
gitea-client = { version = "1.25.2", default-features = false, features = ["rustls"] }
|
||||
gitea-client = { git = "https://git.kjuulh.io/kjuulh/gitea-rs.git", default-features = false, features = ["rustls-ring"] }
|
||||
reqwest = { version = "0.13", default-features = false, features = ["__rustls", "json", "webpki-roots"] }
|
||||
rustls = { version = "0.23", default-features = false, features = ["ring", "logging", "std", "tls12"] }
|
||||
url = "2.5.2"
|
||||
octocrab = "0.43.0"
|
||||
dirs = "6.0.0"
|
||||
prost = "0.13.2"
|
||||
prost-types = "0.13.2"
|
||||
|
||||
@@ -1,7 +1,20 @@
|
||||
use octocrab::{models::Repository, params::repos::Sort, Octocrab, Page};
|
||||
use reqwest::header::{ACCEPT, AUTHORIZATION, USER_AGENT};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{app::App, config::GitHubAccessToken};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct GitHubRepo {
|
||||
name: String,
|
||||
owner: Option<GitHubOwner>,
|
||||
ssh_url: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct GitHubOwner {
|
||||
login: String,
|
||||
}
|
||||
|
||||
pub struct GitHubProvider {
|
||||
#[allow(dead_code)]
|
||||
app: &'static App,
|
||||
@@ -19,30 +32,17 @@ impl GitHubProvider {
|
||||
) -> anyhow::Result<Vec<super::Repository>> {
|
||||
tracing::debug!("fetching github repositories for current user");
|
||||
|
||||
let client = self.get_client(url, access_token)?;
|
||||
let client = self.get_client(access_token)?;
|
||||
let base = self.api_base(url);
|
||||
|
||||
let current_page = client
|
||||
.current()
|
||||
.list_repos_for_authenticated_user()
|
||||
.type_("all")
|
||||
.per_page(100)
|
||||
.sort("full_name")
|
||||
.send()
|
||||
let repos: Vec<GitHubRepo> = self
|
||||
.paginate(
|
||||
&client,
|
||||
&format!("{base}/user/repos?type=all&sort=full_name&per_page=100"),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let repos = self.unfold_pages(client, current_page).await?;
|
||||
|
||||
Ok(repos
|
||||
.into_iter()
|
||||
.filter_map(|repo| {
|
||||
Some(super::Repository {
|
||||
provider: self.get_url(url),
|
||||
owner: repo.owner.map(|su| su.login)?,
|
||||
repo_name: repo.name,
|
||||
ssh_url: repo.ssh_url?,
|
||||
})
|
||||
})
|
||||
.collect())
|
||||
Ok(self.to_repositories(url, repos))
|
||||
}
|
||||
|
||||
pub async fn list_repositories_for_user(
|
||||
@@ -53,30 +53,17 @@ impl GitHubProvider {
|
||||
) -> anyhow::Result<Vec<super::Repository>> {
|
||||
tracing::debug!(user = user, "fetching github repositories for user");
|
||||
|
||||
let client = self.get_client(url, access_token)?;
|
||||
let client = self.get_client(access_token)?;
|
||||
let base = self.api_base(url);
|
||||
|
||||
let current_page = client
|
||||
.users(user)
|
||||
.repos()
|
||||
.r#type(octocrab::params::users::repos::Type::All)
|
||||
.sort(Sort::FullName)
|
||||
.per_page(100)
|
||||
.send()
|
||||
let repos: Vec<GitHubRepo> = self
|
||||
.paginate(
|
||||
&client,
|
||||
&format!("{base}/users/{user}/repos?type=all&sort=full_name&per_page=100"),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let repos = self.unfold_pages(client, current_page).await?;
|
||||
|
||||
Ok(repos
|
||||
.into_iter()
|
||||
.filter_map(|repo| {
|
||||
Some(super::Repository {
|
||||
provider: self.get_url(url),
|
||||
owner: repo.owner.map(|su| su.login)?,
|
||||
repo_name: repo.name,
|
||||
ssh_url: repo.ssh_url?,
|
||||
})
|
||||
})
|
||||
.collect())
|
||||
Ok(self.to_repositories(url, repos))
|
||||
}
|
||||
|
||||
pub async fn list_repositories_for_organisation(
|
||||
@@ -90,47 +77,66 @@ impl GitHubProvider {
|
||||
"fetching github repositories for organisation"
|
||||
);
|
||||
|
||||
let client = self.get_client(url, access_token)?;
|
||||
let client = self.get_client(access_token)?;
|
||||
let base = self.api_base(url);
|
||||
|
||||
let current_page = client
|
||||
.orgs(organisation)
|
||||
.list_repos()
|
||||
.repo_type(Some(octocrab::params::repos::Type::All))
|
||||
.sort(Sort::FullName)
|
||||
.per_page(100)
|
||||
.send()
|
||||
let repos: Vec<GitHubRepo> = self
|
||||
.paginate(
|
||||
&client,
|
||||
&format!("{base}/orgs/{organisation}/repos?type=all&sort=full_name&per_page=100"),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let repos = self.unfold_pages(client, current_page).await?;
|
||||
Ok(self.to_repositories(url, repos))
|
||||
}
|
||||
|
||||
Ok(repos
|
||||
async fn paginate(
|
||||
&self,
|
||||
client: &reqwest::Client,
|
||||
initial_url: &str,
|
||||
) -> anyhow::Result<Vec<GitHubRepo>> {
|
||||
let mut repos = Vec::new();
|
||||
let mut url = Some(initial_url.to_string());
|
||||
|
||||
while let Some(current_url) = url {
|
||||
let resp = client
|
||||
.get(¤t_url)
|
||||
.send()
|
||||
.await?
|
||||
.error_for_status()?;
|
||||
|
||||
url = parse_next_link(resp.headers());
|
||||
|
||||
let page: Vec<GitHubRepo> = resp.json().await?;
|
||||
repos.extend(page);
|
||||
}
|
||||
|
||||
Ok(repos)
|
||||
}
|
||||
|
||||
fn to_repositories(
|
||||
&self,
|
||||
url: Option<&String>,
|
||||
repos: Vec<GitHubRepo>,
|
||||
) -> Vec<super::Repository> {
|
||||
repos
|
||||
.into_iter()
|
||||
.filter_map(|repo| {
|
||||
Some(super::Repository {
|
||||
provider: self.get_url(url),
|
||||
owner: repo.owner.map(|su| su.login)?,
|
||||
owner: repo.owner.map(|o| o.login)?,
|
||||
repo_name: repo.name,
|
||||
ssh_url: repo.ssh_url?,
|
||||
})
|
||||
})
|
||||
.collect())
|
||||
.collect()
|
||||
}
|
||||
|
||||
async fn unfold_pages(
|
||||
&self,
|
||||
client: octocrab::Octocrab,
|
||||
page: Page<Repository>,
|
||||
) -> anyhow::Result<Vec<Repository>> {
|
||||
let mut current_page = page;
|
||||
|
||||
let mut repos = current_page.take_items();
|
||||
while let Ok(Some(mut new_page)) = client.get_page(¤t_page.next).await {
|
||||
repos.extend(new_page.take_items());
|
||||
|
||||
current_page = new_page;
|
||||
fn api_base(&self, url: Option<&String>) -> String {
|
||||
match url {
|
||||
Some(u) => format!("{u}/api/v3"),
|
||||
None => "https://api.github.com".to_string(),
|
||||
}
|
||||
|
||||
Ok(repos)
|
||||
}
|
||||
|
||||
fn get_url(&self, url: Option<&String>) -> String {
|
||||
@@ -151,15 +157,19 @@ impl GitHubProvider {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_client(
|
||||
&self,
|
||||
_url: Option<&String>,
|
||||
access_token: &GitHubAccessToken,
|
||||
) -> anyhow::Result<Octocrab> {
|
||||
let client = octocrab::Octocrab::builder()
|
||||
.personal_token(match access_token {
|
||||
GitHubAccessToken::Direct(token) => token.to_owned(),
|
||||
GitHubAccessToken::Env { env } => std::env::var(env)?,
|
||||
fn get_client(&self, access_token: &GitHubAccessToken) -> anyhow::Result<reqwest::Client> {
|
||||
let token = match access_token {
|
||||
GitHubAccessToken::Direct(token) => token.to_owned(),
|
||||
GitHubAccessToken::Env { env } => std::env::var(env)?,
|
||||
};
|
||||
|
||||
let client = reqwest::Client::builder()
|
||||
.default_headers({
|
||||
let mut headers = reqwest::header::HeaderMap::new();
|
||||
headers.insert(AUTHORIZATION, format!("token {token}").parse()?);
|
||||
headers.insert(ACCEPT, "application/vnd.github+json".parse()?);
|
||||
headers.insert(USER_AGENT, "gitnow".parse()?);
|
||||
headers
|
||||
})
|
||||
.build()?;
|
||||
|
||||
@@ -167,6 +177,18 @@ impl GitHubProvider {
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_next_link(headers: &reqwest::header::HeaderMap) -> Option<String> {
|
||||
let link = headers.get("link")?.to_str().ok()?;
|
||||
for part in link.split(',') {
|
||||
let part = part.trim();
|
||||
if part.ends_with("rel=\"next\"") {
|
||||
let url = part.split('>').next()?.trim_start_matches('<');
|
||||
return Some(url.to_string());
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub trait GitHubProviderApp {
|
||||
fn github_provider(&self) -> GitHubProvider;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user