feat: add cibtool trigger --all option

Signed-off-by: Lars Wirzenius <liw@liw.fi>
This commit is contained in:
Lars Wirzenius
2025-08-15 16:06:32 +03:00
parent 7c3af34ac1
commit 7a27e0c01a
+86 -40
View File
@@ -8,16 +8,20 @@ use radicle_ci_broker::{
use super::*;
/// Trigger a CI run.
#[derive(Parser)]
#[derive(Debug, Parser)]
pub struct TriggerCmd {
/// Set the node where the node originated from.
#[clap(long)]
node: Option<NodeId>,
/// Trigger CI to run for all repositories on the local node.
#[clap(long, required_unless_present_any = ["patch","commit"])]
all: bool,
/// Set the repository the event refers to. Can be a RID, or the
/// repository name.
#[clap(long)]
repo: String,
#[clap(long, required_unless_present = "all")]
repo: Option<String>,
/// Set the name of the ref the event refers to.
#[clap(long = "ref", aliases = ["name"], default_value = "main")]
@@ -26,11 +30,11 @@ pub struct TriggerCmd {
/// Set the commit the event refers to. Can be the SHA1 commit id,
/// or a symbolic Git revision, as understood by `git rev-parse`.
/// For example, `HEAD`.
#[clap(long, required_unless_present = "patch")]
#[clap(long, required_unless_present_any = ["patch","all"])]
commit: Option<String>,
/// Trigger CI to run on this patch.
#[clap(long, required_unless_present = "commit")]
#[clap(long, required_unless_present_any = ["commit","all"])]
patch: Option<PatchId>,
/// Write the event ID to this file, after adding the event to the
@@ -40,52 +44,30 @@ pub struct TriggerCmd {
/// Output the event to trigger a CI run to the standard output,
/// instead of adding to the event queue in the database.
#[clap(long)]
#[clap(long, conflicts_with = "all")]
stdout: bool,
/// Output the event to trigger a CI run to a named file, instead
/// of adding to the event queue in the database.
#[clap(long)]
#[clap(long, conflicts_with = "all")]
output: Option<PathBuf>,
}
impl Leaf for TriggerCmd {
fn run(&self, args: &Args) -> Result<(), CibToolError> {
let r = ergo::Radicle::new().map_err(TriggerError::Ergonomic)?;
let profile = r.profile();
let nid = self.node.unwrap_or(*profile.id());
let repo = r
.repository_by_name(&self.repo)
.map_err(TriggerError::Ergonomic)?;
let oid = self.oid(&r, &repo)?;
let base = r
.resolve_commit(&repo.id, &format!("{oid}^"))
.unwrap_or(oid);
let branch_name = self.branch(&r, &repo)?;
let event = CiEvent::branch_updated(nid, repo.id, &branch_name, oid, base)
.map_err(CibToolError::CiEvent)?;
if self.stdout {
let json = event.to_pretty_json().map_err(CibToolError::EventToJson2)?;
println!("{json}");
} else if let Some(filename) = &self.output {
let json = event.to_pretty_json().map_err(CibToolError::EventToJson2)?;
std::fs::write(filename, &json)
.map_err(|err| CibToolError::Write(filename.into(), err))?;
} else {
let db = args.open_db()?;
let id = db.push_queued_ci_event(event)?;
println!("{id}");
if let Some(filename) = &self.id_file {
write(filename, id.to_string().as_bytes())
.map_err(|e| CibToolError::WriteEventId(filename.into(), e))?;
if self.all {
for ri in r.repositories().map_err(TriggerError::Ergonomic)? {
let repo = r.repository(&ri.rid).map_err(TriggerError::Ergonomic)?;
self.trigger(args, &r, &repo)?;
}
} else if let Some(repo) = &self.repo {
let repo = r
.repository_by_name(repo)
.map_err(TriggerError::Ergonomic)?;
self.trigger(args, &r, &repo)?;
} else {
Err(TriggerError::NoRepo)?
}
Ok(())
@@ -112,6 +94,49 @@ impl TriggerCmd {
Ok(project.default_branch().clone())
}
}
fn trigger(
&self,
args: &Args,
r: &ergo::Radicle,
repo: &Repository,
) -> Result<(), TriggerError> {
let profile = r.profile();
let nid = self.node.unwrap_or(*profile.id());
let oid = self.oid(r, repo)?;
let base = r
.resolve_commit(&repo.id, &format!("{oid}^"))
.unwrap_or(oid);
let branch_name = self.branch(r, repo)?;
let event = CiEvent::branch_updated(nid, repo.id, &branch_name, oid, base)
.map_err(TriggerError::CiEvent)?;
if self.stdout {
let json = event.to_pretty_json().map_err(TriggerError::EventToJson2)?;
println!("{json}");
} else if let Some(filename) = &self.output {
let json = event.to_pretty_json().map_err(TriggerError::EventToJson2)?;
std::fs::write(filename, &json)
.map_err(|err| TriggerError::Write(filename.into(), err))?;
} else {
let db = args
.open_db()
.map_err(|err| TriggerError::OpenDb(Box::new(err)))?;
let id = db.push_queued_ci_event(event)?;
println!("{id}");
if let Some(filename) = &self.id_file {
write(filename, id.to_string().as_bytes())
.map_err(|e| TriggerError::WriteEventId(filename.into(), e))?;
}
}
Ok(())
}
}
#[derive(Debug, thiserror::Error)]
@@ -121,4 +146,25 @@ pub enum TriggerError {
#[error(transparent)]
RefError(#[from] radicle_ci_broker::refs::RefError),
#[error("repository must be given with --repo")]
NoRepo,
#[error("failed to construct a CI event")]
CiEvent(#[source] CiEventError),
#[error(transparent)]
EventToJson2(#[from] CiEventError),
#[error(transparent)]
Db(#[from] DbError),
#[error("failed to write file: {0}")]
Write(PathBuf, #[source] std::io::Error),
#[error("failed to write event ID to file {0}")]
WriteEventId(PathBuf, #[source] std::io::Error),
#[error("failed to get database")]
OpenDb(#[source] Box<CibToolError>),
}