Radish alpha
r
Radicle CI broker
Radicle
Git (anonymous pull)
Log in to clone via SSH
feat: generate sample adapter messages with cibtool
Lars Wirzenius committed 1 year ago
commit 43c076c22ef98ff507f16f50c19487ac75c32913
parent c377b05a97dc21e212539430b058d8f3891c7b9c
6 files changed +187 -2
modified Cargo.toml
@@ -27,6 +27,7 @@ slog-scope = "4.4.0"
sqlite = "0.32.0"
sqlite3-sys = "0.15.0"
subplotlib = "0.11.0"
+
tempfile = { version = "3.10.1" }
thiserror = "1.0.63"
time = { version = "0.3.36", features = ["formatting", "macros"] }
uuid = { version = "1.10.0", features = ["v4"] }
@@ -38,7 +39,6 @@ features = ["default", "test"]
[dev-dependencies]
ctor = "0.2.8"
culpa = "1.0.2"
-
tempfile = { version = "3.10.1" }

[build-dependencies]
subplot-build = "0.11.0"
modified src/bin/cibtool.rs
@@ -122,6 +122,8 @@ enum Cmd {
    Trigger(cibtoolcmd::TriggerCmd),
    #[clap(hide = true)]
    Timeout(cibtoolcmd::TimeoutCmd),
+
    #[clap(hide = true)]
+
    Message(cibtoolcmd::MessageCmd),
}

