From e95f9d687c4a4c8cf154a4ef521288d1c4246b89 Mon Sep 17 00:00:00 2001 From: Dylan Frankland Date: Fri, 27 Dec 2024 07:57:45 -0800 Subject: [PATCH] organize cli commands and imports --- cli/src/api_client/mod.rs | 8 +- cli/src/lib.rs | 1 + cli/src/main.rs | 182 ++------------------------------------ cli/src/runner.rs | 10 ++- cli/src/test.rs | 128 +++++++++++++++++++++++++++ cli/src/upload.rs | 6 +- cli/src/validate.rs | 70 ++++++++++++--- 7 files changed, 209 insertions(+), 196 deletions(-) create mode 100644 cli/src/test.rs diff --git a/cli/src/api_client/mod.rs b/cli/src/api_client/mod.rs index d5c6c505..aa038104 100644 --- a/cli/src/api_client/mod.rs +++ b/cli/src/api_client/mod.rs @@ -1,12 +1,12 @@ -use anyhow::Context; -use http::{header::HeaderMap, HeaderValue}; -use reqwest::{header, Client, Response, StatusCode}; use std::path::Path; -use tokio::fs; +use anyhow::Context; use api; use call_api::CallApi; use constants::{DEFAULT_ORIGIN, TRUNK_PUBLIC_API_ADDRESS_ENV}; +use http::{header::HeaderMap, HeaderValue}; +use reqwest::{header, Client, Response, StatusCode}; +use tokio::fs; mod call_api; diff --git a/cli/src/lib.rs b/cli/src/lib.rs index 008801fc..2c835fd1 100644 --- a/cli/src/lib.rs +++ b/cli/src/lib.rs @@ -2,5 +2,6 @@ pub mod api_client; pub mod print; pub mod runner; pub mod scanner; +pub mod test; pub mod upload; pub mod validate; diff --git a/cli/src/main.rs b/cli/src/main.rs index 3b2799d1..e6c3ec30 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -1,20 +1,12 @@ -use std::env; -use std::io::Write; +use std::{env, io::Write}; + +use clap::{Parser, Subcommand}; +use constants::SENTRY_DSN; -use bundle::RunResult; -use clap::{Args, Parser, Subcommand}; -use codeowners::CodeOwners; -use constants::{EXIT_FAILURE, SENTRY_DSN}; -use context::{ - bazel_bep::parser::BazelBepParser, junit::junit_path::JunitReportFileWithStatus, - repo::BundleRepo, -}; use trunk_analytics_cli::{ - api_client::ApiClient, - print::print_bep_results, - runner::{run_quarantine, run_test_command, JunitSpec}, + test::{run_test, TestArgs}, upload::{run_upload, UploadArgs}, - validate::validate, + validate::{run_validate, ValidateArgs}, }; #[derive(Debug, Parser)] @@ -29,42 +21,6 @@ struct Cli { pub command: Commands, } -#[derive(Args, Clone, Debug)] -struct TestArgs { - #[command(flatten)] - upload_args: UploadArgs, - #[arg( - required = true, - allow_hyphen_values = true, - trailing_var_arg = true, - help = "Test command to invoke." - )] - command: Vec, -} - -#[derive(Args, Clone, Debug)] -struct ValidateArgs { - #[arg( - long, - 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, - #[arg( - long, - required_unless_present = "junit_paths", - help = "Path to bazel build event protocol JSON file." - )] - bazel_bep_path: Option, - #[arg(long, help = "Show warning-level log messages in output.")] - show_warnings: bool, - #[arg(long, help = "Value to override CODEOWNERS file or directory path.")] - pub codeowners_path: Option, -} - #[derive(Debug, Subcommand)] enum Commands { /// Upload data to Trunk Flaky Tests @@ -110,110 +66,6 @@ fn main() -> anyhow::Result<()> { }) } -async fn run_test(test_args: TestArgs) -> anyhow::Result { - let TestArgs { - command, - upload_args, - } = test_args; - let UploadArgs { - junit_paths, - bazel_bep_path, - org_url_slug, - token, - repo_root, - repo_url, - repo_head_sha, - repo_head_branch, - repo_head_commit_epoch, - use_quarantining, - team, - codeowners_path, - .. - } = &upload_args; - - let repo = BundleRepo::new( - repo_root.clone(), - repo_url.clone(), - repo_head_sha.clone(), - repo_head_branch.clone(), - repo_head_commit_epoch.clone(), - )?; - - if junit_paths.is_empty() && bazel_bep_path.is_none() { - return Err(anyhow::anyhow!("No junit 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.as_deref().unwrap_or_default().to_string()) - }; - - log::info!("running command: {:?}", command); - let run_result = run_test_command( - &repo, - &org_url_slug, - command.first().unwrap(), - command.iter().skip(1).collect(), - junit_spec, - team.clone(), - &codeowners, - ) - .await - .unwrap_or_else(|e| { - log::error!("Test command failed to run: {}", e); - RunResult { - exit_code: EXIT_FAILURE, - failures: Vec::new(), - exec_start: None, - } - }); - - let run_exit_code = run_result.exit_code; - let failures = run_result.failures; - - let quarantine_run_result = if *use_quarantining { - Some( - run_quarantine( - &api_client, - &api::GetQuarantineBulkTestStatusRequest { - repo: repo.repo, - org_url_slug: org_url_slug.clone(), - test_identifiers: failures.clone(), - }, - failures, - run_exit_code, - ) - .await, - ) - } else { - None - }; - - let exit_code = quarantine_run_result - .as_ref() - .map(|r| r.exit_code) - .unwrap_or(run_exit_code); - - let exec_start = run_result.exec_start; - if let Err(e) = run_upload( - upload_args, - Some(command.join(" ")), - None, // don't re-run quarantine checks - codeowners, - exec_start, - ) - .await - { - log::error!("Error uploading test results: {:?}", e) - }; - - Ok(exit_code) -} - async fn run(cli: Cli) -> anyhow::Result { match cli.command { Commands::Upload(upload_args) => { @@ -222,28 +74,8 @@ async fn run(cli: Cli) -> anyhow::Result { } Commands::Test(test_args) => run_test(test_args).await, Commands::Validate(validate_args) => { - let ValidateArgs { - junit_paths, - bazel_bep_path, - show_warnings, - codeowners_path, - } = validate_args; - print_cli_start_info(); - - let junit_file_paths = match bazel_bep_path { - Some(bazel_bep_path) => { - let mut parser = BazelBepParser::new(bazel_bep_path); - let bep_result = parser.parse()?; - print_bep_results(&bep_result); - bep_result.uncached_xml_files() - } - None => junit_paths - .into_iter() - .map(JunitReportFileWithStatus::from) - .collect(), - }; - validate(junit_file_paths, show_warnings, codeowners_path).await + run_validate(validate_args).await } } } diff --git a/cli/src/runner.rs b/cli/src/runner.rs index dc69bf23..c1367b99 100644 --- a/cli/src/runner.rs +++ b/cli/src/runner.rs @@ -1,7 +1,8 @@ -use quick_junit::TestCaseStatus; -use std::collections::HashMap; -use std::process::{Command, Stdio}; -use std::time::SystemTime; +use std::{ + collections::HashMap, + process::{Command, Stdio}, + time::SystemTime, +}; use api; use bundle::{ @@ -17,6 +18,7 @@ use context::{ }, repo::BundleRepo, }; +use quick_junit::TestCaseStatus; use crate::{api_client::ApiClient, print::print_bep_results}; diff --git a/cli/src/test.rs b/cli/src/test.rs new file mode 100644 index 00000000..10882510 --- /dev/null +++ b/cli/src/test.rs @@ -0,0 +1,128 @@ +use bundle::RunResult; +use clap::Args; +use codeowners::CodeOwners; +use constants::EXIT_FAILURE; +use context::repo::BundleRepo; + +use crate::{ + api_client::ApiClient, + runner::{run_quarantine, run_test_command, JunitSpec}, + upload::{run_upload, UploadArgs}, +}; + +#[derive(Args, Clone, Debug)] +pub struct TestArgs { + #[command(flatten)] + upload_args: UploadArgs, + #[arg( + required = true, + allow_hyphen_values = true, + trailing_var_arg = true, + help = "Test command to invoke." + )] + command: Vec, +} + +pub async fn run_test(test_args: TestArgs) -> anyhow::Result { + let TestArgs { + command, + upload_args, + } = test_args; + let UploadArgs { + junit_paths, + bazel_bep_path, + org_url_slug, + token, + repo_root, + repo_url, + repo_head_sha, + repo_head_branch, + repo_head_commit_epoch, + use_quarantining, + team, + codeowners_path, + .. + } = &upload_args; + + let repo = BundleRepo::new( + repo_root.clone(), + repo_url.clone(), + repo_head_sha.clone(), + repo_head_branch.clone(), + repo_head_commit_epoch.clone(), + )?; + + if junit_paths.is_empty() && bazel_bep_path.is_none() { + return Err(anyhow::anyhow!("No junit 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.as_deref().unwrap_or_default().to_string()) + }; + + log::info!("running command: {:?}", command); + let run_result = run_test_command( + &repo, + org_url_slug, + command.first().unwrap(), + command.iter().skip(1).collect(), + junit_spec, + team.clone(), + &codeowners, + ) + .await + .unwrap_or_else(|e| { + log::error!("Test command failed to run: {}", e); + RunResult { + exit_code: EXIT_FAILURE, + failures: Vec::new(), + exec_start: None, + } + }); + + let run_exit_code = run_result.exit_code; + let failures = run_result.failures; + + let quarantine_run_result = if *use_quarantining { + Some( + run_quarantine( + &api_client, + &api::GetQuarantineBulkTestStatusRequest { + repo: repo.repo, + org_url_slug: org_url_slug.clone(), + test_identifiers: failures.clone(), + }, + failures, + run_exit_code, + ) + .await, + ) + } else { + None + }; + + let exit_code = quarantine_run_result + .as_ref() + .map(|r| r.exit_code) + .unwrap_or(run_exit_code); + + let exec_start = run_result.exec_start; + if let Err(e) = run_upload( + upload_args, + Some(command.join(" ")), + None, // don't re-run quarantine checks + codeowners, + exec_start, + ) + .await + { + log::error!("Error uploading test results: {:?}", e) + }; + + Ok(exit_code) +} diff --git a/cli/src/upload.rs b/cli/src/upload.rs index 252b0add..0fa2f282 100644 --- a/cli/src/upload.rs +++ b/cli/src/upload.rs @@ -1,4 +1,3 @@ -use clap::{ArgAction, Args}; #[cfg(target_os = "macos")] use std::io::Write; use std::{ @@ -6,14 +5,13 @@ use std::{ io::BufReader, time::{SystemTime, UNIX_EPOCH}, }; -#[cfg(target_os = "macos")] -use xcresult::XCResult; use api::BundleUploadStatus; use bundle::{ parse_custom_tags, BundleMeta, BundleMetaBaseProps, BundleMetaDebugProps, BundleMetaJunitProps, BundlerUtil, FileSet, QuarantineBulkTestStatus, QuarantineRunResult, META_VERSION, }; +use clap::{ArgAction, Args}; use codeowners::CodeOwners; use constants::{EXIT_FAILURE, EXIT_SUCCESS}; #[cfg(target_os = "macos")] @@ -23,6 +21,8 @@ use context::{ junit::{junit_path::JunitReportFileWithStatus, parser::JunitParser}, repo::BundleRepo, }; +#[cfg(target_os = "macos")] +use xcresult::XCResult; use crate::{ api_client::ApiClient, diff --git a/cli/src/validate.rs b/cli/src/validate.rs index ed48e243..b2173e68 100644 --- a/cli/src/validate.rs +++ b/cli/src/validate.rs @@ -1,27 +1,77 @@ -use quick_junit::Report; use std::{collections::BTreeMap, io::BufReader}; use bundle::{FileSet, FileSetCounter}; +use clap::{arg, Args}; use codeowners::CodeOwners; use colored::{ColoredString, Colorize}; use console::Emoji; use constants::{EXIT_FAILURE, EXIT_SUCCESS}; -use context::junit::{ - junit_path::JunitReportFileWithStatus, - parser::{JunitParseError, JunitParser}, - validator::{ - validate as validate_report, JunitReportValidation, JunitReportValidationFlatIssue, - JunitReportValidationIssueSubOptimal, JunitValidationIssue, JunitValidationIssueType, - JunitValidationLevel, +use context::{ + bazel_bep::parser::BazelBepParser, + junit::{ + junit_path::JunitReportFileWithStatus, + parser::{JunitParseError, JunitParser}, + validator::{ + validate as validate_report, JunitReportValidation, JunitReportValidationFlatIssue, + JunitReportValidationIssueSubOptimal, JunitValidationIssue, JunitValidationIssueType, + JunitValidationLevel, + }, }, }; +use quick_junit::Report; -use crate::runner::build_filesets; +use crate::{print::print_bep_results, runner::build_filesets}; + +#[derive(Args, Clone, Debug)] +pub struct ValidateArgs { + #[arg( + long, + 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, + #[arg( + long, + required_unless_present = "junit_paths", + help = "Path to bazel build event protocol JSON file." + )] + bazel_bep_path: Option, + #[arg(long, help = "Show warning-level log messages in output.")] + show_warnings: bool, + #[arg(long, help = "Value to override CODEOWNERS file or directory path.")] + pub codeowners_path: Option, +} + +pub async fn run_validate(validate_args: ValidateArgs) -> anyhow::Result { + 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); + let bep_result = parser.parse()?; + print_bep_results(&bep_result); + bep_result.uncached_xml_files() + } + None => junit_paths + .into_iter() + .map(JunitReportFileWithStatus::from) + .collect(), + }; + validate(junit_file_paths, show_warnings, codeowners_path).await +} type JunitFileToReportAndErrors = BTreeMap, Vec)>; type JunitFileToValidation = BTreeMap>; -pub async fn validate( +async fn validate( junit_paths: Vec, show_warnings: bool, codeowners_path: Option,