Skip to content

Commit

Permalink
first impl working
Browse files Browse the repository at this point in the history
  • Loading branch information
TylerJang27 committed Dec 6, 2024
1 parent c21215e commit 00cd6bc
Show file tree
Hide file tree
Showing 8 changed files with 478 additions and 33 deletions.
323 changes: 305 additions & 18 deletions Cargo.lock

Large diffs are not rendered by default.

38 changes: 31 additions & 7 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ use bundle::RunResult;
use clap::{Args, Parser, Subcommand};
use codeowners::CodeOwners;
use constants::{EXIT_FAILURE, SENTRY_DSN};
use context::repo::BundleRepo;
use context::{bazel_bep::parser::BazelBepParser, repo::BundleRepo};
use trunk_analytics_cli::{
api_client::ApiClient,
runner::{run_quarantine, run_test_command},
runner::{run_quarantine, run_test_command, JunitSpec},
upload::{run_upload, UploadArgs},
validate::validate,
};
Expand Down Expand Up @@ -42,12 +42,19 @@ struct TestArgs {
struct ValidateArgs {
#[arg(
long,
required = true,
required_unless_present = "bazel_bep_path",
conflicts_with = "bazel_bep_path",
value_delimiter = ',',
value_parser = clap::builder::NonEmptyStringValueParser::new(),
help = "Comma-separated list of glob paths to junit files."
)]
junit_paths: Vec<String>,
#[arg(
long,
required_unless_present = "junit_paths",
help = "Path to bazel build event protocol JSON file."
)]
bazel_bep_path: Option<String>,
#[arg(long, help = "Show warning-level log messages in output.")]
show_warnings: bool,
#[arg(long, help = "Value to override CODEOWNERS file or directory path.")]
Expand Down Expand Up @@ -100,6 +107,7 @@ async fn run_test(test_args: TestArgs) -> anyhow::Result<i32> {
} = test_args;
let UploadArgs {
junit_paths,
bazel_bep_path,
org_url_slug,
token,
repo_root,
Expand All @@ -121,21 +129,26 @@ async fn run_test(test_args: TestArgs) -> anyhow::Result<i32> {
repo_head_commit_epoch.clone(),
)?;

if junit_paths.is_empty() {
return Err(anyhow::anyhow!("No junit paths provided."));
if junit_paths.is_empty() && bazel_bep_path.is_none() {
return Err(anyhow::anyhow!("No junit or bazel BEP paths provided."));
}

let api_client = ApiClient::new(String::from(token))?;

let codeowners = CodeOwners::find_file(&repo.repo_root, codeowners_path);
let junit_spec = if !junit_paths.is_empty() {
JunitSpec::Paths(junit_paths.clone())
} else {
JunitSpec::BazelBep(bazel_bep_path.clone().unwrap())
};

log::info!("running command: {:?}", command);
let run_result = run_test_command(
&repo,
&org_url_slug,
command.first().unwrap(),
command.iter().skip(1).collect(),
junit_paths,
&junit_spec,
team.clone(),
&codeowners,
)
Expand Down Expand Up @@ -200,11 +213,22 @@ async fn run(cli: Cli) -> anyhow::Result<i32> {
Commands::Validate(validate_args) => {
let ValidateArgs {
junit_paths,
bazel_bep_path,
show_warnings,
codeowners_path,
} = validate_args;

let junit_file_paths = match bazel_bep_path {
Some(bazel_bep_path) => {
let mut parser = BazelBepParser::new(bazel_bep_path.clone());
parser.parse()?;
parser.xml_files()
}
None => junit_paths,
};

print_cli_start_info();
validate(junit_paths, show_warnings, codeowners_path).await
validate(junit_file_paths, show_warnings, codeowners_path).await
}
}
}
Expand Down
19 changes: 17 additions & 2 deletions cli/src/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,37 @@ use bundle::{
};
use codeowners::CodeOwners;
use constants::{EXIT_FAILURE, EXIT_SUCCESS};
use context::{junit::parser::JunitParser, repo::BundleRepo};
use context::{bazel_bep::parser::BazelBepParser, junit::parser::JunitParser, repo::BundleRepo};

use crate::api_client::ApiClient;

pub enum JunitSpec {
Paths(Vec<String>),
BazelBep(String),
}

pub async fn run_test_command(
repo: &BundleRepo,
org_slug: &str,
command: &String,
args: Vec<&String>,
output_paths: &[String],
junit_spec: &JunitSpec,
team: Option<String>,
codeowners: &Option<CodeOwners>,
) -> anyhow::Result<RunResult> {
let start = SystemTime::now();
let exit_code = run_test_and_get_exit_code(command, args).await?;
log::info!("Command exit code: {}", exit_code);

let output_paths = match junit_spec {
JunitSpec::Paths(paths) => paths,
JunitSpec::BazelBep(bep_path) => {
let mut parser = BazelBepParser::new(bep_path.clone());
parser.parse()?;
&parser.xml_files()
}
};

let (file_sets, ..) =
build_filesets(&repo.repo_root, output_paths, team, codeowners, Some(start))?;
let failures = if exit_code != EXIT_SUCCESS {
Expand Down
28 changes: 22 additions & 6 deletions cli/src/upload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use codeowners::CodeOwners;
use constants::{EXIT_FAILURE, EXIT_SUCCESS};
#[cfg(target_os = "macos")]
use context::repo::RepoUrlParts;
use context::{junit::parser::JunitParser, repo::BundleRepo};
use context::{bazel_bep::parser::BazelBepParser, junit::parser::JunitParser, repo::BundleRepo};

use crate::{
api_client::ApiClient,
Expand All @@ -30,14 +30,25 @@ use crate::{
pub struct UploadArgs {
#[arg(
long,
required_unless_present = junit_require(),
required_unless_present_any = [junit_require(), "bazel_bep_path"],
conflicts_with = "bazel_bep_path",
value_delimiter = ',',
value_parser = clap::builder::NonEmptyStringValueParser::new(),
help = "Comma-separated list of glob paths to junit files."
)]
pub junit_paths: Vec<String>,
#[arg(
long,
required_unless_present_any = [junit_require(), "junit_paths"],
help = "Path to bazel build event protocol JSON file."
)]
pub bazel_bep_path: Option<String>,
#[cfg(target_os = "macos")]
#[arg(long, required = false, help = "Path of xcresult directory")]
#[arg(long,
required_unless_present_any = ["junit_paths", "bazel_bep_path"],
conflicts_with_all = ["junit_paths", "bazel_bep_path"],
required = false, help = "Path of xcresult directory"
)]
pub xcresult_path: Option<String>,
#[arg(long, help = "Organization url slug.")]
pub org_url_slug: String,
Expand Down Expand Up @@ -95,12 +106,10 @@ pub async fn run_upload(
exec_start: Option<SystemTime>,
) -> anyhow::Result<i32> {
let UploadArgs {
#[cfg(target_os = "macos")]
mut junit_paths,
#[cfg(target_os = "linux")]
junit_paths,
#[cfg(target_os = "macos")]
xcresult_path,
bazel_bep_path,
org_url_slug,
token,
repo_root,
Expand Down Expand Up @@ -134,6 +143,12 @@ pub async fn run_upload(
let codeowners =
codeowners.or_else(|| CodeOwners::find_file(&repo.repo_root, &codeowners_path));

if let Some(bazel_bep_path) = bazel_bep_path {
let mut parser = BazelBepParser::new(bazel_bep_path.clone());
parser.parse()?;
junit_paths = parser.xml_files();
}

let tags = parse_custom_tags(&tags)?;
#[cfg(target_os = "macos")]
let junit_temp_dir = tempfile::tempdir()?;
Expand Down Expand Up @@ -258,6 +273,7 @@ pub async fn run_upload(

log::info!("Total files pack and upload: {}", file_counter.get_count());
if file_counter.get_count() == 0 {
// DONOTLAND FIX LOG MESSAGE HERE
log::warn!(
"No JUnit files found to pack and upload using globs: {:?}",
junit_paths
Expand Down
2 changes: 2 additions & 0 deletions context/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ tempfile = "3.2.0"

[dependencies]
anyhow = "1.0.44"
# Fork from 0.2.2 that adds serde serialize/deserialize via pbjson
bazel-bep = { git = "https://github.com/TylerJang27/bazel-bep.git", rev = "e51c546960067b9fe98ae35ae00bc53302973a9e" }
chrono = "0.4.33"
gix = { version = "0.67.0", default-features = false, features = [
], optional = true }
Expand Down
1 change: 1 addition & 0 deletions context/src/bazel_bep/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod parser;
99 changes: 99 additions & 0 deletions context/src/bazel_bep/parser.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
use anyhow::Ok;
use bazel_bep::types::build_event_stream::{build_event::Payload, file::File::Uri, BuildEvent};
use serde_json::Deserializer;

#[derive(Debug, Clone)]
pub struct TestResult {
pub cached: bool,
pub xml_files: Vec<String>,
}

const FILE_URI_PREFIX: &str = "file://";

/// Uses proto spec
/// https://github.com/TylerJang27/bazel-bep/blob/master/proto/build_event_stream.proto based on
/// https://github.com/bazelbuild/bazel/blob/master/src/main/java/com/google/devtools/build/lib/buildeventstream/proto/build_event_stream.proto
pub struct BazelBepParser {
bazel_bep_path: String,
test_results: Vec<TestResult>,
errors: Vec<String>,
}

impl BazelBepParser {
pub fn new(bazel_bep_path: String) -> Self {
Self {
bazel_bep_path,
test_results: Vec::new(),
errors: Vec::new(),
}
}

pub fn errors(&self) -> &Vec<String> {
&self.errors
}

pub fn xml_files(&self) -> Vec<String> {
self.test_results
.iter()
.filter_map(|r| {
if r.cached {
return None;
}
Some(r.xml_files.clone())
})
.flatten()
.collect()
}

pub fn parse(&mut self) -> anyhow::Result<()> {
let file = std::fs::File::open(&self.bazel_bep_path)?;
let reader = std::io::BufReader::new(file);

let build_events = Deserializer::from_reader(reader).into_iter::<BuildEvent>();
build_events.for_each(|parse_event| {
if let Some(err) = parse_event.as_ref().err() {
self.errors
.push(format!("Error parsing build event: {}", err));
return;
}
if let Some(build_event) = parse_event.ok() {
if let Some(Payload::TestResult(test_result)) = build_event.payload {
let xml_files = test_result.test_action_output.into_iter().fold(
Vec::new(),
|mut xml_files, action_output| {
if action_output.name.ends_with(".xml") {
if let Some(Uri(file)) = action_output.file {
xml_files.push(
file.strip_prefix(FILE_URI_PREFIX)
.unwrap_or(&file)
.to_string(),
);
}
}
xml_files
},
);

let cached = if let Some(execution_info) = test_result.execution_info {
execution_info.cached_remotely || test_result.cached_locally
} else {
test_result.cached_locally
};

self.test_results.push(TestResult { cached, xml_files });
}
}
});

if !&self.errors.is_empty() {
log::warn!("Errors parsing BEP file: {:?}", &self.errors);
}

log::info!(
"Parsed {} ({} cached) test results from BEP file",
self.test_results.len(),
self.test_results.iter().filter(|r| r.cached).count()
);
Ok(())
}
}
1 change: 1 addition & 0 deletions context/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
/* trunk-ignore(clippy/E0554) */
#![feature(round_char_boundary)]

pub mod bazel_bep;
pub mod env;
pub mod junit;
pub mod repo;
Expand Down

0 comments on commit 00cd6bc

Please sign in to comment.