feat: filter for those with renovate enabled
All checks were successful
continuous-integration/drone/push Build is passing

Signed-off-by: kjuulh <contact@kjuulh.io>
This commit is contained in:
Kasper Juul Hermansen 2024-04-12 16:44:32 +02:00
parent 33e6cd8a3a
commit 3709e37599
Signed by: kjuulh
GPG Key ID: 57B6E1465221F912
4 changed files with 80 additions and 4 deletions

View File

@ -22,7 +22,7 @@ enum Commands {
#[arg(long)]
user: Option<String>,
#[arg(long)]
orgs: Option<Vec<String>>,
org: Option<Vec<String>>,
},
}
@ -61,12 +61,12 @@ async fn main() -> anyhow::Result<()> {
result??
}
}
Some(Commands::Reconcile { user, orgs }) => {
Some(Commands::Reconcile { user, org }) => {
tracing::info!("running reconcile");
let state = SharedState::from(Arc::new(State::new().await?));
state.reconciler().reconcile(user, orgs).await?;
state.reconciler().reconcile(user, org).await?;
}
None => {}
}

View File

@ -72,6 +72,7 @@ impl Default for DefaultGiteaClient {
impl DefaultGiteaClient {
pub async fn fetch_user_repos(&self) -> anyhow::Result<Vec<Repository>> {
//FIXME: We should collect the pages for these queries
let client = reqwest::Client::new();
let url = format!("{}/api/v1/user/repos", self.url);
@ -114,6 +115,32 @@ impl DefaultGiteaClient {
.flat_map(Repository::try_from)
.collect())
}
async fn fetch_renovate(&self, repo: &Repository) -> anyhow::Result<Option<()>> {
let client = reqwest::Client::new();
let url = format!(
"{}/api/v1/repos/{}/{}/contents/renovate.json",
self.url, &repo.owner, &repo.name
);
tracing::trace!("calling url: {}", &url);
let response = client
.get(&url)
.header("Content-Type", "application/json")
.header("Authorization", format!("token {}", self.token))
.send()
.await?;
match response.error_for_status() {
Ok(_) => Ok(Some(())),
Err(e) => match e.status() {
Some(StatusCode::NOT_FOUND) => Ok(None),
_ => anyhow::bail!(e),
},
}
}
}
impl traits::GiteaClient for DefaultGiteaClient {
@ -136,6 +163,15 @@ impl traits::GiteaClient for DefaultGiteaClient {
Box::pin(async move { self.fetch_org_repos(org).await })
}
fn renovate_enabled<'a>(
&'a self,
repo: &'a Repository,
) -> Pin<Box<dyn futures::prelude::Future<Output = anyhow::Result<bool>> + Send + 'a>> {
tracing::trace!("checking whether renovate is enabled for: {:?}", repo);
Box::pin(async { self.fetch_renovate(repo).await.map(|s| s.is_some()) })
}
}
mod extensions;
@ -143,4 +179,5 @@ pub mod traits;
use anyhow::Context;
pub use extensions::*;
use reqwest::StatusCode;
use serde::Deserialize;

View File

@ -14,4 +14,9 @@ pub trait GiteaClient {
&'a self,
org: &'a str,
) -> Pin<Box<dyn Future<Output = anyhow::Result<Vec<Repository>>> + Send + 'a>>;
fn renovate_enabled<'a>(
&'a self,
repo: &'a Repository,
) -> Pin<Box<dyn Future<Output = anyhow::Result<bool>> + Send + 'a>>;
}

View File

@ -1,3 +1,4 @@
use futures::{stream::FuturesUnordered, StreamExt};
use itertools::Itertools;
use crate::SharedState;
@ -19,8 +20,13 @@ impl Reconciler {
orgs: Option<Vec<String>>,
) -> anyhow::Result<()> {
let repos = self.get_repos(user, orgs).await?;
tracing::debug!("found repositories: {}", repos.len());
tracing::info!("found repositories: {}", repos.len());
let renovate_enabled = self.get_renovate_enabled(&repos).await?;
tracing::debug!(
"found repositories with renovate enabled: {}",
renovate_enabled.len()
);
Ok(())
}
@ -47,6 +53,34 @@ impl Reconciler {
Ok(repos.into_iter().unique().collect())
}
async fn get_renovate_enabled(&self, repos: &[Repository]) -> anyhow::Result<Vec<Repository>> {
let mut futures = FuturesUnordered::new();
for repo in repos {
futures.push(async move {
let enabled = self.gitea_client.renovate_enabled(repo).await?;
if enabled {
Ok::<Option<Repository>, anyhow::Error>(Some(repo.to_owned()))
} else {
tracing::trace!("repository: {:?}, doesn't have renovate enabled", repo);
Ok(None)
}
})
}
let mut enabled = Vec::new();
while let Some(res) = futures.next().await {
let res = res?;
if let Some(repo) = res {
enabled.push(repo)
}
}
Ok(enabled)
}
}
pub trait ReconcilerState {