1 // Copyright 2025 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 use std::collections::HashMap;
16 use std::io::BufReader;
17 use std::path::PathBuf;
18 use std::process;
19
20 use anyhow::{anyhow, Result};
21 use cliclack::{intro, outro, outro_cancel, spinner};
22 use serde::Deserialize;
23
24 #[derive(Clone, Debug, Deserialize)]
25 pub struct BazelPrograms {
26 pub default: Vec<Vec<String>>,
27 }
28
29 #[derive(Clone, Debug, Deserialize)]
30 pub struct BazelPresubmit {
31 pub remote_cache: bool,
32 pub upload_local_results: bool,
33 pub programs: HashMap<String, Vec<Vec<String>>>,
34 }
35
36 #[derive(Clone, Debug, Deserialize)]
37 pub struct PigweedConfig {
38 pub bazel_presubmit: BazelPresubmit,
39 }
40
41 #[derive(Clone, Debug, Deserialize)]
42 pub struct PigweedProject {
43 pub pw: PigweedConfig,
44 }
45
load_project_config() -> Result<PigweedProject>46 fn load_project_config() -> Result<PigweedProject> {
47 let workspace_dir = std::env::var("BUILD_WORKSPACE_DIRECTORY")
48 .map_err(|_| anyhow!("BUILD_WORKSPACE_DIRECTORY not found. Please run inside bazel."))?;
49 let path: PathBuf = [&workspace_dir, "pigweed.json"].iter().collect();
50 let reader = BufReader::new(std::fs::File::open(path)?);
51
52 let project_config = serde_json::from_reader(reader)?;
53 Ok(project_config)
54 }
55
run_bazel_presubmit(args: &Vec<String>) -> Result<()>56 fn run_bazel_presubmit(args: &Vec<String>) -> Result<()> {
57 let workspace_dir = std::env::var("BUILD_WORKSPACE_DIRECTORY")
58 .map_err(|_| anyhow!("BUILD_WORKSPACE_DIRECTORY not found. Please run inside bazel."))?;
59 let spinner = spinner();
60 spinner.start(format!("Running {args:?}..."));
61 let output = process::Command::new("bazelisk")
62 .current_dir(workspace_dir)
63 .args(args.clone())
64 .output()?;
65
66 if !output.status.success() {
67 return Err(anyhow!(
68 "{args:?} failed:\n{}",
69 String::from_utf8_lossy(&output.stderr)
70 ));
71 }
72 spinner.stop(format!("{args:?} success."));
73
74 Ok(())
75 }
76
run_presubmit() -> Result<()>77 fn run_presubmit() -> Result<()> {
78 intro("Running presubmits")?;
79
80 let config =
81 load_project_config().map_err(|e| anyhow!("Failed to load project config: {e}"))?;
82
83 let Some(kernel_program) = config.pw.bazel_presubmit.programs.get("kernel") else {
84 return Err(anyhow!("No `kernel` program in projects bazel_presubmit"));
85 };
86
87 for args in kernel_program {
88 run_bazel_presubmit(args)?;
89 }
90
91 outro("All presubmits succeeded")?;
92 Ok(())
93 }
94
main()95 fn main() {
96 if let Err(e) = run_presubmit() {
97 outro_cancel(format!("{e}")).expect("outro succeeds");
98 std::process::exit(1);
99 }
100 }
101