cerberus_mergeguard/
lib.rs1#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/README.md"))]
2use clap::{Args, Parser, Subcommand};
3use tracing::Level;
4
5mod api;
6mod client;
7mod config;
8mod error;
9mod server;
10#[cfg(test)]
11mod test;
12#[cfg(any(test, feature = "e2e"))]
13pub mod testutils;
14mod types;
15mod version;
16
17#[derive(Debug, Parser)]
19#[clap(disable_version_flag = true)]
20pub struct App {
21 #[clap(flatten)]
23 pub global_opts: GlobalOpts,
24
25 #[clap(subcommand)]
27 pub command: Command,
28}
29
30impl App {
31 pub async fn run(self) -> Result<(), error::Error> {
33 if let Command::Version = self.command {
34 version::print_version_and_exit();
35 }
36
37 let config = config::Configuration::load(&self.global_opts.config)?;
38
39 let log_level = match self.global_opts.log {
40 Some(level) => level,
41 None => config.log_level,
42 };
43 set_log_level(&log_level);
44
45 let client = client::Client::build(config.github)?;
46
47 match self.command {
48 Command::Server => {
49 let server = server::Server::new(config.server);
50 server.run(client).await?;
51 }
52 Command::Create { cli_opts } => {
53 return client
54 .create_check_run(
55 cli_opts.app_installation_id,
56 &cli_opts.repo,
57 &cli_opts.commit,
58 )
59 .await;
60 }
61 Command::Refresh { cli_opts } => {
62 let (uncompleted, own_run) = get_and_print_status(&cli_opts, &client).await?;
63 if uncompleted == 0 {
64 println!("All check runs are completed, setting check-run to 'completed'");
65 }
66 if own_run.is_none() {
67 println!("No cerberus check-run found, creating a new one");
68 }
69 client
70 .update_check_run(
71 cli_opts.app_installation_id,
72 &cli_opts.repo,
73 &cli_opts.commit,
74 uncompleted,
75 own_run,
76 )
77 .await?;
78 println!("Updated PR status");
79 }
80 Command::Status { cli_opts } => {
81 get_and_print_status(&cli_opts, &client).await?;
82 }
83 Command::Version => {
84 version::print_version_and_exit();
85 }
86 }
87 Ok(())
88 }
89}
90
91#[derive(Debug, Subcommand)]
93pub enum Command {
94 Server,
96 Create {
98 #[clap(flatten)]
99 cli_opts: CLIOptions,
100 },
101 Refresh {
103 #[clap(flatten)]
104 cli_opts: CLIOptions,
105 },
106 Status {
108 #[clap(flatten)]
109 cli_opts: CLIOptions,
110 },
111 Version,
113}
114
115#[derive(Debug, Args)]
118pub struct GlobalOpts {
119 #[clap(long, global = true)]
121 pub log: Option<String>,
122
123 #[clap(long, short, global = true, default_value = "/config/config.yaml")]
125 pub config: String,
126}
127
128#[derive(Debug, Args)]
130pub struct CLIOptions {
131 #[clap(index = 1)]
133 pub app_installation_id: u64,
134 #[clap(index = 2)]
136 pub repo: String,
137 #[clap(index = 3)]
139 pub commit: String,
140}
141
142fn set_log_level(level: &str) {
143 let level = match level.to_lowercase().as_str() {
144 "error" => Level::ERROR,
145 "warn" => Level::WARN,
146 "info" => Level::INFO,
147 "debug" => Level::DEBUG,
148 _ => {
149 eprintln!("Invalid log level: {level}. Defaulting to 'info'.");
150 Level::INFO
151 }
152 };
153 let logger = tracing_subscriber::fmt()
154 .with_max_level(level)
155 .with_ansi(false);
156 #[cfg(not(test))]
157 logger.init();
158
159 #[cfg(test)]
161 logger.try_init().unwrap_or_default();
162}
163
164async fn get_and_print_status(
165 cli_opts: &CLIOptions,
166 client: &client::Client,
167) -> Result<(u32, Option<types::CheckRun>), error::Error> {
168 let (count, own_run) = client
169 .get_check_run_status(
170 cli_opts.app_installation_id,
171 &cli_opts.repo,
172 &cli_opts.commit,
173 )
174 .await?;
175 println!("Waiting on '{count}' check runs to complete");
176 if let Some(own_run) = own_run.clone() {
177 println!(
178 "Found {} check-run, status: '{}', conclusion: '{}'",
179 types::CHECK_RUN_NAME,
180 own_run.status,
181 own_run.conclusion.unwrap_or("null".to_string())
182 );
183 } else {
184 println!(
185 "No {} check-run found for this commit",
186 types::CHECK_RUN_NAME
187 );
188 };
189 Ok((count, own_run))
190}