Radish alpha
r
rad:zwTxygwuz5LDGBq255RA2CbNGrz8
Radicle CI broker
Radicle
Git
Add scenarios to verify most of the event filters work
Merged liw opened 1 year ago

No scenarios for patch filters, because I ran into trouble setting up a test repository. Maybe some day.

5 files changed +452 -8 f65a5b83 d0c6a8c8
modified ci-broker.md
@@ -650,7 +650,7 @@ given a Git repository xyzzy in the Radicle node
given the Radicle node emits a refsUpdated event for xyzzy
when I run ./env.sh synthetic-events synt.sock event.json --log log.txt

-
when I run ./env.sh cibtool --db ci-broker.db trigger --repo xyzzy --ref main --commit HEAD
+
when I run ./env.sh cibtool --db ci-broker.db trigger --repo xyzzy
when I run ./env.sh cibtool --db ci-broker.db event list --json

given a directory reports
@@ -732,6 +732,430 @@ when I run cibtool --db ci-broker.db run list
then stdout has one line
~~~

+
# Acceptance criteria for event filtering
+

+
The scenarios in this chapter verify that the event filters work as
+
intended. Each scenario sets up the event queue with an event, and
+
runs `cib queued` to process the event queue, and then verifies that
+
CI was run, or not run, as appropriate.
+

+
In each scenario we verify by running CI twice: once to make sure the
+
filter allows what it should, and once to make sure it doesn't allow
+
what it shouldn't
+

+
## Filter predicate `Repository`
+

+
_Want:_ We can allow an event that is for a specific repository.
+

+
_Why:_ We want to constrain CI to a specific 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-repository.yaml
+
given file update-repoid.sh
+
when I run bash update-repoid.sh xyzzy config.yaml
+

+
when I run cibtool --db ci-broker.db trigger --repo xyzzy
+
when I run cibtool --db ci-broker.db trigger --repo other
+

+
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-repository.yaml .file .json}
+
db: ci-broker.db
+
adapters:
+
  default:
+
    command: ./adapter.sh
+
triggers:
+
  - adapter: default
+
    filters:
+
      - !Repository "REPOID"
+
~~~
+

+
~~~{#update-repoid.sh .file .sh}
+
#!/bin/sh
+

+
set -eu
+

+
dir="$1"
+
yaml="$2"
+

+
rid="$(cd "$dir" && rad .)"
+
sed -i "s/REPOID/$rid/g" "$yaml"
+
~~~
+

+

+
## Filter predicate `Node`
+

+
_Want:_ We can allow an event that originates in a given node.
+

+
_Why:_ We want to constrain CI to a specific developer.
+

+
~~~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-node.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
+
when I run cibtool --db ci-broker.db trigger --repo other --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-node.yaml .file .json}
+
db: ci-broker.db
+
adapters:
+
  default:
+
    command: ./adapter.sh
+
triggers:
+
  - adapter: default
+
    filters:
+
      - !Node "NODEID"
+
~~~
+

+
~~~{#update-nodeid.sh .file .sh}
+
#!/bin/sh
+

+
set -eu
+

+
dir="$1"
+
yaml="$2"
+

+
rid="$(cd "$dir" && rad self --nid)"
+
sed -i "s/NODEID/$rid/g" "$yaml"
+
~~~
+

+

+
## Filter predicate `Branch`
+

+
_Want:_ We can allow an event that is about a specific branch.
+

+
_Why:_ We want to constrain CI to specific branches, such as the
+
`main` branch.
+

+
~~~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-branch.yaml
+

+
when I run cibtool --db ci-broker.db trigger --repo xyzzy
+
when I run cibtool --db ci-broker.db trigger --repo other --ref oksa
+

+
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-branch.yaml .file .json}
+
db: ci-broker.db
+
adapters:
+
  default:
+
    command: ./adapter.sh
+
triggers:
+
  - adapter: default
+
    filters:
+
      - !Branch "main"
+
~~~
+

+
## Filter predicate `BranchCreated`
+

+
_Want:_ We can allow an event for a branch having been created.
+

+
_Why:_ We want to constrain CI to only new branches.
+

+
~~~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-branchcreated.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-updated --base main
+

+
when I run ./env.sh cib --config config.yaml queued
+

+
when I run cibtool --db ci-broker.db run list --json
+
then stdout contains "/refs/heads/main"
+
then stdout doesn't contain "/refs/heads/oksa"
+
~~~
+