impl Subcommand for Cmd {
@@ -133,6 +135,7 @@ impl Subcommand for Cmd {
            Self::Report(x) => x.run(args),
            Self::Trigger(x) => x.run(args),
            Self::Timeout(x) => x.run(args),
+
            Self::Message(x) => x.run(args),
        }
    }
}
@@ -357,4 +360,7 @@ enum CibToolError {

    #[error(transparent)]
    Timeout(#[from] radicle_ci_broker::timeoutcmd::TimeoutError),
+

+
    #[error(transparent)]
+
    Message(#[from] cibtoolcmd::MessageError),
}
added src/bin/cibtoolcmd/message.rs
@@ -0,0 +1,165 @@
+
use clap::ValueEnum;
+

+
use radicle_ci_broker::msg::{EventType, Request, RequestBuilder, Response, RunId, RunResult};
+
use radicle_ci_broker::test::{MockNode, TestResult};
+

+
use radicle::crypto::ssh::Keystore;
+
use radicle::crypto::test::signer::MockSigner;
+
use radicle::crypto::Signer;
+
use radicle::git::RefString;
+
use radicle::patch::{MergeTarget, Patches};
+
use radicle::prelude::Did;
+
use radicle::profile::{Config, Home};
+
use radicle::storage::ReadRepository;
+
use radicle::test::setup::Node;
+
use radicle::Profile;
+

+
use super::*;
+

+
/// Output sample adapter messages.
+
#[derive(Parser)]
+
pub struct MessageCmd {
+
    /// What kind of message should be output?
+
    #[clap(long)]
+
    #[arg(value_enum)]
+
    kind: MessageKind,
+

+
    /// Info URL to include in a `triggered` message.
+
    #[clap(long)]
+
    info_url: Option<String>,
+
}
+

+
#[derive(Clone, Debug, Eq, PartialEq, ValueEnum)]
+
enum MessageKind {
+
    Push,
+
    Patch,
+
    Triggered,
+
    Success,
+
    Failure,
+
}
+

+
impl Leaf for MessageCmd {
+
    fn run(&self, _args: &Args) -> Result<(), CibToolError> {
+
        let msg = match self.kind {
+
            MessageKind::Push => request_json(push()?)?,
+
            MessageKind::Patch => request_json(patch()?)?,
+
            MessageKind::Triggered => response_json(triggered(&self.info_url)?)?,
+
            MessageKind::Success => response_json(success()?)?,
+
            MessageKind::Failure => response_json(failure()?)?,
+
        };
+

+
        println!("{}", msg);
+

+
        Ok(())
+
    }
+
}
+

+
fn request_json(req: Request) -> Result<String, MessageError> {
+
    req.to_json_pretty().map_err(MessageError::Message)
+
}
+

+
fn response_json(resp: Response) -> Result<String, MessageError> {
+
    resp.to_json_pretty().map_err(MessageError::Message)
+
}
+

+
fn push() -> Result<Request, MessageError> {
+
    let mock_node = MockNode::new().map_err(MessageError::Test)?;
+
    let profile = mock_node.profile().map_err(MessageError::Test)?;
+

+
    let project = mock_node.node().project();
+
    let (_, repo_head) = project.repo.head().map_err(MessageError::Repo)?;
+
    let cmt =
+
        radicle::test::fixtures::commit("my test commit", &[repo_head.into()], &project.backend);
+

+
    let ci_event = CiEvent::V1(CiEventV1::BranchCreated {
+
        from_node: *profile.id(),
+
        repo: project.id,
+
        branch: RefString::try_from(
+
            "refs/namespaces/$nid/refs/heads/master".replace("$nid", &profile.id().to_string()),
+
        )
+
        .map_err(MessageError::Git)?,
+
        tip: cmt,
+
    });
+

+
    let req = RequestBuilder::default()
+
        .profile(&profile)
+
        .ci_event(&ci_event)
+
        .build_trigger_from_ci_event()
+
        .map_err(MessageError::Message)?;
+
    Ok(req)
+
}
+

+
fn patch() -> Result<Request, MessageError> {
+
    let mock_node = MockNode::new()?;
+
    let profile = mock_node.profile()?;
+

+
    let project = mock_node.node().project();
+
    let (_, repo_head) = project.repo.head()?;
+
    let cmt =
+
        radicle::test::fixtures::commit("my test commit", &[repo_head.into()], &project.backend);
+

+
    let node = mock_node.node();
+

+
    let mut patches = Patches::open(&project.repo)?;
+
    let mut cache = radicle::cob::cache::NoCache;
+
    let patch_cob = patches
+
        .create(
+
            "my patch title",
+
            "my patch description",
+
            MergeTarget::Delegates,
+
            repo_head,
+
            cmt,
+
            &[],
+
            &mut cache,
+
            &node.signer,
+
        )
+
        .map_err(MessageError::Patch)?;
+

+
    let ci_event = CiEvent::V1(CiEventV1::PatchCreated {
+
        from_node: *profile.id(),
+
        repo: project.id,
+
        patch: *patch_cob.id(),
+
        new_tip: cmt,
+
    });
+

+
    let req = RequestBuilder::default()
+
        .profile(&profile)
+
        .ci_event(&ci_event)
+
        .build_trigger_from_ci_event()?;
+
    Ok(req)
+
}
+

+
fn triggered(info_url: &Option<String>) -> Result<Response, MessageError> {
+
    let run_id = RunId::from("xyzzy");
+
    if let Some(url) = info_url {
+
        Ok(Response::triggered_with_url(run_id, url))
+
    } else {
+
        Ok(Response::triggered(run_id))
+
    }
+
}
+

+
fn success() -> Result<Response, MessageError> {
+
    Ok(Response::finished(RunResult::Success))
+
}
+

+
fn failure() -> Result<Response, MessageError> {
+
    Ok(Response::finished(RunResult::Failure))
+
}
+

+
#[derive(Debug, thiserror::Error)]
+
pub enum MessageError {
+
    #[error(transparent)]
+
    Test(#[from] Box<dyn std::error::Error>),
+

+
    #[error(transparent)]
+
    Repo(#[from] radicle::storage::RepositoryError),
+

+
    #[error(transparent)]
+
    Git(#[from] radicle_git_ext::ref_format::Error),
+

+
    #[error(transparent)]
+
    Message(#[from] radicle_ci_broker::msg::MessageError),
+

+
    #[error(transparent)]
+
    Patch(#[from] radicle::patch::Error),
+
}
modified src/bin/cibtoolcmd/mod.rs
@@ -23,3 +23,6 @@ pub use trigger::*;

mod timeout;
pub use timeout::*;
+

+
mod message;
+
pub use message::*;
modified src/lib.rs
@@ -23,7 +23,6 @@ pub mod queueadd;
pub mod queueproc;
pub mod run;
pub mod sensitive;
-
#[cfg(test)]
pub mod test;
pub mod timeoutcmd;
pub mod util;
modified src/msg.rs
@@ -474,6 +474,12 @@ impl Request {
        }
    }

+
    /// Serialize the request as a pretty JSON, including the newline.
+
    /// This is meant for the broker to use.
+
    pub fn to_json_pretty(&self) -> Result<String, MessageError> {
+
        serde_json::to_string_pretty(&self).map_err(MessageError::SerializeRequest)
+
    }
+

    /// Serialize the request as a single-line JSON, including the
    /// newline. This is meant for the broker to use.
    pub fn to_writer<W: Write>(&self, mut writer: W) -> Result<(), MessageError> {
@@ -788,6 +794,12 @@ impl Response {
        Ok(())
    }

+
    /// Serialize the response as a pretty JSON, including the newline.
+
    /// This is meant for the broker to use.
+
    pub fn to_json_pretty(&self) -> Result<String, MessageError> {
+
        serde_json::to_string_pretty(&self).map_err(MessageError::SerializeResponse)
+
    }
+

    /// Read a response from a reader. This is meant for the broker to
    /// use.
    pub fn from_reader<R: Read + BufRead>(reader: &mut R) -> Result<Option<Self>, MessageError> {