diff --git a/Cargo.lock b/Cargo.lock index 04cc68a..23d81de 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -30,6 +30,15 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + [[package]] name = "allocator-api2" version = "0.2.18" @@ -320,6 +329,7 @@ dependencies = [ "dotenv", "futures", "itertools", + "regex", "reqwest", "serde", "sqlx", @@ -1240,6 +1250,35 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "regex" +version = "1.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" + [[package]] name = "reqwest" version = "0.12.3" diff --git a/crates/contractor/Cargo.toml b/crates/contractor/Cargo.toml index 6bc08c7..d80450f 100644 --- a/crates/contractor/Cargo.toml +++ b/crates/contractor/Cargo.toml @@ -19,3 +19,4 @@ tower-http = { version = "0.5.2", features = ["cors", "trace"] } futures = "0.3.30" reqwest = {version = "0.12.3", default-features = false, features = ["json", "rustls-tls"]} itertools = "0.12.1" +regex = "1.10.4" diff --git a/crates/contractor/src/main.rs b/crates/contractor/src/main.rs index cc133d7..d35415f 100644 --- a/crates/contractor/src/main.rs +++ b/crates/contractor/src/main.rs @@ -23,6 +23,9 @@ enum Commands { user: Option, #[arg(long)] org: Option>, + + #[arg(long, env = "CONTRACTOR_FILTER")] + filter: Option, }, } @@ -61,12 +64,12 @@ async fn main() -> anyhow::Result<()> { result?? } } - Some(Commands::Reconcile { user, org }) => { + Some(Commands::Reconcile { user, org, filter }) => { tracing::info!("running reconcile"); let state = SharedState::from(Arc::new(State::new().await?)); - state.reconciler().reconcile(user, org).await?; + state.reconciler().reconcile(user, org, filter).await?; } None => {} } diff --git a/crates/contractor/src/services/gitea.rs b/crates/contractor/src/services/gitea.rs index 6c6bb0b..81cb39e 100644 --- a/crates/contractor/src/services/gitea.rs +++ b/crates/contractor/src/services/gitea.rs @@ -1,4 +1,4 @@ -use std::{ops::Deref, pin::Pin, sync::Arc}; +use std::{fmt::Display, ops::Deref, pin::Pin, sync::Arc}; type DynGiteaClient = Arc; pub struct GiteaClient(DynGiteaClient); @@ -23,6 +23,12 @@ pub struct Repository { pub name: String, } +impl Display for Repository { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_fmt(format_args!("{}/{}", self.owner, self.name)) + } +} + impl TryFrom for Repository { type Error = anyhow::Error; diff --git a/crates/contractor/src/services/reconciler.rs b/crates/contractor/src/services/reconciler.rs index 77fba4a..5c0d65c 100644 --- a/crates/contractor/src/services/reconciler.rs +++ b/crates/contractor/src/services/reconciler.rs @@ -1,3 +1,4 @@ +use anyhow::Context; use futures::{stream::FuturesUnordered, StreamExt}; use itertools::Itertools; @@ -18,11 +19,38 @@ impl Reconciler { &self, user: Option, orgs: Option>, + filter: Option, ) -> anyhow::Result<()> { let repos = self.get_repos(user, orgs).await?; tracing::debug!("found repositories: {}", repos.len()); - let renovate_enabled = self.get_renovate_enabled(&repos).await?; + let filtered_repos = match filter { + Some(filter) => { + let re = regex::Regex::new(&filter).context( + "filter regex failed to compile, make sure it is valid against rust-lang/regex", + )?; + + repos + .into_iter() + .filter(|r| { + if re.is_match(&r.to_string()) { + true + } else { + tracing::trace!( + filter = &filter, + "repository: {}, didn't match filter", + r.to_string(), + ); + false + } + }) + .collect() + } + None => repos, + }; + tracing::debug!("filtered repositories: {}", filtered_repos.len()); + + let renovate_enabled = self.get_renovate_enabled(&filtered_repos).await?; tracing::debug!( "found repositories with renovate enabled: {}", renovate_enabled.len()