Skip to content

Commit 6bb4975

Browse files
feat(system): represent the host OS as a typed Os enum with per-executor support gates
Replaces the loose `os` + `os_version` strings on `SystemInfo` with an `Os` enum. Adds `Executor::support_level(&Os)` so each executor declares its own OS matrix: `run_executor` bails on `Unsupported`, skips `setup()` on `RequiresManualSetup`, and runs it on `FullySupported`.
1 parent 14a89b2 commit 6bb4975

13 files changed

Lines changed: 336 additions & 128 deletions

File tree

src/cli/setup.rs

Lines changed: 66 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::executor::{ToolInstallStatus, get_all_executors};
1+
use crate::executor::{ExecutorSupport, ToolInstallStatus, get_all_executors};
22
use crate::prelude::*;
33
use crate::system::SystemInfo;
44
use clap::{Args, Subcommand};
@@ -22,10 +22,7 @@ enum SetupCommands {
2222
pub async fn run(args: SetupArgs, setup_cache_dir: Option<&Path>) -> Result<()> {
2323
match args.command {
2424
None => setup(setup_cache_dir).await,
25-
Some(SetupCommands::Status) => {
26-
status();
27-
Ok(())
28-
}
25+
Some(SetupCommands::Status) => status(),
2926
}
3027
}
3128

@@ -34,41 +31,81 @@ async fn setup(setup_cache_dir: Option<&Path>) -> Result<()> {
3431
let executors = get_all_executors();
3532
start_group!("Setting up the environment for all executors");
3633
for executor in executors {
37-
info!(
38-
"Setting up the environment for the executor: {}",
39-
executor.name()
40-
);
41-
executor.setup(&system_info, setup_cache_dir).await?;
34+
match executor.support_level(&system_info.os) {
35+
ExecutorSupport::Unsupported => {
36+
info!(
37+
"Skipping setup for the {} executor: not supported on {}",
38+
executor.name(),
39+
system_info.os
40+
);
41+
}
42+
ExecutorSupport::RequiresManualInstallation => {
43+
info!(
44+
"Skipping automatic setup for the {} executor on {}; install required tooling manually.",
45+
executor.name(),
46+
system_info.os
47+
);
48+
}
49+
ExecutorSupport::FullySupported => {
50+
info!(
51+
"Setting up the environment for the executor: {}",
52+
executor.name()
53+
);
54+
executor.setup(&system_info, setup_cache_dir).await?;
55+
}
56+
}
4257
}
4358
info!("Environment setup completed");
4459
end_group!();
4560
Ok(())
4661
}
4762

48-
pub fn status() {
63+
pub fn status() -> Result<()> {
64+
let system_info = SystemInfo::new()?;
4965
info!("{}", style("Tools").bold());
5066
for executor in get_all_executors() {
51-
let tool_status = executor.tool_status();
52-
match &tool_status.status {
53-
ToolInstallStatus::Installed { version } => {
54-
info!(" {} {} ({})", check_mark(), tool_status.tool_name, version);
55-
}
56-
ToolInstallStatus::IncorrectVersion { version, message } => {
57-
info!(
58-
" {} {} ({}, {})",
59-
warn_mark(),
60-
tool_status.tool_name,
61-
version,
62-
message
63-
);
64-
}
65-
ToolInstallStatus::NotInstalled => {
67+
// Don't probe for tooling that can't be used on this OS anyway.
68+
if executor.support_level(&system_info.os) == ExecutorSupport::Unsupported {
69+
continue;
70+
}
71+
match executor.tool_status() {
72+
Some(tool_status) => match &tool_status.status {
73+
ToolInstallStatus::Installed { version } => {
74+
info!(
75+
" {} {} executor: {} ({})",
76+
check_mark(),
77+
executor.name(),
78+
tool_status.tool_name,
79+
version
80+
);
81+
}
82+
ToolInstallStatus::IncorrectVersion { version, message } => {
83+
info!(
84+
" {} {} executor: {} ({}, {})",
85+
warn_mark(),
86+
executor.name(),
87+
tool_status.tool_name,
88+
version,
89+
message
90+
);
91+
}
92+
ToolInstallStatus::NotInstalled => {
93+
info!(
94+
" {} {} executor: {} (not installed)",
95+
cross_mark(),
96+
executor.name(),
97+
tool_status.tool_name
98+
);
99+
}
100+
},
101+
None => {
66102
info!(
67-
" {} {} (not installed)",
68-
cross_mark(),
69-
tool_status.tool_name
103+
" {} {} executor: No tool to install",
104+
check_mark(),
105+
executor.name()
70106
);
71107
}
72108
}
73109
}
110+
Ok(())
74111
}

src/cli/status.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,14 @@ pub async fn run(api_client: &CodSpeedAPIClient) -> Result<()> {
2222
info!("");
2323

2424
// Setup/tools status
25-
super::setup::status();
25+
super::setup::status()?;
2626
info!("");
2727

2828
// System info
2929
info!("{}", style("System").bold());
3030
info!(" codspeed {VERSION}");
3131
let system_info = SystemInfo::new()?;
32-
info!(
33-
" {} {} ({})",
34-
system_info.os, system_info.os_version, system_info.arch
35-
);
32+
info!(" {} ({})", system_info.os, system_info.arch);
3633
info!(
3734
" {} ({}C / {}GB)",
3835
system_info.cpu_brand, system_info.cpu_cores, system_info.total_memory_gb

src/executor/helpers/apt.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
use super::run_with_sudo::run_with_sudo;
22
use crate::prelude::*;
3-
use crate::system::SystemInfo;
3+
use crate::system::{Os, SystemInfo};
44
use std::path::Path;
55
use std::process::Command;
66

77
const METADATA_FILENAME: &str = "./tmp/codspeed-cache-metadata.txt";
88

99
fn is_system_compatible(system_info: &SystemInfo) -> bool {
10-
system_info.os == "ubuntu" || system_info.os == "debian"
10+
matches!(system_info.os, Os::SupportedLinux { .. })
1111
}
1212

1313
/// Installs packages with caching support.

src/executor/memory/executor.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::executor::ExecutorName;
2+
use crate::executor::ExecutorSupport;
23
use crate::executor::ToolStatus;
34
use crate::executor::helpers::command::CommandBuilder;
45
use crate::executor::helpers::env::get_base_injected_env;
@@ -11,7 +12,7 @@ use crate::executor::{ExecutionContext, Executor};
1112
use crate::instruments::mongo_tracer::MongoTracer;
1213
use crate::prelude::*;
1314
use crate::runner_mode::RunnerMode;
14-
use crate::system::SystemInfo;
15+
use crate::system::{Os, SystemInfo};
1516
use async_trait::async_trait;
1617
use ipc_channel::ipc;
1718
use memtrack::MemtrackIpcClient;
@@ -72,8 +73,15 @@ impl Executor for MemoryExecutor {
7273
ExecutorName::Memory
7374
}
7475

75-
fn tool_status(&self) -> ToolStatus {
76-
get_memtrack_status()
76+
fn tool_status(&self) -> Option<ToolStatus> {
77+
Some(get_memtrack_status())
78+
}
79+
80+
fn support_level(&self, os: &Os) -> ExecutorSupport {
81+
match os {
82+
Os::SupportedLinux { .. } | Os::Other { .. } => ExecutorSupport::FullySupported,
83+
Os::MacOs { .. } => ExecutorSupport::Unsupported,
84+
}
7785
}
7886

7987
async fn setup(

src/executor/mod.rs

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ mod wall_time;
1515
use crate::instruments::mongo_tracer::{MongoTracer, install_mongodb_tracer};
1616
use crate::prelude::*;
1717
use crate::runner_mode::RunnerMode;
18-
use crate::system::SystemInfo;
18+
use crate::system::{Os, SystemInfo};
1919
use async_trait::async_trait;
2020
pub use config::{BenchmarkTarget, ExecutorConfig, OrchestratorConfig};
2121
pub use execution_context::ExecutionContext;
@@ -73,12 +73,28 @@ pub enum ToolInstallStatus {
7373
NotInstalled,
7474
}
7575

76+
/// How well a given executor runs on a given [`Os`].
77+
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
78+
pub enum ExecutorSupport {
79+
/// The executor cannot run on this OS at all — `run_executor` hard-bails.
80+
Unsupported,
81+
/// The executor runs on this OS, but the user is responsible for installing the required tooling themselves.
82+
RequiresManualInstallation,
83+
/// The executor runs on this OS and `setup()` knows how to auto-install tooling.
84+
FullySupported,
85+
}
86+
7687
#[async_trait(?Send)]
7788
pub trait Executor {
7889
fn name(&self) -> ExecutorName;
7990

8091
/// Report the installation status of the tool(s) this executor depends on.
81-
fn tool_status(&self) -> ToolStatus;
92+
fn tool_status(&self) -> Option<ToolStatus>;
93+
94+
/// Declare how well this executor runs on the host OS. Drives whether `setup()` is invoked
95+
/// (only when [`ExecutorSupport::FullySupported`]) and whether we bail out of running the
96+
/// executor at all (on [`ExecutorSupport::Unsupported`]).
97+
fn support_level(&self, os: &Os) -> ExecutorSupport;
8298

8399
async fn setup(
84100
&self,
@@ -107,11 +123,24 @@ pub async fn run_executor(
107123
execution_context: &ExecutionContext,
108124
setup_cache_dir: Option<&Path>,
109125
) -> Result<()> {
110-
if !execution_context.config.skip_setup {
111-
executor
112-
.setup(&orchestrator.system_info, setup_cache_dir)
113-
.await?;
126+
match executor.support_level(&orchestrator.system_info.os) {
127+
ExecutorSupport::Unsupported => {
128+
bail!(
129+
"The {} executor is not supported on {}",
130+
executor.name(),
131+
orchestrator.system_info.os
132+
);
133+
}
134+
ExecutorSupport::RequiresManualInstallation | ExecutorSupport::FullySupported => {
135+
if !execution_context.config.skip_setup {
136+
executor
137+
.setup(&orchestrator.system_info, setup_cache_dir)
138+
.await?;
139+
}
140+
}
141+
}
114142

143+
if !execution_context.config.skip_setup {
115144
// TODO: refactor and move directly in the Instruments struct as a `setup` method
116145
if execution_context.config.instruments.is_mongodb_enabled() {
117146
install_mongodb_tracer().await?;

src/executor/valgrind/executor.rs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ use std::path::Path;
33

44
use crate::executor::Executor;
55
use crate::executor::ToolStatus;
6-
use crate::executor::{ExecutionContext, ExecutorName};
6+
use crate::executor::{ExecutionContext, ExecutorName, ExecutorSupport};
77
use crate::instruments::mongo_tracer::MongoTracer;
88
use crate::prelude::*;
9-
use crate::system::SystemInfo;
9+
use crate::system::{Os, SystemInfo};
1010

1111
use super::setup::{get_valgrind_status, install_valgrind};
1212
use super::{helpers::perf_maps::harvest_perf_maps, helpers::venv_compat, measure};
@@ -19,8 +19,16 @@ impl Executor for ValgrindExecutor {
1919
ExecutorName::Valgrind
2020
}
2121

22-
fn tool_status(&self) -> ToolStatus {
23-
get_valgrind_status()
22+
fn tool_status(&self) -> Option<ToolStatus> {
23+
Some(get_valgrind_status())
24+
}
25+
26+
fn support_level(&self, os: &Os) -> ExecutorSupport {
27+
match os {
28+
Os::SupportedLinux { .. } => ExecutorSupport::FullySupported,
29+
Os::Other { .. } => ExecutorSupport::RequiresManualInstallation,
30+
Os::MacOs { .. } => ExecutorSupport::Unsupported,
31+
}
2432
}
2533

2634
async fn setup(&self, system_info: &SystemInfo, setup_cache_dir: Option<&Path>) -> Result<()> {

src/executor/valgrind/setup.rs

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::cli::run::helpers::download_file;
22
use crate::executor::helpers::apt;
33
use crate::executor::{ToolInstallStatus, ToolStatus};
44
use crate::prelude::*;
5-
use crate::system::SystemInfo;
5+
use crate::system::{Os, SupportedLinuxDistro, SystemInfo};
66
use crate::{
77
VALGRIND_CODSPEED_DEB_VERSION, VALGRIND_CODSPEED_VERSION, VALGRIND_CODSPEED_VERSION_STRING,
88
};
@@ -11,22 +11,25 @@ use std::{env, path::Path, process::Command};
1111
use url::Url;
1212

1313
fn get_codspeed_valgrind_filename(system_info: &SystemInfo) -> Result<String> {
14-
let (version, architecture) = match (
15-
system_info.os.as_str(),
16-
system_info.os_version.as_str(),
17-
system_info.arch.as_str(),
18-
) {
19-
("ubuntu", "22.04", "x86_64") | ("debian", "12", "x86_64") => ("22.04", "amd64"),
20-
("ubuntu", "24.04", "x86_64") => ("24.04", "amd64"),
21-
("ubuntu", "22.04", "aarch64") | ("debian", "12", "aarch64") => ("22.04", "arm64"),
22-
("ubuntu", "24.04", "aarch64") => ("24.04", "arm64"),
23-
_ => bail!("Unsupported system"),
14+
let Os::SupportedLinux { distro, version } = &system_info.os else {
15+
bail!("Unsupported system");
2416
};
2517

18+
let (deb_ubuntu_version, architecture) =
19+
match (distro, version.as_str(), system_info.arch.as_str()) {
20+
(SupportedLinuxDistro::Ubuntu, "22.04", "x86_64")
21+
| (SupportedLinuxDistro::Debian, "12", "x86_64") => ("22.04", "amd64"),
22+
(SupportedLinuxDistro::Ubuntu, "24.04", "x86_64") => ("24.04", "amd64"),
23+
(SupportedLinuxDistro::Ubuntu, "22.04", "aarch64")
24+
| (SupportedLinuxDistro::Debian, "12", "aarch64") => ("22.04", "arm64"),
25+
(SupportedLinuxDistro::Ubuntu, "24.04", "aarch64") => ("24.04", "arm64"),
26+
_ => bail!("Unsupported system"),
27+
};
28+
2629
Ok(format!(
2730
"valgrind_{}_ubuntu-{}_{}.deb",
2831
VALGRIND_CODSPEED_DEB_VERSION.as_str(),
29-
version,
32+
deb_ubuntu_version,
3033
architecture
3134
))
3235
}
@@ -175,8 +178,7 @@ mod tests {
175178
#[test]
176179
fn test_system_info_to_codspeed_valgrind_version_ubuntu() {
177180
let system_info = SystemInfo {
178-
os: "ubuntu".to_string(),
179-
os_version: "22.04".to_string(),
181+
os: Os::from_id("ubuntu", "22.04"),
180182
arch: "x86_64".to_string(),
181183
..SystemInfo::test()
182184
};
@@ -189,8 +191,7 @@ mod tests {
189191
#[test]
190192
fn test_system_info_to_codspeed_valgrind_version_ubuntu_24() {
191193
let system_info = SystemInfo {
192-
os: "ubuntu".to_string(),
193-
os_version: "24.04".to_string(),
194+
os: Os::from_id("ubuntu", "24.04"),
194195
arch: "x86_64".to_string(),
195196
..SystemInfo::test()
196197
};
@@ -203,8 +204,7 @@ mod tests {
203204
#[test]
204205
fn test_system_info_to_codspeed_valgrind_version_debian() {
205206
let system_info = SystemInfo {
206-
os: "debian".to_string(),
207-
os_version: "12".to_string(),
207+
os: Os::from_id("debian", "12"),
208208
arch: "x86_64".to_string(),
209209
..SystemInfo::test()
210210
};
@@ -217,8 +217,7 @@ mod tests {
217217
#[test]
218218
fn test_system_info_to_codspeed_valgrind_version_ubuntu_arm() {
219219
let system_info = SystemInfo {
220-
os: "ubuntu".to_string(),
221-
os_version: "22.04".to_string(),
220+
os: Os::from_id("ubuntu", "22.04"),
222221
arch: "aarch64".to_string(),
223222
..SystemInfo::test()
224223
};

0 commit comments

Comments
 (0)