diff --git a/src/cli/exec/mod.rs b/src/cli/exec/mod.rs index e8ed95b04..ab4cb8efc 100644 --- a/src/cli/exec/mod.rs +++ b/src/cli/exec/mod.rs @@ -84,6 +84,7 @@ fn build_orchestrator_config( simulation_tool: args.shared.simulation_tool.unwrap_or_default(), profile_folder: args.shared.profile_folder, skip_upload: args.shared.skip_upload, + export_run_id: args.shared.export_run_id, skip_run: args.shared.skip_run, skip_setup: args.shared.skip_setup, allow_empty: args.shared.allow_empty, diff --git a/src/cli/run/mod.rs b/src/cli/run/mod.rs index b56fc4399..5bac95835 100644 --- a/src/cli/run/mod.rs +++ b/src/cli/run/mod.rs @@ -63,6 +63,7 @@ impl RunArgs { walltime_profiler: None, profile_folder: None, skip_upload: false, + export_run_id: false, skip_run: false, skip_setup: false, allow_empty: false, @@ -128,6 +129,7 @@ fn build_orchestrator_config( simulation_tool: args.shared.simulation_tool.unwrap_or_default(), profile_folder: args.shared.profile_folder, skip_upload: args.shared.skip_upload, + export_run_id: args.shared.export_run_id, skip_run: args.shared.skip_run, skip_setup: args.shared.skip_setup, allow_empty: args.shared.allow_empty, diff --git a/src/cli/shared.rs b/src/cli/shared.rs index 7aba09db5..b79554ce3 100644 --- a/src/cli/shared.rs +++ b/src/cli/shared.rs @@ -87,6 +87,15 @@ pub struct ExecAndRunSharedArgs { )] pub skip_upload: bool, + /// Used internally by the GitHub Action to expose the run id as the `run-id` step output + #[arg( + long, + default_value = "false", + hide = true, + env = "CODSPEED_EXPORT_RUN_ID" + )] + pub export_run_id: 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)] diff --git a/src/executor/config.rs b/src/executor/config.rs index 7d51d1b1d..ceba92ab9 100644 --- a/src/executor/config.rs +++ b/src/executor/config.rs @@ -77,6 +77,8 @@ pub struct OrchestratorConfig { pub profile_folder: Option, pub skip_upload: bool, + /// If true, expose the run id to the next steps of the CI job after the upload + pub export_run_id: bool, pub skip_run: bool, pub skip_setup: bool, /// If true, allow execution even when no benchmarks are found @@ -247,6 +249,7 @@ impl OrchestratorConfig { simulation_tool: SimulationTool::default(), profile_folder: None, skip_upload: false, + export_run_id: false, skip_run: false, skip_setup: false, allow_empty: false, diff --git a/src/executor/orchestrator.rs b/src/executor/orchestrator.rs index ca2dbdf4f..66eca201d 100644 --- a/src/executor/orchestrator.rs +++ b/src/executor/orchestrator.rs @@ -226,6 +226,12 @@ impl Orchestrator { let last_upload_result = self.upload_all(&mut completed_runs, api_client).await?; end_group!(); + if self.config.export_run_id { + if let Err(e) = self.provider.export_run_id(&last_upload_result.run_id) { + warn!("Failed to expose the run id as a step output: {e}"); + } + } + if self.is_local() { poll_results( api_client, diff --git a/src/run_environment/github_actions/provider.rs b/src/run_environment/github_actions/provider.rs index 4a411db2b..fbaf1fd36 100644 --- a/src/run_environment/github_actions/provider.rs +++ b/src/run_environment/github_actions/provider.rs @@ -7,6 +7,7 @@ use serde::Deserialize; use serde_json::Value; use simplelog::SharedLogger; use std::collections::BTreeMap; +use std::io::Write; use std::{env, fs}; use crate::api_client::{Authentication, CodSpeedAPIClient}; @@ -382,6 +383,18 @@ impl RunEnvironmentProvider for GitHubActionsProvider { Ok(()) } + + /// Write the run id as the `run-id` step output. + fn export_run_id(&self, run_id: &str) -> Result<()> { + let output_path = get_env_variable("GITHUB_OUTPUT")?; + let mut output_file = fs::OpenOptions::new() + .append(true) + .create(true) + .open(output_path)?; + writeln!(output_file, "run-id={run_id}")?; + + Ok(()) + } } #[cfg(test)] @@ -833,4 +846,37 @@ mod tests { }, ) } + + #[test] + fn test_export_run_id() { + let output_file = tempfile::NamedTempFile::new().unwrap(); + fs::write(output_file.path(), "previous=value\n").unwrap(); + + with_var("GITHUB_OUTPUT", Some(output_file.path()), || { + let github_actions_provider = GitHubActionsProvider { + owner: "owner".into(), + repository: "repository".into(), + ref_: "refs/head/my-branch".into(), + head_ref: Some("my-branch".into()), + base_ref: None, + sender: None, + run_id: "123789".into(), + job_name: "my_job".into(), + event: RunEvent::Push, + repository_root_path: "/home/work/my-repo".into(), + is_head_repo_fork: false, + is_repository_private: false, + oidc_config: None, + }; + + github_actions_provider + .export_run_id("66f7e2c1a3b4d5e6f7a8b9c0") + .unwrap(); + }); + + assert_eq!( + fs::read_to_string(output_file.path()).unwrap(), + "previous=value\nrun-id=66f7e2c1a3b4d5e6f7a8b9c0\n" + ); + } } diff --git a/src/run_environment/provider.rs b/src/run_environment/provider.rs index 317ac2178..d8c1628c9 100644 --- a/src/run_environment/provider.rs +++ b/src/run_environment/provider.rs @@ -100,6 +100,11 @@ pub trait RunEnvironmentProvider { Ok(()) } + /// Expose the CodSpeed run id to the next steps of the CI job, if supported. + fn export_run_id(&self, _run_id: &str) -> Result<()> { + Ok(()) + } + /// Returns the metadata necessary for uploading results to CodSpeed. /// /// `orchestrator_run_part_suffix` is structured data from the orchestrator used to differentiate