diff --git a/Cargo.lock b/Cargo.lock index 041dc1b..d9e4957 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -82,15 +82,6 @@ version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" -[[package]] -name = "arc-swap" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d03449bb8ca2cc2ef70869af31463d1ae5ccc8fa3e334b307203fbf815207e" -dependencies = [ - "rustversion", -] - [[package]] name = "async-trait" version = "0.1.89" @@ -460,15 +451,6 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5729f5117e208430e437df2f4843f5e5952997175992d1414f94c57d61e270b4" -[[package]] -name = "deranged" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" -dependencies = [ - "powerfmt", -] - [[package]] name = "diff" version = "0.1.13" @@ -523,7 +505,7 @@ dependencies = [ "libc", "option-ext", "redox_users 0.5.2", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -568,7 +550,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -780,11 +762,10 @@ dependencies = [ [[package]] name = "gitea-client" -version = "1.25.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8d7ee6af6705c898e79464b74e6f6a6b6271baa6b015e34383b7687d21d8dbb" +version = "1.25.2+0.1.0" dependencies = [ "reqwest", + "rustls", "serde", "serde_json", "serde_repr", @@ -809,12 +790,13 @@ dependencies = [ "gitea-client", "minijinja", "nucleo-matcher", - "octocrab", "pretty_assertions", "prost", "prost-types", "ratatui", "regex", + "reqwest", + "rustls", "serde", "serde_json", "shell-words", @@ -925,28 +907,13 @@ dependencies = [ "http", "hyper", "hyper-util", - "log", "rustls", - "rustls-native-certs", "rustls-pki-types", "tokio", "tokio-rustls", "tower-service", ] -[[package]] -name = "hyper-timeout" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" -dependencies = [ - "hyper", - "hyper-util", - "pin-project-lite", - "tokio", - "tower-service", -] - [[package]] name = "hyper-util" version = "0.1.19" @@ -1223,21 +1190,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "jsonwebtoken" -version = "9.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a87cc7a48537badeae96744432de36f4be2b4a34a05a5ef32e9dd8a1c169dde" -dependencies = [ - "base64 0.22.1", - "js-sys", - "pem", - "ring", - "serde", - "serde_json", - "simple_asn1", -] - [[package]] name = "lab" version = "0.11.0" @@ -1448,7 +1400,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -1461,22 +1413,6 @@ dependencies = [ "unicode-segmentation", ] -[[package]] -name = "num-bigint" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" -dependencies = [ - "num-integer", - "num-traits", -] - -[[package]] -name = "num-conv" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" - [[package]] name = "num-derive" version = "0.3.3" @@ -1499,15 +1435,6 @@ dependencies = [ "syn 2.0.114", ] -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - [[package]] name = "num-traits" version = "0.2.19" @@ -1517,46 +1444,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "octocrab" -version = "0.43.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27527d68322f4c603319f7958973db8f9fa4be62c0e3fafe084f5562cf6353df" -dependencies = [ - "arc-swap", - "async-trait", - "base64 0.22.1", - "bytes", - "cfg-if", - "chrono", - "either", - "futures", - "futures-util", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-timeout", - "hyper-util", - "jsonwebtoken", - "once_cell", - "percent-encoding", - "pin-project", - "secrecy", - "serde", - "serde_json", - "serde_path_to_error", - "serde_urlencoded", - "snafu", - "tokio", - "tower", - "tower-http", - "tracing", - "url", - "web-time", -] - [[package]] name = "once_cell" version = "1.21.3" @@ -1619,16 +1506,6 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" -[[package]] -name = "pem" -version = "3.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" -dependencies = [ - "base64 0.22.1", - "serde_core", -] - [[package]] name = "percent-encoding" version = "2.3.2" @@ -1730,26 +1607,6 @@ dependencies = [ "siphasher 1.0.2", ] -[[package]] -name = "pin-project" -version = "1.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.114", -] - [[package]] name = "pin-project-lite" version = "0.2.16" @@ -1771,12 +1628,6 @@ dependencies = [ "zerovec", ] -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - [[package]] name = "ppv-lite86" version = "0.2.21" @@ -1859,9 +1710,9 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.13" +version = "0.11.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" +checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" dependencies = [ "aws-lc-rs", "bytes", @@ -1890,7 +1741,7 @@ dependencies = [ "once_cell", "socket2", "tracing", - "windows-sys 0.60.2", + "windows-sys 0.59.0", ] [[package]] @@ -2074,6 +1925,7 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", + "webpki-roots", ] [[package]] @@ -2119,7 +1971,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.11.0", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -2178,7 +2030,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -2235,15 +2087,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "secrecy" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e891af845473308773346dc847b2c23ee78fe442e0472ac50e22a18a93d3ae5a" -dependencies = [ - "zeroize", -] - [[package]] name = "security-framework" version = "3.5.1" @@ -2328,17 +2171,6 @@ dependencies = [ "zmij", ] -[[package]] -name = "serde_path_to_error" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" -dependencies = [ - "itoa", - "serde", - "serde_core", -] - [[package]] name = "serde_repr" version = "0.1.20" @@ -2434,18 +2266,6 @@ dependencies = [ "libc", ] -[[package]] -name = "simple_asn1" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb" -dependencies = [ - "num-bigint", - "num-traits", - "thiserror 2.0.18", - "time", -] - [[package]] name = "siphasher" version = "0.3.11" @@ -2470,27 +2290,6 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" -[[package]] -name = "snafu" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e84b3f4eacbf3a1ce05eac6763b4d629d60cbc94d632e4092c54ade71f1e1a2" -dependencies = [ - "snafu-derive", -] - -[[package]] -name = "snafu-derive" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1c97747dbf44bb1ca44a561ece23508e99cb592e862f22222dcf42f51d1e451" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.114", -] - [[package]] name = "socket2" version = "0.6.2" @@ -2599,7 +2398,7 @@ dependencies = [ "getrandom 0.3.4", "once_cell", "rustix 1.1.3", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -2771,37 +2570,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "time" -version = "0.3.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9da98b7d9b7dad93488a84b8248efc35352b0b2657397d4167e7ad67e5d535e5" -dependencies = [ - "deranged", - "itoa", - "num-conv", - "powerfmt", - "serde_core", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" - -[[package]] -name = "time-macros" -version = "0.2.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78cc610bac2dcee56805c99642447d4c5dbde4d01f752ffea0199aee1f601dc4" -dependencies = [ - "num-conv", - "time-core", -] - [[package]] name = "tinystr" version = "0.8.2" @@ -2930,10 +2698,8 @@ dependencies = [ "pin-project-lite", "sync_wrapper", "tokio", - "tokio-util", "tower-layer", "tower-service", - "tracing", ] [[package]] @@ -2952,7 +2718,6 @@ dependencies = [ "tower", "tower-layer", "tower-service", - "tracing", ] [[package]] @@ -3104,7 +2869,6 @@ dependencies = [ "idna", "percent-encoding", "serde", - "serde_derive", ] [[package]] @@ -3275,7 +3039,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" dependencies = [ "js-sys", - "serde", "wasm-bindgen", ] @@ -3288,6 +3051,15 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "webpki-roots" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "wezterm-bidi" version = "0.2.3" @@ -3382,7 +3154,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] diff --git a/crates/gitnow/Cargo.toml b/crates/gitnow/Cargo.toml index 69fa195..e9d7ae4 100644 --- a/crates/gitnow/Cargo.toml +++ b/crates/gitnow/Cargo.toml @@ -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" diff --git a/crates/gitnow/src/git_provider/github.rs b/crates/gitnow/src/git_provider/github.rs index 7091fed..65a52ba 100644 --- a/crates/gitnow/src/git_provider/github.rs +++ b/crates/gitnow/src/git_provider/github.rs @@ -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, + ssh_url: Option, +} + +#[derive(Deserialize)] +struct GitHubOwner { + login: String, +} + pub struct GitHubProvider { #[allow(dead_code)] app: &'static App, @@ -19,30 +32,17 @@ impl GitHubProvider { ) -> anyhow::Result> { 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 = 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> { 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 = 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 = 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> { + 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 = resp.json().await?; + repos.extend(page); + } + + Ok(repos) + } + + fn to_repositories( + &self, + url: Option<&String>, + repos: Vec, + ) -> Vec { + 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, - ) -> anyhow::Result> { - 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 { - 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 { + 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 { + 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; }