diff --git a/ci-broker.md b/ci-broker.md index 48b79e3c..bea74d56 100644 --- a/ci-broker.md +++ b/ci-broker.md @@ -1194,6 +1194,42 @@ triggers: ~~~ +## Filter predicate `DefaultBranch` + +_Want:_ We can allow an event if the event refers to the default +branch. + +_Why:_ This is so that the user doesn't need to spell out the name +explicitly. + +~~~scenario +given a Radicle node, with CI configured with broker.yaml and adapter dummy.sh +given a Git repository xyzzy in the Radicle node + +given file config.yaml from filter-defaultbranch.yaml + +when I run cibtool --db ci-broker.db event add --repo xyzzy --ref main --commit HEAD --kind branch-created +when I run cibtool --db ci-broker.db event add --repo xyzzy --ref oksa --commit HEAD --kind branch-created + +when I run ./env.sh cib --config config.yaml queued + +when I run cibtool --db ci-broker.db run list --json +then stdout contains "main" +then stdout doesn't contain "oksa" +~~~ + +~~~{#filter-defaultbranch.yaml .file .json} +db: ci-broker.db +adapters: + default: + command: ./adapter.sh +triggers: + - adapter: default + filters: + - !DefaultBranch +~~~ + + # Acceptance criteria for test tooling The event synthesizer is a helper to feed the CI broker node events in diff --git a/src/filter.rs b/src/filter.rs index f2149124..b860dcdd 100644 --- a/src/filter.rs +++ b/src/filter.rs @@ -2,7 +2,13 @@ use std::path::{Path, PathBuf}; use serde::{Deserialize, Serialize}; -use radicle::{cob::patch::PatchId, git::RefString, node::NodeId, prelude::RepoId}; +use radicle::{ + cob::patch::PatchId, + git::RefString, + node::NodeId, + prelude::{Profile, RepoId}, + storage::git::Repository, +}; use radicle_git_ext::Oid; use crate::{ @@ -49,6 +55,9 @@ pub enum EventFilter { /// Event is for a specific branch. Branch(RefString), + /// Event if for the default branch for the repository. + DefaultBranch, + /// Branch was created. BranchCreated, @@ -113,6 +122,7 @@ impl EventFilter { Self::Node(wantedc) => from_node == wantedc, Self::Repository(wanted) => repo == wanted, Self::Branch(wanted) => branch == wanted, + Self::DefaultBranch => is_default_branch(repo, branch), Self::BranchCreated => true, _ => false, }, @@ -125,6 +135,7 @@ impl EventFilter { Self::Node(wanted) => from_node == wanted, Self::Repository(wanted) => repo == wanted, Self::Branch(wanted) => branch == wanted, + Self::DefaultBranch => is_default_branch(repo, branch), Self::BranchUpdated => true, _ => false, }, @@ -137,6 +148,7 @@ impl EventFilter { Self::Node(wanted) => from_node == wanted, Self::Repository(wanted) => repo == wanted, Self::Branch(wanted) => branch == wanted, + Self::DefaultBranch => is_default_branch(repo, branch), Self::BranchDeleted => true, _ => false, }, @@ -176,6 +188,39 @@ impl EventFilter { } } +fn is_default_branch(repo_id: &RepoId, wanted: &str) -> bool { + let res = get_default_branch(repo_id); + logger::debug2(format!("is_default_branch: res={res:?}")); + if let Ok(actual) = res { + logger::debug2(format!( + "is_default_branch: actual={actual} wanted={wanted}" + )); + actual == wanted + } else { + logger::debug("is_default_branch: could not get actual"); + false + } +} + +fn get_default_branch(repo_id: &RepoId) -> Result> { + let profile = Profile::load()?; + logger::debug("loaded profile"); + let path = profile.storage.path().join(repo_id.canonical()); + logger::debug2(format!("path={}", path.display())); + let repo = Repository::open(path, *repo_id)?; + logger::debug("loaded repo"); + let proj = repo.project()?; + logger::debug("loaded project"); + + let def = format!( + "{}/refs/heads/{}", + profile.id().to_namespace(), + proj.default_branch() + ); + + Ok(def) +} + #[derive(Deserialize)] struct Filters { filters: Vec,