+
~~~{#filter-branchcreated.yaml .file .json}
+
db: ci-broker.db
+
adapters:
+
  default:
+
    command: ./adapter.sh
+
triggers:
+
  - adapter: default
+
    filters:
+
      - !BranchCreated
+
~~~
+

+

+
## Filter predicate `BranchUpdated`
+

+
_Want:_ We can allow an event for a branch having been updated.
+

+
_Why:_ We want to constrain CI to only updated branches, as distinct
+
from new branches.
+

+
~~~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-branchupdated.yaml
+

+
when I run cibtool --db ci-broker.db event add --repo xyzzy --ref main --commit HEAD --kind branch-updated --base main
+
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-branchupdated.yaml .file .json}
+
db: ci-broker.db
+
adapters:
+
  default:
+
    command: ./adapter.sh
+
triggers:
+
  - adapter: default
+
    filters:
+
      - !BranchUpdated
+
~~~
+

+

+
## Filter predicate `BranchDeleted`
+

+
_Want:_ We can allow an event for a branch having been deleted.
+

+
_Why:_ We want to constrain CI to only deleted branches, e.g., to
+
update a mirror.
+

+
~~~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-branchdeleted.yaml
+

+
when I run cibtool --db ci-broker.db event add --repo xyzzy --ref main --commit HEAD --kind branch-deleted
+
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-branchdeleted.yaml .file .json}
+
db: ci-broker.db
+
adapters:
+
  default:
+
    command: ./adapter.sh
+
triggers:
+
  - adapter: default
+
    filters:
+
      - !BranchDeleted
+
~~~
+

+

+
## Filter predicate `Allow`
+

+
_Want:_ We can allow all events.
+

+
_Why:_ This is for consistency.
+

+
~~~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-allow.yaml
+

+
when I run cibtool --db ci-broker.db event add --repo xyzzy --ref main --commit HEAD --kind branch-deleted
+
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 contains "oksa"
+
~~~
+

