use clap::ValueEnum;
use radicle::cob::Title;
use radicle_ci_broker::msg::{Request, RequestBuilder, Response, RunId, RunResult};
use radicle_ci_broker::test::MockNode;
use git_ref_format_core::RefString;
use radicle::patch::{MergeTarget, Patches};
use radicle::storage::ReadRepository;
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(
Title::new("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::fmt::Error),
#[error(transparent)]
Message(#[from] radicle_ci_broker::msg::MessageError),
#[error(transparent)]
Patch(#[from] radicle::patch::Error),
#[error(transparent)]
Title(#[from] radicle::cob::common::TitleError),
}