From fa45209a5314721430e63748fba07b8c5cd3f572 Mon Sep 17 00:00:00 2001 From: gnalh Date: Wed, 20 Mar 2024 18:08:16 +0000 Subject: [PATCH] add test command --- src/clients.rs | 4 +- src/main.rs | 222 +++++++++++++++++++++++++++++++------------------ src/runner.rs | 8 +- 3 files changed, 145 insertions(+), 89 deletions(-) diff --git a/src/clients.rs b/src/clients.rs index 8d6276db..32cd5470 100644 --- a/src/clients.rs +++ b/src/clients.rs @@ -52,7 +52,7 @@ pub async fn get_quarantine_bulk_test_status( api_token: &str, org_slug: &str, repo: &Repo, - test_identifiers: &Vec, + test_identifiers: &[Test], ) -> anyhow::Result { let client = reqwest::Client::new(); let resp = match client @@ -62,7 +62,7 @@ pub async fn get_quarantine_bulk_test_status( .json(&GetQuarantineBulkTestStatusRequest { org_url_slug: org_slug.to_owned(), repo: repo.clone(), - test_identifiers: test_identifiers.clone(), + test_identifiers: test_identifiers.to_vec(), }) .send() .await diff --git a/src/main.rs b/src/main.rs index f90aa07e..19743f66 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ use std::io::Write; use std::time::{SystemTime, UNIX_EPOCH}; -use clap::{Parser, Subcommand}; +use clap::{Args, Parser, Subcommand}; use tokio_retry::strategy::ExponentialBackoff; use tokio_retry::Retry; use trunk_analytics_cli::bundler::BundlerUtil; @@ -23,54 +23,63 @@ struct Cli { pub command: Commands, } +#[derive(Args, Clone, Debug)] +struct UploadArgs { + #[arg( + long, + required = true, + value_delimiter = ',', + help = "Comma-separated list of glob paths to junit files." + )] + junit_paths: Vec, + #[arg(long, help = "Organization url slug.")] + org_url_slug: String, + #[arg( + long, + required = true, + env = "TRUNK_API_TOKEN", + help = "Organization token. Defaults to TRUNK_API_TOKEN env var." + )] + token: String, + #[arg(long, help = "Path to repository root. Defaults to current directory.")] + repo_root: Option, + #[arg(long, help = "Value to override URL of repository.")] + repo_url: Option, + #[arg(long, help = "Value to override SHA of repository head.")] + repo_head_sha: Option, + #[arg(long, help = "Value to override branch of repository head.")] + repo_head_branch: Option, + #[arg(long, help = "Value to override commit epoch of repository head.")] + repo_head_commit_epoch: Option, + #[arg( + long, + value_delimiter = ',', + help = "Comma separated list of custom tag=value pairs." + )] + tags: Vec, + #[arg(long, help = "Print files which will be uploaded to stdout.")] + print_files: bool, + #[arg(long, help = "Run metrics CLI without uploading to API.")] + dry_run: bool, +} + +#[derive(Args, Clone, Debug)] +struct TestArgs { + #[command(flatten)] + upload_args: UploadArgs, + #[arg( + required = false, + allow_hyphen_values = true, + trailing_var_arg = true, + help = "Test command to invoke." + )] + command: Vec, +} + #[derive(Debug, Subcommand)] enum Commands { - #[clap(name = "upload")] - Upload { - #[arg( - long, - required = false, - value_delimiter = ' ', - help = "Test command to invoke." - )] - command: Vec, - #[arg( - long, - required = true, - value_delimiter = ',', - help = "Comma-separated list of glob paths to junit files." - )] - junit_paths: Vec, - #[arg(long, help = "Organization url slug.")] - org_url_slug: String, - #[arg( - long, - required = true, - env = "TRUNK_API_TOKEN", - help = "Organization token. Defaults to TRUNK_API_TOKEN env var." - )] - token: String, - #[arg(long, help = "Path to repository root. Defaults to current directory.")] - repo_root: Option, - #[arg(long, help = "Value to override URL of repository.")] - repo_url: Option, - #[arg(long, help = "Value to override SHA of repository head.")] - repo_head_sha: Option, - #[arg(long, help = "Value to override branch of repository head.")] - repo_head_branch: Option, - #[arg(long, help = "Value to override commit epoch of repository head.")] - repo_head_commit_epoch: Option, - #[arg( - long, - value_delimiter = ',', - help = "Comma separated list of custom tag=value pairs." - )] - tags: Vec, - #[arg(long, help = "Print files which will be uploaded to stdout.")] - print_files: bool, - #[arg(long, help = "Run metrics CLI without uploading to API.")] - dry_run: bool, - }, + Upload(UploadArgs), + Test(TestArgs), } const DEFAULT_ORIGIN: &str = "https://api.trunk.io"; @@ -93,9 +102,8 @@ async fn main() -> anyhow::Result<()> { } } -async fn run(cli: Cli) -> anyhow::Result { - let Commands::Upload { - command, +async fn run_upload(upload_args: UploadArgs) -> anyhow::Result { + let UploadArgs { junit_paths, org_url_slug, token, @@ -107,7 +115,7 @@ async fn run(cli: Cli) -> anyhow::Result { tags, print_files, dry_run, - } = cli.command; + } = upload_args; let repo = BundleRepo::try_read_from_root( repo_root, @@ -117,7 +125,7 @@ async fn run(cli: Cli) -> anyhow::Result { repo_head_commit_epoch, )?; - if junit_paths.len() == 0 { + if junit_paths.is_empty() { return Err(anyhow::anyhow!("No junit paths provided.")); } @@ -126,37 +134,7 @@ async fn run(cli: Cli) -> anyhow::Result { DEFAULT_ORIGIN.to_string(), |s| s, ); - log::info!("Using Trunk API address: {}", api_address); - - let mut exit_code: i32 = EXIT_SUCCESS; - - // run the command - if command.len() != 0 { - log::info!("running command: {:?}", command); - // check with the API if the group is quarantined - let run_result = run_test_command( - &repo, - command.get(0).unwrap(), - command.iter().skip(1).collect(), - junit_paths.iter().skip(0).collect(), - ) - .await?; - let quarantine_results = get_quarantine_bulk_test_status( - &api_address, - &token, - &org_url_slug, - &repo.repo, - &run_result.failures, - ) - .await?; - // use the exit code from the command if the group is not quarantined - // override exit code to be exit_success if the group is quarantined - if !run_result.exit_code != 0 && !quarantine_results.group_is_quarantined { - exit_code = run_result.exit_code; - } else { - exit_code = EXIT_SUCCESS; - } - } + let exit_code: i32 = EXIT_SUCCESS; log::info!( "Starting trunk-analytics-cli {} (git={}) rustc={}", @@ -260,6 +238,84 @@ async fn run(cli: Cli) -> anyhow::Result { Ok(exit_code) } +async fn run_test(test_args: TestArgs) -> anyhow::Result { + let TestArgs { + ref command, + upload_args, + } = test_args; + let UploadArgs { + ref junit_paths, + ref org_url_slug, + ref token, + ref repo_root, + ref repo_url, + ref repo_head_sha, + ref repo_head_branch, + ref repo_head_commit_epoch, + tags: _, + print_files: _, + dry_run: _, + } = upload_args; + + let repo = BundleRepo::try_read_from_root( + repo_root.clone(), + repo_url.clone(), + repo_head_sha.clone(), + repo_head_branch.clone(), + repo_head_commit_epoch.clone(), + )?; + + if junit_paths.is_empty() { + return Err(anyhow::anyhow!("No junit paths provided.")); + } + + let api_address = from_non_empty_or_default( + std::env::var("TRUNK_API_ADDRESS").ok(), + DEFAULT_ORIGIN.to_string(), + |s| s, + ); + + log::info!("running command: {:?}", command); + // check with the API if the group is quarantined + let run_result = run_test_command( + &repo, + command.first().unwrap(), + command.iter().skip(1).collect(), + junit_paths.iter().collect(), + ) + .await?; + let quarantine_results = get_quarantine_bulk_test_status( + &api_address, + token, + org_url_slug, + &repo.repo, + &run_result.failures, + ) + .await?; + // use the exit code from the command if the group is not quarantined + // override exit code to be exit_success if the group is quarantined + let exit_code = if !run_result.exit_code != 0 && !quarantine_results.group_is_quarantined { + run_result.exit_code + } else { + EXIT_SUCCESS + }; + + let upload_exit_code = run_upload(upload_args).await?; + // use the upload exit code if the command exit code is exit_success + if exit_code == EXIT_SUCCESS { + Ok(upload_exit_code) + } else { + Ok(exit_code) + } +} + +async fn run(cli: Cli) -> anyhow::Result { + match cli.command { + Commands::Upload(upload_args) => run_upload(upload_args).await, + Commands::Test(test_args) => run_test(test_args).await, + } +} + fn default_delay() -> std::iter::Take { ExponentialBackoff::from_millis(RETRY_BASE_MS) .factor(RETRY_FACTOR) diff --git a/src/runner.rs b/src/runner.rs index f9529ab4..0f917954 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -56,8 +56,8 @@ pub async fn run_test_command( } } let exit_code = result.code().unwrap_or(EXIT_FAILURE); - return Ok(RunResult { - exit_code: exit_code, - failures: failures, - }); + Ok(RunResult { + exit_code, + failures, + }) }