diff --git a/ci-broker.md b/ci-broker.md index f86748e2..ac217d06 100644 --- a/ci-broker.md +++ b/ci-broker.md @@ -1234,6 +1234,43 @@ sed -i "s/NODEID/$rid/g" "$yaml" ~~~ +## Filter predicate `AnyDelegate` + +_Want:_ We can allow an event that originates in a node for any delegate. + +_Why:_ We want to constrain CI to privileged developers for a repository. + +~~~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 a Git repository other in the Radicle node + +given file config.yaml from filter-anydelegate.yaml +given file update-nodeid.sh +when I run bash update-nodeid.sh xyzzy config.yaml + +when I run cibtool --db ci-broker.db trigger --repo xyzzy --commit HEAD +when I run cibtool --db ci-broker.db trigger --repo other --commit HEAD --node z6MkgEMYod7Hxfy9qCvDv5hYHkZ4ciWmLFgfvm3Wn1b2w2FV + +when I run ./env.sh cib --config config.yaml queued + +when I run cibtool --db ci-broker.db run list --json +then stdout contains ""repo_name": "xyzzy"" +then stdout doesn't contain ""repo_name": "other"" +~~~ + +~~~{#filter-anydelegate.yaml .file .json} +db: ci-broker.db +adapters: + default: + command: ./adapter.sh +triggers: + - adapter: default + filters: + - !AnyDelegate +~~~ + + ## Filter predicate `Tag` _Want:_ We can allow an event that is about a specific tag. diff --git a/src/filter.rs b/src/filter.rs index 188c8bdd..86ea6483 100644 --- a/src/filter.rs +++ b/src/filter.rs @@ -1,5 +1,6 @@ use std::path::{Path, PathBuf}; +use radicle_crypto::PublicKey; use regex::Regex; use serde::{Deserialize, Serialize}; @@ -8,7 +9,7 @@ use radicle::{ git::{BranchName, Oid, raw::ObjectType}, node::NodeId, prelude::{Profile, RepoId}, - storage::git::Repository, + storage::{ReadRepository, git::Repository}, }; use crate::{ @@ -92,6 +93,11 @@ pub enum EventFilter { /// Change originated from specific node. Node(NodeId), + /// Change originated from any delegate node. Note that will change to + /// "from delegate" once Radicle separates the "user" and "node" + /// concepts. + AnyDelegate, + /// Commit in change contains a file or directory with this name. HasFile(PathBuf), @@ -147,6 +153,25 @@ impl EventFilter { format!("wanted={wanted} actual={actual:?}"), ) } + #[allow(clippy::unwrap_used)] + Self::AnyDelegate => { + let repo_id = event.repository().unwrap(); + let radicle = crate::ergo::Radicle::new().unwrap(); + let repo = radicle.repository(repo_id).unwrap(); + let origin = event.from_node().unwrap(); + let delegates: Vec = repo + .delegates() + .iter() + .flatten() + .map(|d| *d.as_key()) + .collect(); + let allowed = delegates.contains(origin); + Decision::string( + "AnyDelegate", + allowed, + format!("wanted={origin} delegates={delegates:?}",), + ) + } Self::Repository(wanted) => { let actual = event.repository(); let allowed = Some(wanted) == actual;