| use std::io::BufReader; |
| use std::path::PathBuf; |
| use std::process; |
| |
| use anyhow::{anyhow, Result}; |
| use clap::{Parser, Subcommand}; |
| use cliclack::{intro, outro, outro_cancel, spinner}; |
| use serde::Deserialize; |
| |
| #[derive(Parser)] |
| #[command(version, about, long_about = None)] |
| struct Cli { |
| #[command(subcommand)] |
| command: Command, |
| } |
| |
| #[derive(Subcommand)] |
| enum Command { |
| /// runs presubmit testing |
| Presubmit {}, |
| } |
| |
| #[derive(Clone, Debug, Deserialize)] |
| pub struct BazelPrograms { |
| pub default: Vec<Vec<String>>, |
| } |
| |
| #[derive(Clone, Debug, Deserialize)] |
| pub struct BazelPresubmit { |
| pub remote_cache: bool, |
| pub upload_local_results: bool, |
| pub programs: BazelPrograms, |
| } |
| |
| #[derive(Clone, Debug, Deserialize)] |
| pub struct PigweedConfig { |
| pub bazel_presubmit: BazelPresubmit, |
| } |
| |
| #[derive(Clone, Debug, Deserialize)] |
| pub struct PigweedProject { |
| pub pw: PigweedConfig, |
| } |
| |
| fn load_project_config() -> Result<PigweedProject> { |
| let workspace_dir = std::env::var("BUILD_WORKSPACE_DIRECTORY") |
| .map_err(|_| anyhow!("BUILD_WORKSPACE_DIRECTORY not found. Please run inside bazel."))?; |
| let path: PathBuf = [&workspace_dir, "pigweed.json"].iter().collect(); |
| let reader = BufReader::new(std::fs::File::open(path)?); |
| |
| let project_config = serde_json::from_reader(reader)?; |
| Ok(project_config) |
| } |
| |
| fn run_bazel_presubmit(args: Vec<String>) -> Result<()> { |
| let workspace_dir = std::env::var("BUILD_WORKSPACE_DIRECTORY") |
| .map_err(|_| anyhow!("BUILD_WORKSPACE_DIRECTORY not found. Please run inside bazel."))?; |
| let spinner = spinner(); |
| spinner.start(format!("Running {args:?}...")); |
| let output = process::Command::new("bazelisk") |
| .current_dir(workspace_dir) |
| .args(args.clone()) |
| .output()?; |
| |
| if !output.status.success() { |
| return Err(anyhow!( |
| "{args:?} failed:\n{}", |
| String::from_utf8_lossy(&output.stdout) |
| )); |
| } |
| spinner.stop(format!("{args:?} success.")); |
| |
| Ok(()) |
| } |
| |
| fn command_presubmit() -> Result<()> { |
| intro("Running presubmits")?; |
| |
| let config = |
| load_project_config().map_err(|e| anyhow!("Failed to load project config: {e}"))?; |
| |
| for args in config.pw.bazel_presubmit.programs.default { |
| run_bazel_presubmit(args)?; |
| } |
| |
| outro("All presubmits succeeded")?; |
| Ok(()) |
| } |
| |
| fn main() { |
| let cli = Cli::parse(); |
| |
| let result = match cli.command { |
| Command::Presubmit {} => command_presubmit(), |
| }; |
| |
| if let Err(e) = result { |
| outro_cancel(format!("{e}")).expect("outro succeeds"); |
| std::process::exit(1); |
| } |
| } |