diff --git a/src/api_client.rs b/src/api_client.rs index 4eb0d8d6..159d5dfd 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -3,7 +3,7 @@ use std::fmt::Display; use crate::executor::ExecutorName; use crate::prelude::*; use crate::run_environment::RepositoryProvider; -use crate::{app::Cli, config::CodSpeedConfig}; +use crate::{cli::Cli, config::CodSpeedConfig}; use console::style; use gql_client::{Client as GQLClient, ClientConfig}; use nestify::nest; diff --git a/src/binary_installer/mod.rs b/src/binary_installer/mod.rs index fa5a6b41..c4f488f0 100644 --- a/src/binary_installer/mod.rs +++ b/src/binary_installer/mod.rs @@ -1,5 +1,5 @@ +use crate::cli::run::helpers::download_file; use crate::prelude::*; -use crate::run::helpers::download_file; use semver::Version; use std::process::Command; use tempfile::NamedTempFile; diff --git a/src/auth.rs b/src/cli/auth.rs similarity index 100% rename from src/auth.rs rename to src/cli/auth.rs diff --git a/src/exec/mod.rs b/src/cli/exec/mod.rs similarity index 95% rename from src/exec/mod.rs rename to src/cli/exec/mod.rs index 96d76cbc..15e9c670 100644 --- a/src/exec/mod.rs +++ b/src/cli/exec/mod.rs @@ -1,3 +1,4 @@ +use super::ExecAndRunSharedArgs; use crate::api_client::CodSpeedAPIClient; use crate::binary_installer::ensure_binary_installed; use crate::config::CodSpeedConfig; @@ -5,7 +6,7 @@ use crate::executor; use crate::prelude::*; use crate::project_config::ProjectConfig; use crate::project_config::merger::ConfigMerger; -use crate::run::uploader::UploadResult; +use crate::upload::UploadResult; use clap::Args; use std::path::Path; @@ -36,7 +37,7 @@ pub fn wrap_with_exec_harness( #[derive(Args, Debug)] pub struct ExecArgs { #[command(flatten)] - pub shared: crate::run::ExecAndRunSharedArgs, + pub shared: ExecAndRunSharedArgs, #[command(flatten)] pub walltime_args: exec_harness::walltime::WalltimeExecutionArgs, @@ -96,6 +97,11 @@ pub async fn execute_with_harness( setup_cache_dir: Option<&Path>, ) -> Result<()> { let mut execution_context = executor::ExecutionContext::try_from((config, codspeed_config))?; + + if execution_context.is_local() { + super::show_banner(); + } + debug!("config: {:#?}", execution_context.config); let executor = executor::get_executor_from_mode( &execution_context.config.mode, diff --git a/src/exec/multi_targets.rs b/src/cli/exec/multi_targets.rs similarity index 100% rename from src/exec/multi_targets.rs rename to src/cli/exec/multi_targets.rs diff --git a/src/cli/exec/poll_results.rs b/src/cli/exec/poll_results.rs new file mode 100644 index 00000000..1812bf73 --- /dev/null +++ b/src/cli/exec/poll_results.rs @@ -0,0 +1,34 @@ +use console::style; + +use crate::api_client::CodSpeedAPIClient; +use crate::cli::run::helpers::benchmark_display::{build_benchmark_table, build_detailed_summary}; +use crate::prelude::*; +use crate::upload::{UploadResult, poll_run_report}; + +#[allow(clippy::borrowed_box)] +pub async fn poll_results( + api_client: &CodSpeedAPIClient, + upload_result: &UploadResult, +) -> Result<()> { + let response = poll_run_report(api_client, upload_result).await?; + + if !response.run.results.is_empty() { + end_group!(); + start_group!("Benchmark results"); + + if response.run.results.len() == 1 { + let summary = build_detailed_summary(&response.run.results[0]); + info!("\n{summary}"); + } else { + let table = build_benchmark_table(&response.run.results); + info!("\n{table}"); + } + + info!( + "\nTo see the full report, visit: {}", + style(response.run.url).blue().bold().underlined() + ); + } + + Ok(()) +} diff --git a/src/app.rs b/src/cli/mod.rs similarity index 94% rename from src/app.rs rename to src/cli/mod.rs index 469fbe94..df313b13 100644 --- a/src/app.rs +++ b/src/cli/mod.rs @@ -1,14 +1,20 @@ +mod auth; +pub(crate) mod exec; +pub(crate) mod run; +mod setup; +mod shared; +mod use_mode; + +pub(crate) use shared::*; + use std::path::PathBuf; use crate::{ api_client::CodSpeedAPIClient, - auth, config::CodSpeedConfig, - exec, local_logger::{CODSPEED_U8_COLOR_CODE, init_local_logger}, prelude::*, project_config::ProjectConfig, - run, setup, }; use clap::{ Parser, Subcommand, @@ -78,6 +84,8 @@ enum Commands { Auth(auth::AuthArgs), /// Pre-install the codspeed executors Setup, + /// Set the codspeed mode for the rest of the shell session + Use(use_mode::UseArgs), } pub async fn run() -> Result<()> { @@ -127,6 +135,7 @@ pub async fn run() -> Result<()> { } Commands::Auth(args) => auth::run(args, &api_client, cli.config_name.as_deref()).await?, Commands::Setup => setup::setup(setup_cache_dir).await?, + Commands::Use(args) => use_mode::run(args)?, } Ok(()) } diff --git a/src/run/helpers/benchmark_display.rs b/src/cli/run/helpers/benchmark_display.rs similarity index 98% rename from src/run/helpers/benchmark_display.rs rename to src/cli/run/helpers/benchmark_display.rs index 3bef377b..23f05dd4 100644 --- a/src/run/helpers/benchmark_display.rs +++ b/src/cli/run/helpers/benchmark_display.rs @@ -1,17 +1,13 @@ use crate::api_client::FetchLocalRunBenchmarkResult; +use crate::cli::run::helpers; use crate::executor::ExecutorName; -use crate::run::helpers; use std::collections::HashMap; -use std::time::Duration; use tabled::settings::object::{Columns, Rows}; use tabled::settings::panel::Panel; use tabled::settings::style::HorizontalLine; use tabled::settings::{Alignment, Color, Modify, Style}; use tabled::{Table, Tabled}; -pub const RUN_PROCESSING_MAX_DURATION: Duration = Duration::from_secs(60 * 5); // 5 minutes -pub const POLLING_INTERVAL: Duration = Duration::from_secs(1); - fn format_with_thousands_sep(n: u64) -> String { let s = n.to_string(); let mut result = String::new(); diff --git a/src/run/helpers/download_file.rs b/src/cli/run/helpers/download_file.rs similarity index 100% rename from src/run/helpers/download_file.rs rename to src/cli/run/helpers/download_file.rs diff --git a/src/run/helpers/find_repository_root.rs b/src/cli/run/helpers/find_repository_root.rs similarity index 100% rename from src/run/helpers/find_repository_root.rs rename to src/cli/run/helpers/find_repository_root.rs diff --git a/src/run/helpers/format_duration.rs b/src/cli/run/helpers/format_duration.rs similarity index 100% rename from src/run/helpers/format_duration.rs rename to src/cli/run/helpers/format_duration.rs diff --git a/src/run/helpers/format_memory.rs b/src/cli/run/helpers/format_memory.rs similarity index 100% rename from src/run/helpers/format_memory.rs rename to src/cli/run/helpers/format_memory.rs diff --git a/src/run/helpers/get_env_var.rs b/src/cli/run/helpers/get_env_var.rs similarity index 100% rename from src/run/helpers/get_env_var.rs rename to src/cli/run/helpers/get_env_var.rs diff --git a/src/run/helpers/mod.rs b/src/cli/run/helpers/mod.rs similarity index 100% rename from src/run/helpers/mod.rs rename to src/cli/run/helpers/mod.rs diff --git a/src/run/helpers/parse_git_remote.rs b/src/cli/run/helpers/parse_git_remote.rs similarity index 100% rename from src/run/helpers/parse_git_remote.rs rename to src/cli/run/helpers/parse_git_remote.rs diff --git a/src/cli/run/helpers/snapshots/codspeed_runner__cli__run__helpers__benchmark_display__tests__benchmark_table_formatting.snap b/src/cli/run/helpers/snapshots/codspeed_runner__cli__run__helpers__benchmark_display__tests__benchmark_table_formatting.snap new file mode 100644 index 00000000..9189e67b --- /dev/null +++ b/src/cli/run/helpers/snapshots/codspeed_runner__cli__run__helpers__benchmark_display__tests__benchmark_table_formatting.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:75c9e10b85ec7f6ec4b8e89975bd19c617a82f7c321ce0688834c03c86b9ea02 +size 3501 diff --git a/src/run/logger.rs b/src/cli/run/logger.rs similarity index 100% rename from src/run/logger.rs rename to src/cli/run/logger.rs diff --git a/src/run/mod.rs b/src/cli/run/mod.rs similarity index 57% rename from src/run/mod.rs rename to src/cli/run/mod.rs index 47221f27..af991b00 100644 --- a/src/run/mod.rs +++ b/src/cli/run/mod.rs @@ -1,4 +1,4 @@ -use crate::VERSION; +use super::ExecAndRunSharedArgs; use crate::api_client::CodSpeedAPIClient; use crate::config::CodSpeedConfig; use crate::executor; @@ -6,133 +6,14 @@ use crate::executor::Config; use crate::prelude::*; use crate::project_config::ProjectConfig; use crate::project_config::merger::ConfigMerger; -use crate::run::uploader::UploadResult; use crate::run_environment::interfaces::RepositoryProvider; -use crate::runner_mode::RunnerMode; +use crate::upload::UploadResult; use clap::{Args, ValueEnum}; use std::path::Path; -use std::path::PathBuf; -pub mod check_system; pub mod helpers; -pub(crate) mod poll_results; -pub mod run_index_state; -pub(crate) mod uploader; - pub mod logger; - -pub(crate) fn show_banner() { - let banner = format!( - r#" - ______ __ _____ __ - / ____/____ ____/ // ___/ ____ ___ ___ ____/ / - / / / __ \ / __ / \__ \ / __ \ / _ \ / _ \ / __ / -/ /___ / /_/ // /_/ / ___/ // /_/ // __// __// /_/ / -\____/ \____/ \__,_/ /____// .___/ \___/ \___/ \__,_/ - https://codspeed.io /_/ runner v{VERSION} -"# - ); - println!("{banner}"); - debug!("codspeed v{VERSION}"); -} - -#[derive(Debug, Copy, Clone, PartialEq, ValueEnum, Default)] -pub enum UnwindingMode { - /// Use the frame pointer for unwinding. Requires the binary to be compiled with frame pointers enabled. - #[clap(name = "fp")] - FramePointer, - - /// Use DWARF unwinding. This does not require any special compilation flags and is enabled by default. - #[default] - Dwarf, -} - -#[derive(Args, Debug, Clone)] -pub struct PerfRunArgs { - /// Enable the linux perf profiler to collect granular performance data. - /// This is only supported on Linux. - #[arg(long, env = "CODSPEED_PERF_ENABLED", default_value_t = true)] - pub enable_perf: bool, - - /// The unwinding mode that should be used with perf to collect the call stack. - #[arg(long, env = "CODSPEED_PERF_UNWINDING_MODE")] - pub perf_unwinding_mode: Option, -} - -/// Arguments shared between run and exec commands -#[derive(Args, Debug, Clone)] -pub struct ExecAndRunSharedArgs { - /// The upload URL to use for uploading the results, useful for on-premises installations - #[arg(long, env = "CODSPEED_UPLOAD_URL")] - pub upload_url: Option, - - /// The token to use for uploading the results, - /// - /// It can be either a CodSpeed token retrieved from the repository setting - /// or an OIDC token issued by the identity provider. - #[arg(long, env = "CODSPEED_TOKEN")] - pub token: Option, - - /// The repository the benchmark is associated with, under the format `owner/repo`. - #[arg(short, long, env = "CODSPEED_REPOSITORY")] - pub repository: Option, - - /// The repository provider to use in case --repository is used. Defaults to github - #[arg( - long, - env = "CODSPEED_PROVIDER", - requires = "repository", - ignore_case = true - )] - pub provider: Option, - - /// The directory where the command will be executed. - #[arg(long)] - pub working_directory: Option, - - /// The mode to run the benchmarks in. - #[arg(short, long, value_enum, env = "CODSPEED_RUNNER_MODE")] - pub mode: RunnerMode, - - /// Profile folder to use for the run. - #[arg(long)] - pub profile_folder: Option, - - /// Only for debugging purposes, skips the upload of the results - #[arg( - long, - default_value = "false", - hide = true, - env = "CODSPEED_SKIP_UPLOAD" - )] - pub skip_upload: bool, - - /// Used internally to upload the results after running the benchmarks in a sandbox environment - /// with no internet access - #[arg(long, default_value = "false", hide = true)] - pub skip_run: bool, - - /// Only for debugging purposes, skips the setup of the runner - #[arg(long, default_value = "false", hide = true)] - pub skip_setup: bool, - - /// Allow runs without any benchmarks to succeed instead of failing - #[arg(long, default_value = "false", hide = true)] - pub allow_empty: bool, - - /// The version of the go-runner to use (e.g., 1.2.3, 1.0.0-beta.1) - /// If not specified, the latest version will be installed - #[arg(long, env = "CODSPEED_GO_RUNNER_VERSION", value_parser = parse_version)] - pub go_runner_version: Option, - - #[command(flatten)] - pub perf_run_args: PerfRunArgs, -} - -/// Parser for go-runner version that validates semver format -fn parse_version(s: &str) -> Result { - semver::Version::parse(s).map_err(|e| format!("Invalid semantic version: {e}")) -} +mod poll_results; #[derive(Args, Debug)] pub struct RunArgs { @@ -179,6 +60,9 @@ pub enum MessageFormat { impl RunArgs { /// Constructs a new `RunArgs` with default values for testing purposes pub fn test() -> Self { + use super::PerfRunArgs; + use crate::RunnerMode; + Self { shared: ExecAndRunSharedArgs { upload_url: None, @@ -186,7 +70,7 @@ impl RunArgs { repository: None, provider: None, working_directory: None, - mode: RunnerMode::Simulation, + mode: Some(RunnerMode::Simulation), profile_folder: None, skip_upload: false, skip_run: false, @@ -263,7 +147,7 @@ pub async fn run( executor::ExecutionContext::try_from((config, codspeed_config))?; if !execution_context.is_local() { - show_banner(); + super::show_banner(); } debug!("config: {:#?}", execution_context.config); @@ -292,10 +176,10 @@ pub async fn run( default_walltime, } => { args.command = - crate::exec::multi_targets::build_pipe_command(targets, default_walltime)?; + super::exec::multi_targets::build_pipe_command(targets, default_walltime)?; let config = Config::try_from(args)?; - crate::exec::execute_with_harness(config, api_client, codspeed_config, setup_cache_dir) + super::exec::execute_with_harness(config, api_client, codspeed_config, setup_cache_dir) .await?; } } diff --git a/src/run/poll_results.rs b/src/cli/run/poll_results.rs similarity index 57% rename from src/run/poll_results.rs rename to src/cli/run/poll_results.rs index e64884e7..acc7b470 100644 --- a/src/run/poll_results.rs +++ b/src/cli/run/poll_results.rs @@ -1,14 +1,9 @@ use console::style; -use tokio::time::{Instant, sleep}; -use crate::api_client::{ - CodSpeedAPIClient, FetchLocalRunReportResponse, FetchLocalRunReportVars, RunStatus, -}; +use crate::api_client::CodSpeedAPIClient; +use crate::cli::run::helpers::benchmark_display::build_benchmark_table; use crate::prelude::*; -use crate::run::helpers::benchmark_display::{ - POLLING_INTERVAL, RUN_PROCESSING_MAX_DURATION, build_benchmark_table, -}; -use crate::run::uploader::UploadResult; +use crate::upload::{UploadResult, poll_run_report}; #[allow(clippy::borrowed_box)] pub async fn poll_results( @@ -16,39 +11,7 @@ pub async fn poll_results( upload_result: &UploadResult, output_json: bool, ) -> Result<()> { - let start = Instant::now(); - let fetch_local_run_report_vars = FetchLocalRunReportVars { - owner: upload_result.owner.clone(), - name: upload_result.repository.clone(), - run_id: upload_result.run_id.clone(), - }; - - let response; - loop { - if start.elapsed() > RUN_PROCESSING_MAX_DURATION { - bail!("Polling results timed out"); - } - - let fetch_result = api_client - .fetch_local_run_report(fetch_local_run_report_vars.clone()) - .await?; - - match fetch_result { - FetchLocalRunReportResponse { run, .. } - if run.status == RunStatus::Pending || run.status == RunStatus::Processing => - { - sleep(POLLING_INTERVAL).await; - } - response_from_api => { - response = response_from_api; - break; - } - } - } - - if response.run.status == RunStatus::Failure { - bail!("Run failed to be processed, try again in a few minutes"); - } + let response = poll_run_report(api_client, upload_result).await?; let report = response .run @@ -100,7 +63,7 @@ pub async fn poll_results( } info!( - "\nFull report: {}", + "\nTo see the full report, visit: {}", style(response.run.url).blue().bold().underlined() ); } diff --git a/src/setup.rs b/src/cli/setup.rs similarity index 93% rename from src/setup.rs rename to src/cli/setup.rs index 3629e392..759a7c51 100644 --- a/src/setup.rs +++ b/src/cli/setup.rs @@ -1,6 +1,6 @@ use crate::executor::get_all_executors; use crate::prelude::*; -use crate::run::check_system::SystemInfo; +use crate::system::SystemInfo; use std::path::Path; pub async fn setup(setup_cache_dir: Option<&Path>) -> Result<()> { diff --git a/src/cli/shared.rs b/src/cli/shared.rs new file mode 100644 index 00000000..0c90d168 --- /dev/null +++ b/src/cli/shared.rs @@ -0,0 +1,144 @@ +use crate::VERSION; +use crate::prelude::*; +use crate::run_environment::interfaces::RepositoryProvider; +use crate::runner_mode::{RunnerMode, load_shell_session_mode}; +use clap::Args; +use clap::ValueEnum; +use std::path::PathBuf; + +pub(crate) fn show_banner() { + let banner = format!( + r#" + ______ __ _____ __ + / ____/____ ____/ // ___/ ____ ___ ___ ____/ / + / / / __ \ / __ / \__ \ / __ \ / _ \ / _ \ / __ / +/ /___ / /_/ // /_/ / ___/ // /_/ // __// __// /_/ / +\____/ \____/ \__,_/ /____// .___/ \___/ \___/ \__,_/ + https://codspeed.io /_/ runner v{VERSION} +"# + ); + println!("{banner}"); + debug!("codspeed v{VERSION}"); +} + +/// Arguments shared between run and exec commands +#[derive(Args, Debug, Clone)] +pub struct ExecAndRunSharedArgs { + /// The upload URL to use for uploading the results, useful for on-premises installations + #[arg(long, env = "CODSPEED_UPLOAD_URL")] + pub upload_url: Option, + + /// The token to use for uploading the results, + /// + /// It can be either a CodSpeed token retrieved from the repository setting + /// or an OIDC token issued by the identity provider. + #[arg(long, env = "CODSPEED_TOKEN")] + pub token: Option, + + /// The repository the benchmark is associated with, under the format `owner/repo`. + #[arg(short, long, env = "CODSPEED_REPOSITORY")] + pub repository: Option, + + /// The repository provider to use in case --repository is used. Defaults to github + #[arg( + long, + env = "CODSPEED_PROVIDER", + requires = "repository", + ignore_case = true + )] + pub provider: Option, + + /// The directory where the command will be executed. + #[arg(long)] + pub working_directory: Option, + + /// The mode to run the benchmarks in. + /// If not provided, the mode will be loaded from the shell session (set via `codspeed use `). + #[arg(short, long, value_enum, env = "CODSPEED_RUNNER_MODE")] + pub mode: Option, + + /// Profile folder to use for the run. + #[arg(long)] + pub profile_folder: Option, + + /// Only for debugging purposes, skips the upload of the results + #[arg( + long, + default_value = "false", + hide = true, + env = "CODSPEED_SKIP_UPLOAD" + )] + pub skip_upload: bool, + + /// Used internally to upload the results after running the benchmarks in a sandbox environment + /// with no internet access + #[arg(long, default_value = "false", hide = true)] + pub skip_run: bool, + + /// Only for debugging purposes, skips the setup of the runner + #[arg(long, default_value = "false", hide = true)] + pub skip_setup: bool, + + /// Allow runs without any benchmarks to succeed instead of failing + #[arg(long, default_value = "false", hide = true)] + pub allow_empty: bool, + + /// The version of the go-runner to use (e.g., 1.2.3, 1.0.0-beta.1) + /// If not specified, the latest version will be installed + #[arg(long, env = "CODSPEED_GO_RUNNER_VERSION", value_parser = parse_version)] + pub go_runner_version: Option, + + #[command(flatten)] + pub perf_run_args: PerfRunArgs, +} + +impl ExecAndRunSharedArgs { + /// Resolves the runner mode from CLI argument, shell session, or returns an error. + /// + /// Priority: + /// 1. CLI argument (--mode or -m) + /// 2. Shell session mode (set via `codspeed use `) + /// 3. Error if neither is available + pub fn resolve_mode(&self) -> Result { + if let Some(mode) = &self.mode { + return Ok(mode.clone()); + } + + if let Some(mode) = load_shell_session_mode()? { + debug!("Loaded mode from shell session: {mode:?}"); + return Ok(mode); + } + + Err(anyhow!( + "No runner mode specified. Use --mode or set the mode for this shell session with `codspeed use `." + )) + } +} + +#[derive(Debug, Copy, Clone, PartialEq, ValueEnum, Default)] +pub enum UnwindingMode { + /// Use the frame pointer for unwinding. Requires the binary to be compiled with frame pointers enabled. + #[clap(name = "fp")] + FramePointer, + + /// Use DWARF unwinding. This does not require any special compilation flags and is enabled by default. + #[default] + Dwarf, +} + +#[derive(Args, Debug, Clone)] +pub struct PerfRunArgs { + /// Enable the linux perf profiler to collect granular performance data. + /// This is only supported on Linux. + #[arg(long, env = "CODSPEED_PERF_ENABLED", default_value_t = true)] + pub enable_perf: bool, + + /// The unwinding mode that should be used with perf to collect the call stack. + #[arg(long, env = "CODSPEED_PERF_UNWINDING_MODE")] + pub perf_unwinding_mode: Option, +} + +/// Parser for go-runner version that validates semver format +fn parse_version(s: &str) -> Result { + semver::Version::parse(s).map_err(|e| format!("Invalid semantic version: {e}")) +} diff --git a/src/cli/use_mode.rs b/src/cli/use_mode.rs new file mode 100644 index 00000000..af0d75fc --- /dev/null +++ b/src/cli/use_mode.rs @@ -0,0 +1,31 @@ +//! Named like this because `use` is a keyword + +use crate::prelude::*; +use crate::runner_mode::RunnerMode; +use clap::Args; + +#[derive(Debug, Args)] +pub struct UseArgs { + /// Set the CodSpeed runner mode for this shell session. If not provided, the current mode will + /// be displayed. + pub mode: Option, +} + +pub fn run(args: UseArgs) -> Result<()> { + if let Some(mode) = &args.mode { + crate::runner_mode::register_shell_session_mode(mode)?; + debug!( + "Registered codspeed use mode '{:?}' for this shell session (parent PID)", + args.mode + ); + } else { + let shell_session_mode = crate::runner_mode::load_shell_session_mode()?; + + if let Some(mode) = shell_session_mode { + info!("{mode:?}"); + } else { + info!("No mode set for this shell session"); + } + } + Ok(()) +} diff --git a/src/executor/config.rs b/src/executor/config.rs index 616cd4ab..90411a86 100644 --- a/src/executor/config.rs +++ b/src/executor/config.rs @@ -1,7 +1,8 @@ -use crate::exec::wrap_with_exec_harness; +use crate::cli::UnwindingMode; +use crate::cli::exec::wrap_with_exec_harness; +use crate::cli::run::RunArgs; use crate::instruments::Instruments; use crate::prelude::*; -use crate::run::{RunArgs, UnwindingMode}; use crate::run_environment::RepositoryProvider; use crate::runner_mode::RunnerMode; use semver::Version; @@ -99,6 +100,7 @@ impl TryFrom for Config { type Error = Error; fn try_from(args: RunArgs) -> Result { let instruments = Instruments::try_from(&args)?; + let mode = args.shared.resolve_mode()?; let raw_upload_url = args .shared .upload_url @@ -115,7 +117,7 @@ impl TryFrom for Config { .map(|repo| RepositoryOverride::from_arg(repo, args.shared.provider)) .transpose()?, working_directory: args.shared.working_directory, - mode: args.shared.mode, + mode, instruments, perf_unwinding_mode: args.shared.perf_run_args.perf_unwinding_mode, enable_perf: args.shared.perf_run_args.enable_perf, @@ -132,7 +134,11 @@ impl TryFrom for Config { impl Config { /// Create a Config from ExecArgs with a custom command (used for targets mode) - pub fn try_from_with_command(args: crate::exec::ExecArgs, command: String) -> Result { + pub fn try_from_with_command( + args: crate::cli::exec::ExecArgs, + command: String, + ) -> Result { + let mode = args.shared.resolve_mode()?; let raw_upload_url = args .shared .upload_url @@ -149,7 +155,7 @@ impl Config { .map(|repo| RepositoryOverride::from_arg(repo, args.shared.provider)) .transpose()?, working_directory: args.shared.working_directory, - mode: args.shared.mode, + mode, instruments: Instruments { mongodb: None }, // exec doesn't support MongoDB perf_unwinding_mode: args.shared.perf_run_args.perf_unwinding_mode, enable_perf: args.shared.perf_run_args.enable_perf, @@ -164,9 +170,9 @@ impl Config { } } -impl TryFrom for Config { +impl TryFrom for Config { type Error = Error; - fn try_from(args: crate::exec::ExecArgs) -> Result { + fn try_from(args: crate::cli::exec::ExecArgs) -> Result { let wrapped_command = wrap_with_exec_harness(&args.walltime_args, &args.command); Self::try_from_with_command(args, wrapped_command) } @@ -174,21 +180,21 @@ impl TryFrom for Config { #[cfg(test)] mod tests { + use crate::cli::PerfRunArgs; use crate::instruments::MongoDBConfig; - use crate::run::PerfRunArgs; use super::*; #[test] fn test_try_from_env_empty() { let config = Config::try_from(RunArgs { - shared: crate::run::ExecAndRunSharedArgs { + shared: crate::cli::ExecAndRunSharedArgs { upload_url: None, token: None, repository: None, provider: None, working_directory: None, - mode: RunnerMode::Simulation, + mode: Some(RunnerMode::Simulation), profile_folder: None, skip_upload: false, skip_run: false, @@ -221,13 +227,13 @@ mod tests { #[test] fn test_try_from_args() { let config = Config::try_from(RunArgs { - shared: crate::run::ExecAndRunSharedArgs { + shared: crate::cli::ExecAndRunSharedArgs { upload_url: Some("https://example.com/upload".into()), token: Some("token".into()), repository: Some("owner/repo".into()), provider: Some(RepositoryProvider::GitLab), working_directory: Some("/tmp".into()), - mode: RunnerMode::Simulation, + mode: Some(RunnerMode::Simulation), profile_folder: Some("./codspeed.out".into()), skip_upload: true, skip_run: true, @@ -241,7 +247,7 @@ mod tests { }, instruments: vec!["mongodb".into()], mongo_uri_env_name: Some("MONGODB_URI".into()), - message_format: Some(crate::run::MessageFormat::Json), + message_format: Some(crate::cli::run::MessageFormat::Json), command: vec!["cargo".into(), "codspeed".into(), "bench".into()], }) .unwrap(); @@ -303,14 +309,14 @@ mod tests { #[test] fn test_try_from_exec_args_default_url() { - let exec_args = crate::exec::ExecArgs { - shared: crate::run::ExecAndRunSharedArgs { + let exec_args = crate::cli::exec::ExecArgs { + shared: crate::cli::ExecAndRunSharedArgs { upload_url: None, token: None, repository: None, provider: None, working_directory: None, - mode: RunnerMode::Simulation, + mode: Some(RunnerMode::Simulation), profile_folder: None, skip_upload: false, skip_run: false, diff --git a/src/executor/execution_context.rs b/src/executor/execution_context.rs index cd46e8fc..acd80585 100644 --- a/src/executor/execution_context.rs +++ b/src/executor/execution_context.rs @@ -1,10 +1,10 @@ use super::Config; +use crate::cli::run::logger::Logger; use crate::config::CodSpeedConfig; use crate::prelude::*; -use crate::run::check_system::{self, SystemInfo}; -use crate::run::logger::Logger; use crate::run_environment::{self, RunEnvironment}; use crate::runner_mode::RunnerMode; +use crate::system::{self, SystemInfo}; use std::path::PathBuf; use super::create_profile_folder; @@ -39,7 +39,7 @@ impl TryFrom<(Config, &CodSpeedConfig)> for ExecutionContext { ) -> Result { let provider = run_environment::get_provider(&config)?; let system_info = SystemInfo::new()?; - check_system::check_system(&system_info)?; + system::check_system(&system_info)?; let logger = Logger::new(provider.as_ref())?; let profile_folder = if let Some(profile_folder) = &config.profile_folder { diff --git a/src/executor/helpers/apt.rs b/src/executor/helpers/apt.rs index 30d6ab98..00fb1cfc 100644 --- a/src/executor/helpers/apt.rs +++ b/src/executor/helpers/apt.rs @@ -1,6 +1,6 @@ use super::run_with_sudo::run_with_sudo; use crate::prelude::*; -use crate::run::check_system::SystemInfo; +use crate::system::SystemInfo; use std::path::Path; use std::process::Command; diff --git a/src/executor/memory/executor.rs b/src/executor/memory/executor.rs index 2d704e02..1a134de6 100644 --- a/src/executor/memory/executor.rs +++ b/src/executor/memory/executor.rs @@ -10,8 +10,8 @@ use crate::executor::shared::fifo::RunnerFifo; use crate::executor::{ExecutionContext, Executor}; use crate::instruments::mongo_tracer::MongoTracer; use crate::prelude::*; -use crate::run::check_system::SystemInfo; use crate::runner_mode::RunnerMode; +use crate::system::SystemInfo; use async_trait::async_trait; use ipc_channel::ipc; use memtrack::MemtrackIpcClient; diff --git a/src/executor/mod.rs b/src/executor/mod.rs index c081ab7d..d2147245 100644 --- a/src/executor/mod.rs +++ b/src/executor/mod.rs @@ -14,9 +14,9 @@ mod wall_time; use crate::api_client::CodSpeedAPIClient; use crate::instruments::mongo_tracer::{MongoTracer, install_mongodb_tracer}; use crate::prelude::*; -use crate::run::check_system::SystemInfo; -use crate::run::uploader::UploadResult; use crate::runner_mode::RunnerMode; +use crate::system::SystemInfo; +use crate::upload::UploadResult; use async_trait::async_trait; pub use config::Config; pub use execution_context::ExecutionContext; @@ -164,7 +164,7 @@ where start_group!("Uploading results"); let upload_result = - crate::run::uploader::upload(execution_context, executor.name(), api_client).await?; + crate::upload::upload(execution_context, executor.name(), api_client).await?; if execution_context.is_local() { poll_results(&upload_result).await?; diff --git a/src/executor/tests.rs b/src/executor/tests.rs index 8cf30fe1..94a848a3 100644 --- a/src/executor/tests.rs +++ b/src/executor/tests.rs @@ -4,8 +4,8 @@ use crate::executor::Executor; use crate::executor::memory::executor::MemoryExecutor; use crate::executor::valgrind::executor::ValgrindExecutor; use crate::executor::wall_time::executor::WallTimeExecutor; -use crate::run::check_system::SystemInfo; use crate::runner_mode::RunnerMode; +use crate::system::SystemInfo; use rstest_reuse::{self, *}; use shell_quote::{Bash, QuoteRefExt}; use tempfile::TempDir; @@ -362,7 +362,7 @@ fi #[rstest::rstest] #[test_log::test(tokio::test)] async fn test_exec_harness(#[case] cmd: &str) { - use crate::exec::wrap_with_exec_harness; + use crate::cli::exec::wrap_with_exec_harness; use exec_harness::walltime::WalltimeExecutionArgs; let (_permit, executor) = get_walltime_executor().await; diff --git a/src/executor/valgrind/executor.rs b/src/executor/valgrind/executor.rs index 53d76034..fc2cb3e6 100644 --- a/src/executor/valgrind/executor.rs +++ b/src/executor/valgrind/executor.rs @@ -5,7 +5,7 @@ use crate::executor::Executor; use crate::executor::{ExecutionContext, ExecutorName}; use crate::instruments::mongo_tracer::MongoTracer; use crate::prelude::*; -use crate::run::check_system::SystemInfo; +use crate::system::SystemInfo; use super::setup::install_valgrind; use super::{helpers::perf_maps::harvest_perf_maps, helpers::venv_compat, measure}; diff --git a/src/executor/valgrind/setup.rs b/src/executor/valgrind/setup.rs index d2d54a96..7eaec2ab 100644 --- a/src/executor/valgrind/setup.rs +++ b/src/executor/valgrind/setup.rs @@ -1,8 +1,9 @@ +use crate::cli::run::helpers::download_file; use crate::executor::helpers::apt; -use crate::{VALGRIND_CODSPEED_DEB_VERSION, run::check_system::SystemInfo}; +use crate::prelude::*; +use crate::system::SystemInfo; use crate::{ - VALGRIND_CODSPEED_VERSION, VALGRIND_CODSPEED_VERSION_STRING, prelude::*, - run::helpers::download_file, + VALGRIND_CODSPEED_DEB_VERSION, VALGRIND_CODSPEED_VERSION, VALGRIND_CODSPEED_VERSION_STRING, }; use semver::Version; use std::{env, path::Path, process::Command}; diff --git a/src/executor/wall_time/executor.rs b/src/executor/wall_time/executor.rs index 6a1814ee..9ddd76f5 100644 --- a/src/executor/wall_time/executor.rs +++ b/src/executor/wall_time/executor.rs @@ -13,8 +13,8 @@ use crate::executor::helpers::run_with_sudo::wrap_with_sudo; use crate::executor::{ExecutionContext, ExecutorName}; use crate::instruments::mongo_tracer::MongoTracer; use crate::prelude::*; -use crate::run::check_system::SystemInfo; use crate::runner_mode::RunnerMode; +use crate::system::SystemInfo; use async_trait::async_trait; use std::fs::canonicalize; use std::io::Write; diff --git a/src/executor/wall_time/perf/mod.rs b/src/executor/wall_time/perf/mod.rs index 14d12e40..0f54cb75 100644 --- a/src/executor/wall_time/perf/mod.rs +++ b/src/executor/wall_time/perf/mod.rs @@ -1,5 +1,6 @@ #![cfg_attr(not(unix), allow(dead_code, unused_mut))] +use crate::cli::UnwindingMode; use crate::executor::Config; use crate::executor::helpers::command::CommandBuilder; use crate::executor::helpers::env::is_codspeed_debug_enabled; @@ -14,7 +15,6 @@ use crate::executor::wall_time::perf::debug_info::ProcessDebugInfo; use crate::executor::wall_time::perf::jit_dump::harvest_perf_jit_for_pids; use crate::executor::wall_time::perf::perf_executable::get_working_perf_executable; use crate::prelude::*; -use crate::run::UnwindingMode; use anyhow::Context; use fifo::PerfFifo; use libc::pid_t; @@ -59,7 +59,7 @@ pub struct PerfRunner { impl PerfRunner { pub async fn setup_environment( - system_info: &crate::run::check_system::SystemInfo, + system_info: &crate::system::SystemInfo, setup_cache_dir: Option<&Path>, ) -> anyhow::Result<()> { setup::install_perf(system_info, setup_cache_dir).await?; diff --git a/src/executor/wall_time/perf/setup.rs b/src/executor/wall_time/perf/setup.rs index 22bb20aa..fa4e8a8b 100644 --- a/src/executor/wall_time/perf/setup.rs +++ b/src/executor/wall_time/perf/setup.rs @@ -1,6 +1,7 @@ use crate::executor::helpers::apt; use crate::executor::wall_time::perf::perf_executable::get_working_perf_executable; -use crate::{prelude::*, run::check_system::SystemInfo}; +use crate::prelude::*; +use crate::system::SystemInfo; use std::{path::Path, process::Command}; diff --git a/src/instruments/mod.rs b/src/instruments/mod.rs index da34e06e..68ef036f 100644 --- a/src/instruments/mod.rs +++ b/src/instruments/mod.rs @@ -3,8 +3,8 @@ use std::collections::HashSet; use log::warn; use serde::{Deserialize, Serialize}; +use crate::cli::run::RunArgs; use crate::prelude::*; -use crate::run::RunArgs; pub mod mongo_tracer; diff --git a/src/instruments/mongo_tracer.rs b/src/instruments/mongo_tracer.rs index 450db62f..c16007a6 100644 --- a/src/instruments/mongo_tracer.rs +++ b/src/instruments/mongo_tracer.rs @@ -11,8 +11,8 @@ use reqwest::Client; use tokio::fs; use url::Url; -use crate::{MONGODB_TRACER_VERSION, run::helpers::get_env_variable}; -use crate::{prelude::*, run::helpers::download_file}; +use crate::{MONGODB_TRACER_VERSION, cli::run::helpers::get_env_variable}; +use crate::{cli::run::helpers::download_file, prelude::*}; use super::MongoDBConfig; diff --git a/src/lib.rs b/src/lib.rs index af122e4f..523ffe0a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,11 +1,9 @@ //! CodSpeed Runner library mod api_client; -pub mod app; -mod auth; mod binary_installer; +pub mod cli; mod config; -mod exec; mod executor; mod instruments; mod local_logger; @@ -13,10 +11,10 @@ pub mod logger; mod prelude; mod project_config; mod request_client; -mod run; mod run_environment; mod runner_mode; -mod setup; +mod system; +mod upload; pub use local_logger::clean_logger; pub use project_config::{ProjectConfig, ProjectOptions, Target, TargetOptions, WalltimeOptions}; diff --git a/src/main.rs b/src/main.rs index ace11d2e..c44740ca 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,10 @@ -use codspeed_runner::{app, clean_logger}; +use codspeed_runner::{clean_logger, cli}; use console::style; use log::log_enabled; #[tokio::main(flavor = "current_thread")] async fn main() { - let res = app::run().await; + let res = cli::run().await; if let Err(err) = res { for cause in err.chain() { if log_enabled!(log::Level::Error) { diff --git a/src/project_config/merger.rs b/src/project_config/merger.rs index 3f19bec2..6d1846a7 100644 --- a/src/project_config/merger.rs +++ b/src/project_config/merger.rs @@ -1,4 +1,4 @@ -use crate::run::ExecAndRunSharedArgs; +use crate::cli::ExecAndRunSharedArgs; use exec_harness::walltime::WalltimeExecutionArgs; use super::{ProjectOptions, WalltimeOptions}; @@ -52,11 +52,6 @@ impl ConfigMerger { } } - // Note: mode field has a required default value from clap, so we can't - // distinguish between "user set it" vs "default value". For now, we - // always use the CLI value. This will be addressed in a future PR - // when we make mode optional in CLI args. - merged } @@ -69,7 +64,7 @@ impl ConfigMerger { #[cfg(test)] mod tests { use super::*; - use crate::run::PerfRunArgs; + use crate::cli::PerfRunArgs; use crate::runner_mode::RunnerMode; #[test] @@ -157,7 +152,7 @@ mod tests { repository: None, provider: None, working_directory: Some("./cli-dir".to_string()), - mode: RunnerMode::Walltime, + mode: Some(RunnerMode::Walltime), profile_folder: None, skip_upload: false, skip_run: false, @@ -189,7 +184,7 @@ mod tests { repository: None, provider: None, working_directory: None, - mode: RunnerMode::Walltime, + mode: Some(RunnerMode::Walltime), profile_folder: None, skip_upload: false, skip_run: false, @@ -211,8 +206,8 @@ mod tests { // Config working_directory should be used assert_eq!(merged.working_directory, Some("./config-dir".to_string())); - // Mode stays as CLI default (can't override due to clap default) - assert_eq!(merged.mode, RunnerMode::Walltime); + // Mode stays as CLI value + assert_eq!(merged.mode, Some(RunnerMode::Walltime)); } #[test] @@ -223,7 +218,7 @@ mod tests { repository: None, provider: None, working_directory: Some("./dir".to_string()), - mode: RunnerMode::Simulation, + mode: Some(RunnerMode::Simulation), profile_folder: None, skip_upload: false, skip_run: false, @@ -240,7 +235,7 @@ mod tests { // Should be identical to CLI assert_eq!(merged.working_directory, Some("./dir".to_string())); - assert_eq!(merged.mode, RunnerMode::Simulation); + assert_eq!(merged.mode, Some(RunnerMode::Simulation)); } #[test] diff --git a/src/project_config/mod.rs b/src/project_config/mod.rs index 0edd6542..06a191ed 100644 --- a/src/project_config/mod.rs +++ b/src/project_config/mod.rs @@ -80,7 +80,7 @@ impl ProjectConfig { let mut dirs = vec![current_dir.to_path_buf()]; // Try to find git repository root - if let Some(git_root) = crate::run::helpers::find_repository_root(current_dir) { + if let Some(git_root) = crate::cli::run::helpers::find_repository_root(current_dir) { // Add parent directories up to git root let mut dir = current_dir.to_path_buf(); while let Some(parent) = dir.parent() { diff --git a/src/run/helpers/snapshots/codspeed_runner__run__helpers__benchmark_display__tests__benchmark_table_formatting.snap b/src/run/helpers/snapshots/codspeed_runner__run__helpers__benchmark_display__tests__benchmark_table_formatting.snap deleted file mode 100644 index 18258cbe..00000000 --- a/src/run/helpers/snapshots/codspeed_runner__run__helpers__benchmark_display__tests__benchmark_table_formatting.snap +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8de3d5add12bcd526d2a148ea56102cb15b05cc65a3749aaa7dcbfd827ef98f3 -size 3497 diff --git a/src/run/uploader/mod.rs b/src/run/uploader/mod.rs deleted file mode 100644 index a99c2629..00000000 --- a/src/run/uploader/mod.rs +++ /dev/null @@ -1,8 +0,0 @@ -mod interfaces; -mod profile_archive; -mod upload; -mod upload_metadata; - -pub use interfaces::*; -pub use profile_archive::ProfileArchive; -pub use upload::{UploadResult, upload}; diff --git a/src/run/uploader/snapshots/codspeed_runner__run__uploader__upload_metadata__tests__get_metadata_hash-2.snap b/src/run/uploader/snapshots/codspeed_runner__run__uploader__upload_metadata__tests__get_metadata_hash-2.snap deleted file mode 100644 index 124ed192..00000000 --- a/src/run/uploader/snapshots/codspeed_runner__run__uploader__upload_metadata__tests__get_metadata_hash-2.snap +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:45dd370f89126d39831fa65a8a507c045e0e8eaa08a986c4493807b85c13fe91 -size 1372 diff --git a/src/run_environment/buildkite/provider.rs b/src/run_environment/buildkite/provider.rs index d2b8e3d9..bb4ad17c 100644 --- a/src/run_environment/buildkite/provider.rs +++ b/src/run_environment/buildkite/provider.rs @@ -3,9 +3,11 @@ use std::env; use async_trait::async_trait; use simplelog::SharedLogger; +use crate::cli::run::helpers::{ + GitRemote, find_repository_root, get_env_variable, parse_git_remote, +}; use crate::executor::Config; use crate::prelude::*; -use crate::run::helpers::{GitRemote, find_repository_root, get_env_variable, parse_git_remote}; use crate::run_environment::interfaces::{RepositoryProvider, RunEnvironmentMetadata, RunEvent}; use crate::run_environment::provider::{RunEnvironmentDetector, RunEnvironmentProvider}; use crate::run_environment::{RunEnvironment, RunPart}; diff --git a/src/run_environment/github_actions/provider.rs b/src/run_environment/github_actions/provider.rs index fba87abc..d53a6493 100644 --- a/src/run_environment/github_actions/provider.rs +++ b/src/run_environment/github_actions/provider.rs @@ -8,10 +8,10 @@ use simplelog::SharedLogger; use std::collections::BTreeMap; use std::{env, fs}; +use crate::cli::run::helpers::{find_repository_root, get_env_variable}; use crate::executor::Config; use crate::prelude::*; use crate::request_client::OIDC_CLIENT; -use crate::run::helpers::{find_repository_root, get_env_variable}; use crate::run_environment::interfaces::{ GhData, RepositoryProvider, RunEnvironmentMetadata, RunEvent, Sender, }; diff --git a/src/run_environment/gitlab_ci/provider.rs b/src/run_environment/gitlab_ci/provider.rs index e3564113..ba741a3c 100644 --- a/src/run_environment/gitlab_ci/provider.rs +++ b/src/run_environment/gitlab_ci/provider.rs @@ -3,9 +3,9 @@ use simplelog::SharedLogger; use std::collections::BTreeMap; use std::env; +use crate::cli::run::helpers::get_env_variable; use crate::executor::Config; use crate::prelude::*; -use crate::run::helpers::get_env_variable; use crate::run_environment::interfaces::{ GlData, RepositoryProvider, RunEnvironment, RunEnvironmentMetadata, RunEvent, Sender, }; diff --git a/src/run_environment/local/provider.rs b/src/run_environment/local/provider.rs index 980506d9..35dba2d9 100644 --- a/src/run_environment/local/provider.rs +++ b/src/run_environment/local/provider.rs @@ -3,18 +3,16 @@ use git2::Repository; use simplelog::SharedLogger; use crate::api_client::CodSpeedAPIClient; +use crate::cli::run::helpers::{GitRemote, find_repository_root, parse_git_remote}; use crate::executor::config::RepositoryOverride; use crate::executor::{Config, ExecutorName}; use crate::local_logger::get_local_logger; use crate::prelude::*; -use crate::run::check_system::SystemInfo; -use crate::run::helpers::{GitRemote, find_repository_root, parse_git_remote}; -use crate::run::uploader::{ - LATEST_UPLOAD_METADATA_VERSION, ProfileArchive, Runner, UploadMetadata, -}; use crate::run_environment::interfaces::{RepositoryProvider, RunEnvironmentMetadata, RunEvent}; use crate::run_environment::provider::{RunEnvironmentDetector, RunEnvironmentProvider}; use crate::run_environment::{RunEnvironment, RunPart}; +use crate::system::SystemInfo; +use crate::upload::{LATEST_UPLOAD_METADATA_VERSION, ProfileArchive, Runner, UploadMetadata}; static FAKE_COMMIT_REF: &str = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; @@ -69,7 +67,7 @@ impl TryFrom<&Config> for LocalProvider { // No git repo and no override - we'll fetch from API using default project name return Ok(Self { source: RepositorySource::ApiProject { - project_name: crate::exec::DEFAULT_REPOSITORY_NAME.to_string(), + project_name: crate::cli::exec::DEFAULT_REPOSITORY_NAME.to_string(), }, repository_root_path: current_dir.to_string_lossy().to_string(), event: RunEvent::Local, diff --git a/src/run_environment/provider.rs b/src/run_environment/provider.rs index 46e84d0c..9c9df406 100644 --- a/src/run_environment/provider.rs +++ b/src/run_environment/provider.rs @@ -5,10 +5,9 @@ use simplelog::SharedLogger; use crate::api_client::CodSpeedAPIClient; use crate::executor::{Config, ExecutorName}; use crate::prelude::*; -use crate::run::check_system::SystemInfo; -use crate::run::run_index_state::RunIndexState; -use crate::run::uploader::{ - LATEST_UPLOAD_METADATA_VERSION, ProfileArchive, Runner, UploadMetadata, +use crate::system::SystemInfo; +use crate::upload::{ + LATEST_UPLOAD_METADATA_VERSION, ProfileArchive, RunIndexState, Runner, UploadMetadata, }; use super::interfaces::{RepositoryProvider, RunEnvironment, RunEnvironmentMetadata, RunPart}; diff --git a/src/runner_mode.rs b/src/runner_mode/mod.rs similarity index 60% rename from src/runner_mode.rs rename to src/runner_mode/mod.rs index 79fe31ee..4d84660c 100644 --- a/src/runner_mode.rs +++ b/src/runner_mode/mod.rs @@ -1,5 +1,11 @@ use clap::ValueEnum; -use serde::{Deserialize, Serialize}; +use serde::Deserialize; +use serde::Serialize; + +mod shell_session; + +pub(crate) use shell_session::load_shell_session_mode; +pub(crate) use shell_session::register_shell_session_mode; #[derive(ValueEnum, Clone, Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "lowercase")] diff --git a/src/runner_mode/shell_session.rs b/src/runner_mode/shell_session.rs new file mode 100644 index 00000000..de6822d3 --- /dev/null +++ b/src/runner_mode/shell_session.rs @@ -0,0 +1,77 @@ +use super::RunnerMode; +use crate::prelude::*; +use libc::pid_t; +use std::path::Path; +use std::path::PathBuf; +use std::sync::OnceLock; +use sysinfo::Pid; +use sysinfo::ProcessRefreshKind; +use sysinfo::RefreshKind; +use sysinfo::System; + +static SYSTEM: OnceLock = OnceLock::new(); + +/// Get the root directory where the use mode is stored +/// If available, uses `$XDG_RUNTIME_DIR/codspeed_use_mode` +/// Otherwise, falls back to `std::env::temp_dir()/codspeed_use_mode` +fn get_use_mode_root_dir() -> PathBuf { + let base_dir = if let Some(xdg_runtime_dir) = std::env::var_os("XDG_RUNTIME_DIR") { + PathBuf::from(xdg_runtime_dir) + } else { + std::env::temp_dir() + }; + + base_dir.join("codspeed_use_mode") +} + +fn get_parent_pid(pid: pid_t) -> Option { + let s = SYSTEM.get_or_init(|| { + System::new_with_specifics( + RefreshKind::nothing().with_processes(ProcessRefreshKind::nothing()), + ) + }); + + let current_pid = Pid::from_u32(pid as u32); + + s.process(current_pid) + .and_then(|p| p.parent()) + .map(|pid| pid.as_u32() as pid_t) +} + +fn get_mode_file_path(base_dir: &Path, pid: pid_t) -> PathBuf { + base_dir.join(pid.to_string()) +} + +pub(crate) fn register_shell_session_mode(mode: &RunnerMode) -> Result<()> { + let use_mode_dir = get_use_mode_root_dir(); + std::fs::create_dir_all(&use_mode_dir)?; + + let Some(parent_pid) = get_parent_pid(std::process::id() as pid_t) else { + return Err(anyhow!("Could not determine parent PID")); + }; + + let mode_file_path = get_mode_file_path(&use_mode_dir, parent_pid); + + std::fs::write(mode_file_path, serde_json::to_string(mode)?)?; + Ok(()) +} + +pub(crate) fn load_shell_session_mode() -> Result> { + // Go up the process tree until we find a registered mode + let mut current_pid = std::process::id() as pid_t; + + while let Some(parent_pid) = get_parent_pid(current_pid) { + let use_mode_dir = get_use_mode_root_dir(); + let mode_file_path = get_mode_file_path(&use_mode_dir, parent_pid); + + if mode_file_path.exists() { + let mode_str = std::fs::read_to_string(mode_file_path)?; + let mode: RunnerMode = serde_json::from_str(&mode_str)?; + return Ok(Some(mode)); + } + + current_pid = parent_pid; + } + + Ok(None) +} diff --git a/src/system/check.rs b/src/system/check.rs new file mode 100644 index 00000000..26070a64 --- /dev/null +++ b/src/system/check.rs @@ -0,0 +1,57 @@ +use lazy_static::lazy_static; +use std::collections::HashSet; + +use crate::prelude::*; + +use super::SystemInfo; + +lazy_static! { + static ref SUPPORTED_SYSTEMS: HashSet<(&'static str, &'static str, &'static str)> = { + HashSet::from([ + ("ubuntu", "22.04", "x86_64"), + ("ubuntu", "24.04", "x86_64"), + ("ubuntu", "22.04", "aarch64"), + ("ubuntu", "24.04", "aarch64"), + ("debian", "12", "x86_64"), + ("debian", "12", "aarch64"), + ]) + }; +} + +/// Checks if the provided system info is supported +/// +/// Supported systems: +/// - Ubuntu 20.04 x86_64 +/// - Ubuntu 22.04 x86_64 and aarch64 +/// - Debian 11 x86_64 +/// - Debian 12 x86_64 +pub fn check_system(system_info: &SystemInfo) -> Result<()> { + debug!("System info: {system_info:#?}"); + + let system_tuple = ( + system_info.os.as_str(), + system_info.os_version.as_str(), + system_info.arch.as_str(), + ); + + if SUPPORTED_SYSTEMS.contains(&system_tuple) { + return Ok(()); + } + + match system_info.arch.as_str() { + "x86_64" | "aarch64" => { + warn!( + "Unofficially supported system: {} {}. Continuing with best effort support.", + system_info.os, system_info.os_version + ); + return Ok(()); + } + _ => {} + } + + bail!( + "Unsupported system: {} {}", + system_info.os, + system_info.os_version + ); +} diff --git a/src/run/check_system.rs b/src/system/info.rs similarity index 68% rename from src/run/check_system.rs rename to src/system/info.rs index efe7a29e..af27eb96 100644 --- a/src/run/check_system.rs +++ b/src/system/info.rs @@ -1,8 +1,6 @@ use std::process::Command; -use lazy_static::lazy_static; use serde::{Deserialize, Serialize}; -use std::collections::HashSet; use sysinfo::{CpuRefreshKind, MemoryRefreshKind, RefreshKind, System}; use crate::prelude::*; @@ -101,54 +99,3 @@ impl SystemInfo { }) } } - -lazy_static! { - static ref SUPPORTED_SYSTEMS: HashSet<(&'static str, &'static str, &'static str)> = { - HashSet::from([ - ("ubuntu", "22.04", "x86_64"), - ("ubuntu", "24.04", "x86_64"), - ("ubuntu", "22.04", "aarch64"), - ("ubuntu", "24.04", "aarch64"), - ("debian", "12", "x86_64"), - ("debian", "12", "aarch64"), - ]) - }; -} - -/// Checks if the provided system info is supported -/// -/// Supported systems: -/// - Ubuntu 20.04 x86_64 -/// - Ubuntu 22.04 x86_64 and aarch64 -/// - Debian 11 x86_64 -/// - Debian 12 x86_64 -pub fn check_system(system_info: &SystemInfo) -> Result<()> { - debug!("System info: {system_info:#?}"); - - let system_tuple = ( - system_info.os.as_str(), - system_info.os_version.as_str(), - system_info.arch.as_str(), - ); - - if SUPPORTED_SYSTEMS.contains(&system_tuple) { - return Ok(()); - } - - match system_info.arch.as_str() { - "x86_64" | "aarch64" => { - warn!( - "Unofficially supported system: {} {}. Continuing with best effort support.", - system_info.os, system_info.os_version - ); - return Ok(()); - } - _ => {} - } - - bail!( - "Unsupported system: {} {}", - system_info.os, - system_info.os_version - ); -} diff --git a/src/system/mod.rs b/src/system/mod.rs new file mode 100644 index 00000000..df68d6a9 --- /dev/null +++ b/src/system/mod.rs @@ -0,0 +1,5 @@ +mod check; +mod info; + +pub use check::check_system; +pub use info::SystemInfo; diff --git a/src/run/uploader/interfaces.rs b/src/upload/interfaces.rs similarity index 96% rename from src/run/uploader/interfaces.rs rename to src/upload/interfaces.rs index 026af2f5..10cc4ab5 100644 --- a/src/run/uploader/interfaces.rs +++ b/src/upload/interfaces.rs @@ -2,8 +2,8 @@ use serde::{Deserialize, Serialize}; use crate::executor::ExecutorName; use crate::instruments::InstrumentName; -use crate::run::check_system::SystemInfo; use crate::run_environment::{RepositoryProvider, RunEnvironment, RunEnvironmentMetadata, RunPart}; +use crate::system::SystemInfo; pub const LATEST_UPLOAD_METADATA_VERSION: u32 = 8; diff --git a/src/upload/mod.rs b/src/upload/mod.rs new file mode 100644 index 00000000..9208ba35 --- /dev/null +++ b/src/upload/mod.rs @@ -0,0 +1,12 @@ +mod interfaces; +mod polling; +mod profile_archive; +mod run_index_state; +mod upload_metadata; +mod uploader; + +pub use interfaces::*; +pub use polling::poll_run_report; +pub use profile_archive::ProfileArchive; +pub use run_index_state::RunIndexState; +pub use uploader::{UploadResult, upload}; diff --git a/src/exec/poll_results.rs b/src/upload/polling.rs similarity index 61% rename from src/exec/poll_results.rs rename to src/upload/polling.rs index 967c27d8..c6a64ca5 100644 --- a/src/exec/poll_results.rs +++ b/src/upload/polling.rs @@ -1,20 +1,23 @@ -use console::style; +use std::time::Duration; use tokio::time::{Instant, sleep}; use crate::api_client::{ CodSpeedAPIClient, FetchLocalRunReportResponse, FetchLocalRunReportVars, RunStatus, }; use crate::prelude::*; -use crate::run::helpers::benchmark_display::{ - POLLING_INTERVAL, RUN_PROCESSING_MAX_DURATION, build_benchmark_table, build_detailed_summary, -}; -use crate::run::uploader::UploadResult; -#[allow(clippy::borrowed_box)] -pub async fn poll_results( +use super::UploadResult; + +pub const RUN_PROCESSING_MAX_DURATION: Duration = Duration::from_secs(60 * 5); // 5 minutes +pub const POLLING_INTERVAL: Duration = Duration::from_secs(1); + +/// Poll the API until the run is processed and return the response. +/// +/// Returns an error if polling times out or the run fails processing. +pub async fn poll_run_report( api_client: &CodSpeedAPIClient, upload_result: &UploadResult, -) -> Result<()> { +) -> Result { let start = Instant::now(); let fetch_local_run_report_vars = FetchLocalRunReportVars { owner: upload_result.owner.clone(), @@ -49,23 +52,5 @@ pub async fn poll_results( bail!("Run failed to be processed, try again in a few minutes"); } - if !response.run.results.is_empty() { - end_group!(); - start_group!("Benchmark results"); - - if response.run.results.len() == 1 { - let summary = build_detailed_summary(&response.run.results[0]); - info!("\n{summary}"); - } else { - let table = build_benchmark_table(&response.run.results); - info!("\n{table}"); - } - - info!( - "\nFull report: {}", - style(response.run.url).blue().bold().underlined() - ); - } - - Ok(()) + Ok(response) } diff --git a/src/run/uploader/profile_archive.rs b/src/upload/profile_archive.rs similarity index 100% rename from src/run/uploader/profile_archive.rs rename to src/upload/profile_archive.rs diff --git a/src/run/run_index_state.rs b/src/upload/run_index_state.rs similarity index 100% rename from src/run/run_index_state.rs rename to src/upload/run_index_state.rs diff --git a/src/upload/snapshots/codspeed_runner__upload__upload_metadata__tests__get_metadata_hash-2.snap b/src/upload/snapshots/codspeed_runner__upload__upload_metadata__tests__get_metadata_hash-2.snap new file mode 100644 index 00000000..641e6115 --- /dev/null +++ b/src/upload/snapshots/codspeed_runner__upload__upload_metadata__tests__get_metadata_hash-2.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5fd142a000c48692a6f77bcce115af69127cc925d67a59273f2ed646ff79b68b +size 1366 diff --git a/src/run/uploader/upload_metadata.rs b/src/upload/upload_metadata.rs similarity index 95% rename from src/run/uploader/upload_metadata.rs rename to src/upload/upload_metadata.rs index 3db452e9..b9561247 100644 --- a/src/run/uploader/upload_metadata.rs +++ b/src/upload/upload_metadata.rs @@ -17,14 +17,12 @@ mod tests { use crate::executor::ExecutorName; use crate::instruments::InstrumentName; - use crate::run::{ - check_system::SystemInfo, - uploader::{LATEST_UPLOAD_METADATA_VERSION, Runner, UploadMetadata}, - }; use crate::run_environment::{ GhData, RepositoryProvider, RunEnvironment, RunEnvironmentMetadata, RunEvent, RunPart, Sender, }; + use crate::system::SystemInfo; + use crate::upload::{LATEST_UPLOAD_METADATA_VERSION, Runner, UploadMetadata}; #[test] fn test_get_metadata_hash() { diff --git a/src/run/uploader/upload.rs b/src/upload/uploader.rs similarity index 98% rename from src/run/uploader/upload.rs rename to src/upload/uploader.rs index 7bcf4317..a8a2fcb9 100644 --- a/src/run/uploader/upload.rs +++ b/src/upload/uploader.rs @@ -1,10 +1,9 @@ use crate::api_client::CodSpeedAPIClient; use crate::executor::Config; -use crate::run::{ - executor::{ExecutionContext, ExecutorName}, - uploader::{UploadError, profile_archive::ProfileArchiveContent}, -}; +use crate::executor::ExecutionContext; +use crate::executor::ExecutorName; use crate::run_environment::RunEnvironment; +use crate::upload::{UploadError, profile_archive::ProfileArchiveContent}; use crate::{ prelude::*, request_client::{REQUEST_CLIENT, STREAMING_CLIENT},