Radish alpha
r
rad:zwTxygwuz5LDGBq255RA2CbNGrz8
Radicle CI broker
Radicle
Git
doc: document how to add a new event filter
Lars Wirzenius committed 5 months ago
commit 0dc74cf8e7dee37d0a94005440bfe51f5e669e8f
parent 94fbd9f
2 files changed +134 -1
modified doc/Makefile
@@ -14,7 +14,7 @@
	pikchr-cli $< > $@.tmp
	mv $@.tmp $@

-
all: architecture.html userguide.html overview.html
+
all: architecture.html userguide.html overview.html filter-impl.html

architecture.html: architecture.subplot architecture.md Makefile messages.md

@@ -26,3 +26,6 @@ userguide.html: userguide.subplot userguide.md Makefile

overview.html: overview.md Makefile
	pandoc --standalone --metadata title="Radicle CI overview" "$<" --output "$@"
+

+
filter-impl.html: filter-impl.md Makefile
+
	pandoc --standalone --metadata title="Radicle CI overview" "$<" --output "$@"
added doc/filter-impl.md
@@ -0,0 +1,130 @@
+
# Implementing a new event filter
+

+
Overview:
+

+
* in `src/filter.rs`, add a new variant to `enum EventFilter`, with whatever
+
  fields are needed for that filter
+
* in `sr/filter.rs`, in the `EventFilter::decide` method, add a new `match` arm
+
  for the new filter variant
+
* in `ci-broker.md` add a new section `## Filter predicte XXX`, like the existing
+
  ones, to verify the new filter works
+
* run `make` at the root of the source tree to make sure everything works
+

+
## Example
+

+
The patch to add a `AnyDelegate` filter looks like below.
+

+
~~~~~~diff
+
commit 94fbd9f72fc4db779422fa97786c99bb9b3b14d4
+
Author: Lars Wirzenius <liw@liw.fi>
+
Date:   Mon Nov 10 14:00:53 2025 +0200
+

+
    feat: add the AnyDelegate filter
+

+
diff --git a/ci-broker.md b/ci-broker.md
+
index f86748e..ac217d0 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 188c8bd..86ea648 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<PublicKey> = 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;
+
~~~~~~