+
~~~{#filter-allow.yaml .file .json}
+
db: ci-broker.db
+
adapters:
+
  default:
+
    command: ./adapter.sh
+
triggers:
+
  - adapter: default
+
    filters:
+
      - !Allow
+
~~~
+

+

+
## Filter predicate `Deny`
+

+
_Want:_ We can allow no events.
+

+
_Why:_ This is for consistency.
+

+
~~~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-deny.yaml
+

+
when I run cibtool --db ci-broker.db event add --repo xyzzy --ref main --commit HEAD --kind branch-deleted
+
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 doesn't contain "main"
+
then stdout doesn't contain "oksa"
+
~~~
+

+
~~~{#filter-deny.yaml .file .json}
+
db: ci-broker.db
+
adapters:
+
  default:
+
    command: ./adapter.sh
+
triggers:
+
  - adapter: default
+
    filters:
+
      - !Deny
+
~~~
+

+

+
## Filter predicate `And`
+

+
_Want:_ We can allow a combination of events if they are all allowed
+
individually.
+

+
_Why:_ This is for consistency.
+

+
~~~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-and.yaml
+

+
when I run cibtool --db ci-broker.db event add --repo xyzzy --ref main --commit HEAD --kind branch-deleted
+
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 contains "oksa"
+
~~~
+

+
~~~{#filter-and.yaml .file .json}
+
db: ci-broker.db
+
adapters:
+
  default:
+
    command: ./adapter.sh
+
triggers:
+
  - adapter: default
+
    filters:
+
      - !And
+
        - !Allow
+
        - !Allow
+
~~~
+

+

+
## Filter predicate `Or`
+

+
_Want:_ We can allow a combination of events if any of them are
+
allowed individually.
+

+
_Why:_ This is for consistency.
+

+
~~~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-or.yaml
+

+
when I run cibtool --db ci-broker.db event add --repo xyzzy --ref main --commit HEAD --kind branch-deleted
+
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 contains "oksa"
+
~~~
+

+
~~~{#filter-or.yaml .file .json}
+
db: ci-broker.db
+
adapters:
+
  default:
+
    command: ./adapter.sh
+
triggers:
+
  - adapter: default
+
    filters:
+
      - !Or
+
        - !Allow
+
        - !Deny
+
~~~
+

+

+
## Filter predicate `Not`
+

+
_Want:_ We can allow an event if the contained filter denies it.
+

+
_Why:_ This is for consistency.
+

+
~~~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-not.yaml
+

+
when I run cibtool --db ci-broker.db event add --repo xyzzy --ref main --commit HEAD --kind branch-deleted
+
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 doesn't contain "main"
+
then stdout doesn't contain "oksa"
+
~~~
+

+
~~~{#filter-not.yaml .file .json}
+
db: ci-broker.db
+
adapters:
+
  default:
+
    command: ./adapter.sh
+
triggers:
+
  - adapter: default
+
    filters:
+
      - !Not Allow
+
~~~
+

+

# Acceptance criteria for test tooling

The event synthesizer is a helper to feed the CI broker node events in
@@ -1233,7 +1657,7 @@ _Who:_ `cib-devs`
given a Radicle node, with CI configured with broker.yaml and adapter dummy.sh
given a Git repository xyzzy in the Radicle node

-
when I run ./env.sh cibtool --db x.db trigger --repo xyzzy --ref main --commit HEAD --id-file id.txt
+
when I run ./env.sh cibtool --db x.db trigger --repo xyzzy --id-file id.txt

when I run cibtool --db x.db event show --id-file id.txt
then stdout contains "rad:"
@@ -1403,11 +1827,11 @@ when I try to run ./env.sh cibtool --db x.db event add --repo rad:z3byzFpcfbMJBp
then command fails
then stderr contains "rad:z3byzFpcfbMJBp4tKYyuuTZiP8WUB"

-
when I try to run ./env.sh cibtool --db x.db trigger --repo missing --ref main --commit HEAD --id-file id.txt
+
when I try to run ./env.sh cibtool --db x.db trigger --repo missing --id-file id.txt
then command fails
then stderr contains "missing"

-
when I try to run ./env.sh cibtool --db x.db trigger --repo rad:z3byzFpcfbMJBp4tKYyuuTZiP8WUB --ref main --commit HEAD --id-file id.txt
+
when I try to run ./env.sh cibtool --db x.db trigger --repo rad:z3byzFpcfbMJBp4tKYyuuTZiP8WUB --id-file id.txt
then command fails
then stderr contains "rad:z3byzFpcfbMJBp4tKYyuuTZiP8WUB"

modified src/bin/cibtoolcmd/trigger.rs
@@ -3,6 +3,10 @@ use super::*;
/// Trigger a CI run.
#[derive(Parser)]
pub struct TriggerCmd {
+
    /// Set the node where the node originated from.
+
    #[clap(long)]
+
    node: Option<NodeId>,
+

    /// Set the repository the event refers to. Can be a RID, or the
    /// repository name.
    #[clap(long)]
@@ -28,6 +32,7 @@ impl Leaf for TriggerCmd {
    fn run(&self, args: &Args) -> Result<(), CibToolError> {
        let profile = util::load_profile()?;
        let nid = util::lookup_nid(&profile)?;
+
        let nid = self.node.unwrap_or(nid);
        let (rid, _repo_name) = util::lookup_repo(&profile, &self.repo)?;
        let oid = util::oid_from_cli_arg(&profile, rid, &self.commit)?;

modified src/config.rs
@@ -99,9 +99,12 @@ impl Config {
#[derive(Debug, Serialize, Deserialize)]
pub struct AdapterConfig {
    pub command: PathBuf,
-
    pub env: HashMap<String, String>,
+

+
    #[serde(default)]
+
    env: HashMap<String, String>,
+

    #[serde(default)]
-
    pub sensitive_env: HashMap<String, Sensitive>,
+
    sensitive_env: HashMap<String, Sensitive>,
}

impl AdapterConfig {
modified src/logger.rs
@@ -3,7 +3,7 @@
use std::{path::Path, process::ExitStatus, time::Duration};

use clap::ValueEnum;
-
use radicle::{git::raw::Oid, identity::RepoId, node::Event};
+
use radicle::{git::raw::Oid, identity::RepoId, node::Event, patch::PatchId};
use serde_json::Value;
use tracing::{debug, error, info, trace, warn, Level};
use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
@@ -179,6 +179,8 @@ enum Id {
    TimeoutWaitStderrReaderEnd,
    TimeoutWaitStdinWriterEnd,
    TimeoutWaitStdoutReaderEnd,
+

+
    TriggerCreate,
}

#[derive(Debug, thiserror::Error)]
@@ -600,6 +602,16 @@ pub fn broker_end_run(run: &Run) {
    );
}

+
pub fn patch_cob_lookup(repo_id: &RepoId, patch_id: &PatchId) {
+
    warn!(
+
        msg_id = ?Id::TriggerCreate,
+
        kind = %Kind::StartRun,
+
        ?repo_id,
+
        ?patch_id,
+
        "did not find patch COB in repository"
+
    );
+
}
+

pub fn adapter_no_first_response() {
    error!(
        msg_id = ?Id::AdapterNoFirstMessage,
modified src/msg.rs
@@ -239,7 +239,7 @@ impl<'a> RequestBuilder<'a> {

            let x = match x {
                None => {
-
                    logger::trace("did not find patch COB from repository");
+
                    logger::patch_cob_lookup(&rad_repo.id, patch_id);
                    return Err(MessageError::PatchCob(*patch_id));
                }
                Some(x) => x,