diff --git a/Cargo.lock b/Cargo.lock index e8ca0bd..dafe63a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1397,9 +1397,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.5" +version = "1.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ "aho-corasick", "memchr", @@ -2336,6 +2336,7 @@ dependencies = [ "http-body-util", "k8s-openapi", "kube", + "regex", "reqwest", "schemars", "serde", diff --git a/Cargo.toml b/Cargo.toml index 80bbb80..22099bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ hmac = "0.12.1" http-body-util = "0.1.2" k8s-openapi = { version = "0.22.0", default-features = false, features = ["v1_30"] } kube = { version = "0.92.1", default-features = false, features = ["client", "config", "rustls-tls", "derive", "runtime"] } +regex = "1.10.6" reqwest = "0.12.5" schemars = "0.8.21" serde = { version = "1.0.204", features = ["derive"] } diff --git a/src/discovery.rs b/src/discovery.rs index 672f4dd..7d0b76e 100644 --- a/src/discovery.rs +++ b/src/discovery.rs @@ -1,14 +1,17 @@ +use std::path::Path; +use std::path::PathBuf; + use crate::gitea_client::GiteaClient; +use crate::gitea_client::Tree; use crate::gitea_client::TreeFileReference; use crate::in_repo_config::InRepoConfig; +use regex::Regex; +use tracing::debug; -pub(crate) async fn discover_webhook_bridge_config, R: AsRef, C: AsRef>( +pub(crate) async fn discover_webhook_bridge_config( gitea: &GiteaClient, - owner: O, - repo: R, - commit: C, + repo_tree: &Tree, ) -> Result> { - let repo_tree = gitea.get_tree(owner, repo, commit).await?; let in_repo_config_reference = repo_tree .files .iter() @@ -22,3 +25,54 @@ pub(crate) async fn discover_webhook_bridge_config, R: AsRef, Ok(parsed_in_repo_config) } + +pub(crate) async fn discover_matching_push_triggers>( + gitea: &GiteaClient, + repo_tree: &Tree, + git_ref: RE, + in_repo_config: &InRepoConfig, +) -> Result<(), Box> { + let ref_to_branch_regex = Regex::new(r"refs/heads/(?P.+)")?; + let captures = ref_to_branch_regex + .captures(git_ref.as_ref()) + .ok_or("Could not find branch name.")?; + let branch = &captures["branch"]; + debug!("Detected branch from push as {:?}", branch); + + let push_triggers = in_repo_config.get_push_triggers_for_branch(branch)?; + for trigger in push_triggers { + let path_to_source = normalize_path(Path::new(".webhook_bridge").join(&trigger.source)); + debug!("Loading path {}", path_to_source.display()); + let pipeline_template = repo_tree + .files + .iter() + .filter(|file_reference| Path::new(&file_reference.path) == path_to_source.as_path()) + .next() + .ok_or("Trigger source not found in remote repo.")?; + let pipeline_contents = String::from_utf8(gitea.read_file(pipeline_template).await?)?; + debug!("Pipeline contents: {}", pipeline_contents); + } + + Ok(()) +} + +fn normalize_path>(path: P) -> PathBuf { + let mut ret = PathBuf::new(); + + for component in path.as_ref().components() { + match component { + // Prefix does not happen on unix-based systems. + std::path::Component::Prefix(_) + | std::path::Component::RootDir + | std::path::Component::Normal(_) => { + ret.push(component); + } + std::path::Component::CurDir => {} + std::path::Component::ParentDir => { + ret.pop(); + } + } + } + + ret +} diff --git a/src/in_repo_config.rs b/src/in_repo_config.rs index b82fae5..478741d 100644 --- a/src/in_repo_config.rs +++ b/src/in_repo_config.rs @@ -1,3 +1,4 @@ +use regex::Regex; use serde::Deserialize; use serde::Serialize; @@ -13,11 +14,8 @@ pub(crate) struct InRepoConfig { /// A config for a job that is triggered by a push to a git repo. #[derive(Serialize, Deserialize, Clone, Debug)] pub(crate) struct TriggerPush { - #[serde(default, skip_serializing_if = "Option::is_none")] - pub(crate) name: Option, - - #[serde(default, skip_serializing_if = "Option::is_none")] - pub(crate) source: Option, + pub(crate) name: String, + pub(crate) source: String, #[serde(default, skip_serializing_if = "Option::is_none")] pub(crate) clone_uri: Option, @@ -40,4 +38,34 @@ impl InRepoConfig { ); Ok(parsed_in_repo_config) } + + pub(crate) fn get_push_triggers_for_branch>( + &self, + branch: B, + ) -> Result, Box> { + let branch = branch.as_ref(); + let mut ret = Vec::new(); + for push in &self.push { + let skip_regex: Vec<_> = push + .skip_branches + .iter() + .map(|s| Regex::new(s.as_str())) + .collect::>()?; + if skip_regex.iter().any(|r| r.is_match(branch)) { + continue; + } + + let match_regex: Vec<_> = push + .branches + .iter() + .map(|s| Regex::new(s.as_str())) + .collect::>()?; + if !push.branches.is_empty() && !match_regex.iter().any(|r| r.is_match(branch)) { + continue; + } + + ret.push(push); + } + Ok(ret) + } } diff --git a/src/main.rs b/src/main.rs index 78cf185..145ee95 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,6 +19,7 @@ use tracing_subscriber::layer::SubscriberExt; use tracing_subscriber::util::SubscriberInitExt; use self::crd_pipeline_run::PipelineRun; +use self::discovery::discover_matching_push_triggers; use self::discovery::discover_webhook_bridge_config; use self::gitea_client::GiteaClient; use self::in_repo_config::InRepoConfig; @@ -54,13 +55,16 @@ async fn main() -> Result<(), Box> { let gitea_api_token = std::env::var("WEBHOOK_BRIDGE_OAUTH_TOKEN")?; let gitea = GiteaClient::new(gitea_api_root, gitea_api_token); - discover_webhook_bridge_config( - &gitea, - "talexander", - "webhook_bridge", - "c32a8650f509b5e4bbf6df0c210a3a01ad405eb5", - ) - .await?; + let repo_tree = gitea + .get_tree( + "talexander", + "webhook_bridge", + "c32a8650f509b5e4bbf6df0c210a3a01ad405eb5", + ) + .await?; + let in_repo_config = discover_webhook_bridge_config(&gitea, &repo_tree).await?; + + discover_matching_push_triggers(&gitea, &repo_tree, "refs/heads/main", &in_repo_config).await?; // let jobs: Api = Api::namespaced(kubernetes_client, "lighthouse"); // let jobs: Api = Api::default_namespaced(kubernetes_client);