diff --git a/src/api_client.rs b/src/api_client.rs index fff9519e..9e33b874 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -1,6 +1,7 @@ use std::fmt::Display; use crate::prelude::*; +use crate::run_environment::RepositoryProvider; use crate::{app::Cli, config::CodSpeedConfig}; use console::style; use gql_client::{Client as GQLClient, ClientConfig}; @@ -44,7 +45,8 @@ fn build_gql_api_client( GQLClient::new_with_config(ClientConfig { endpoint: api_url, - timeout: Some(10), + // Slightly high to account for cold starts + timeout: Some(20), headers: Some(headers), proxy: None, }) @@ -84,12 +86,6 @@ pub struct FetchLocalRunReportVars { pub run_id: String, } -#[derive(Serialize, Clone)] -#[serde(rename_all = "camelCase")] -pub struct FetchLocalExecReportVars { - pub name: String, - pub run_id: String, -} #[derive(Deserialize, Serialize, Debug, Clone, PartialEq)] pub enum ReportConclusion { AcknowledgedFailure, @@ -179,8 +175,22 @@ pub struct FetchLocalRunReportResponse { pub run: FetchLocalRunReportRun, } -pub struct FetchLocalExecReportResponse { - pub run: FetchLocalRunReportRun, +#[derive(Serialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct GetOrCreateProjectRepositoryVars { + pub name: String, +} + +nest! { + #[derive(Debug, Deserialize, Serialize)]* + #[serde(rename_all = "camelCase")]* + struct GetOrCreateProjectRepositoryData { + get_or_create_project_repository: pub struct GetOrCreateProjectRepositoryPayload { + pub provider: RepositoryProvider, + pub owner: String, + pub name: String, + } + } } impl CodSpeedAPIClient { @@ -237,25 +247,41 @@ impl CodSpeedAPIClient { } } - pub async fn fetch_local_exec_report( + pub async fn get_or_create_project_repository( &self, - vars: FetchLocalExecReportVars, - ) -> Result { + vars: GetOrCreateProjectRepositoryVars, + ) -> Result { let response = self .gql_client - .query_with_vars_unwrap::( - include_str!("queries/FetchLocalExecReport.gql"), + .query_with_vars_unwrap::< + GetOrCreateProjectRepositoryData, + GetOrCreateProjectRepositoryVars, + >( + include_str!("queries/GetOrCreateProjectRepository.gql"), vars.clone(), ) .await; match response { - Ok(response) => Ok(FetchLocalExecReportResponse { - run: response.project.run, - }), + Ok(response) => Ok(response.get_or_create_project_repository), Err(err) if err.contains_error_code("UNAUTHENTICATED") => { bail!("Your session has expired, please login again using `codspeed auth login`") } - Err(err) => bail!("Failed to fetch local run report: {err}"), + Err(err) => bail!("Failed to get or create project repository: {err}"), } } } + +impl CodSpeedAPIClient { + /// Create a test API client for use in tests + #[cfg(test)] + pub fn create_test_client() -> Self { + Self::create_test_client_with_url("http://localhost:8000/graphql".to_owned()) + } + + /// Create a test API client with a custom URL for use in tests + #[cfg(test)] + pub fn create_test_client_with_url(api_url: String) -> Self { + let codspeed_config = CodSpeedConfig::default(); + Self::try_from((&Cli::test_with_url(api_url), &codspeed_config)).unwrap() + } +} diff --git a/src/app.rs b/src/app.rs index b87c7e7b..f840efb5 100644 --- a/src/app.rs +++ b/src/app.rs @@ -102,3 +102,17 @@ pub async fn run() -> Result<()> { } Ok(()) } + +impl Cli { + /// Create a test CLI instance with a custom API URL for use in tests + #[cfg(test)] + pub fn test_with_url(api_url: String) -> Self { + Self { + api_url, + oauth_token: None, + config_name: None, + setup_cache_dir: None, + command: Commands::Setup, + } + } +} diff --git a/src/exec/mod.rs b/src/exec/mod.rs index b1133b63..48e9e06e 100644 --- a/src/exec/mod.rs +++ b/src/exec/mod.rs @@ -3,6 +3,7 @@ use crate::binary_installer::ensure_binary_installed; use crate::config::CodSpeedConfig; use crate::executor; use crate::prelude::*; +use crate::run::uploader::UploadResult; use clap::Args; use std::path::Path; @@ -55,13 +56,16 @@ pub async fn run( ) .await?; - let poll_results_fn = |run_id: String| poll_results::poll_results(api_client, run_id); + let poll_results_fn = async |upload_result: &UploadResult| { + poll_results::poll_results(api_client, upload_result).await + }; executor::execute_benchmarks( executor.as_ref(), &mut execution_context, setup_cache_dir, poll_results_fn, + api_client, ) .await?; diff --git a/src/exec/poll_results.rs b/src/exec/poll_results.rs index 5b2be7df..cbf02ee8 100644 --- a/src/exec/poll_results.rs +++ b/src/exec/poll_results.rs @@ -2,21 +2,24 @@ use console::style; use tokio::time::{Instant, sleep}; use crate::api_client::{ - CodSpeedAPIClient, FetchLocalExecReportResponse, FetchLocalExecReportVars, RunStatus, + CodSpeedAPIClient, FetchLocalRunReportResponse, FetchLocalRunReportVars, RunStatus, }; -use crate::exec::DEFAULT_REPOSITORY_NAME; use crate::prelude::*; use crate::run::helpers::poll_results::{ - POLLING_INTERVAL, RUN_PROCESSING_MAX_DURATION, build_benchmark_table, retry_on_timeout, + POLLING_INTERVAL, RUN_PROCESSING_MAX_DURATION, build_benchmark_table, }; +use crate::run::uploader::UploadResult; #[allow(clippy::borrowed_box)] -pub async fn poll_results(api_client: &CodSpeedAPIClient, run_id: String) -> Result<()> { +pub async fn poll_results( + api_client: &CodSpeedAPIClient, + upload_result: &UploadResult, +) -> Result<()> { let start = Instant::now(); - let fetch_local_exec_report_vars = FetchLocalExecReportVars { - // TODO: Set this dynamically based on the upload endpoint return value - name: DEFAULT_REPOSITORY_NAME.to_owned(), - run_id: run_id.clone(), + let fetch_local_run_report_vars = FetchLocalRunReportVars { + owner: upload_result.owner.clone(), + name: upload_result.repository.clone(), + run_id: upload_result.run_id.clone(), }; start_group!("Fetching the results"); @@ -26,15 +29,12 @@ pub async fn poll_results(api_client: &CodSpeedAPIClient, run_id: String) -> Res bail!("Polling results timed out"); } - let fetch_result = retry_on_timeout(|| async { - api_client - .fetch_local_exec_report(fetch_local_exec_report_vars.clone()) - .await - }) - .await?; + let fetch_result = api_client + .fetch_local_run_report(fetch_local_run_report_vars.clone()) + .await?; match fetch_result { - FetchLocalExecReportResponse { run, .. } + FetchLocalRunReportResponse { run, .. } if run.status == RunStatus::Pending || run.status == RunStatus::Processing => { sleep(POLLING_INTERVAL).await; diff --git a/src/executor/config.rs b/src/executor/config.rs index a6b61766..2ec272b7 100644 --- a/src/executor/config.rs +++ b/src/executor/config.rs @@ -1,4 +1,4 @@ -use crate::exec::{DEFAULT_REPOSITORY_NAME, EXEC_HARNESS_COMMAND}; +use crate::exec::EXEC_HARNESS_COMMAND; use crate::instruments::Instruments; use crate::prelude::*; use crate::run::{RunArgs, UnwindingMode}; @@ -132,35 +132,22 @@ impl TryFrom for Config { .shared .upload_url .unwrap_or_else(|| DEFAULT_UPLOAD_URL.into()); - let mut upload_url = Url::parse(&raw_upload_url) + let upload_url = Url::parse(&raw_upload_url) .map_err(|e| anyhow!("Invalid upload URL: {raw_upload_url}, {e}"))?; - // For exec command, append /project to the upload URL path - upload_url - .path_segments_mut() - .map_err(|_| anyhow!("Cannot append to upload URL"))? - .push("project"); - let wrapped_command = std::iter::once(EXEC_HARNESS_COMMAND.to_owned()) .chain(args.command) .collect::>() .join(" "); - let repository_override = args - .shared - .repository - .map(|repo| RepositoryOverride::from_arg(repo, args.shared.provider)) - .transpose()? - .unwrap_or_else(|| RepositoryOverride { - owner: "projects".to_string(), - repository: DEFAULT_REPOSITORY_NAME.to_string(), - repository_provider: RepositoryProvider::GitHub, - }); - Ok(Self { upload_url, token: args.shared.token, - repository_override: Some(repository_override), + repository_override: args + .shared + .repository + .map(|repo| RepositoryOverride::from_arg(repo, args.shared.provider)) + .transpose()?, working_directory: args.shared.working_directory, mode: args.shared.mode, instruments: Instruments { mongodb: None }, // exec doesn't support MongoDB @@ -303,39 +290,6 @@ mod tests { assert!(result.is_err()); } - #[test] - fn test_try_from_exec_args_appends_project_to_url() { - let exec_args = crate::exec::ExecArgs { - shared: crate::run::ExecAndRunSharedArgs { - upload_url: Some("https://api.codspeed.io/upload".into()), - token: Some("token".into()), - repository: None, - provider: None, - working_directory: None, - mode: RunnerMode::Simulation, - profile_folder: None, - skip_upload: false, - skip_run: false, - skip_setup: false, - allow_empty: false, - perf_run_args: PerfRunArgs { - enable_perf: false, - perf_unwinding_mode: None, - }, - }, - name: None, - command: vec!["my-binary".into()], - }; - - let config = Config::try_from(exec_args).unwrap(); - - assert_eq!( - config.upload_url, - Url::parse("https://api.codspeed.io/upload/project").unwrap() - ); - assert_eq!(config.command, "exec-harness my-binary"); - } - #[test] fn test_try_from_exec_args_default_url() { let exec_args = crate::exec::ExecArgs { @@ -364,7 +318,7 @@ mod tests { assert_eq!( config.upload_url, - Url::parse("https://api.codspeed.io/upload/project").unwrap() + Url::parse("https://api.codspeed.io/upload").unwrap() ); assert_eq!(config.command, "exec-harness my-binary arg1 arg2"); } diff --git a/src/executor/mod.rs b/src/executor/mod.rs index 62c3bcae..3c812153 100644 --- a/src/executor/mod.rs +++ b/src/executor/mod.rs @@ -11,9 +11,11 @@ mod tests; mod valgrind; 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 async_trait::async_trait; pub use config::Config; @@ -99,9 +101,10 @@ pub async fn execute_benchmarks( execution_context: &mut ExecutionContext, setup_cache_dir: Option<&Path>, poll_results: F, + api_client: &CodSpeedAPIClient, ) -> Result<()> where - F: AsyncFn(String) -> Result<()>, + F: AsyncFn(&UploadResult) -> Result<()>, { if !execution_context.config.skip_setup { start_group!("Preparing the environment"); @@ -160,11 +163,11 @@ where start_group!("Uploading performance data"); let upload_result = - crate::run::uploader::upload(execution_context, executor.name()).await?; + crate::run::uploader::upload(execution_context, executor.name(), api_client).await?; end_group!(); if execution_context.is_local() { - poll_results(upload_result.run_id).await?; + poll_results(&upload_result).await?; } } else { debug!("Skipping upload of performance data"); diff --git a/src/queries/FetchLocalExecReport.gql b/src/queries/FetchLocalExecReport.gql deleted file mode 100644 index 146ecae2..00000000 --- a/src/queries/FetchLocalExecReport.gql +++ /dev/null @@ -1,20 +0,0 @@ -query FetchLocalRunReport($name: String!, $runId: String!) { - project(name: $name) { - run(id: $runId) { - id - status - url - headReports { - id - impact - conclusion - } - results { - time - benchmark { - name - } - } - } - } -} diff --git a/src/queries/GetOrCreateProjectRepository.gql b/src/queries/GetOrCreateProjectRepository.gql new file mode 100644 index 00000000..59a96e1c --- /dev/null +++ b/src/queries/GetOrCreateProjectRepository.gql @@ -0,0 +1,7 @@ +mutation Mutation($name: String!) { + getOrCreateProjectRepository(name: $name) { + provider + owner + name + } +} diff --git a/src/run/helpers/poll_results.rs b/src/run/helpers/poll_results.rs index ce51fe44..a3a4fc26 100644 --- a/src/run/helpers/poll_results.rs +++ b/src/run/helpers/poll_results.rs @@ -1,17 +1,10 @@ -use std::future::Future; +use crate::run::helpers; use std::time::Duration; - use tabled::settings::Style; use tabled::{Table, Tabled}; -use tokio::time::sleep; - -use crate::prelude::*; -use crate::run::helpers; pub const RUN_PROCESSING_MAX_DURATION: Duration = Duration::from_secs(60 * 5); // 5 minutes pub const POLLING_INTERVAL: Duration = Duration::from_secs(1); -pub const MAX_FETCH_RETRIES: u32 = 3; -pub const FETCH_RETRY_DELAY: Duration = Duration::from_secs(5); #[derive(Tabled)] struct BenchmarkRow { @@ -35,36 +28,6 @@ pub fn build_benchmark_table( Table::new(&table_rows).with(Style::modern()).to_string() } -/// Retry logic for API calls that may timeout due to cold start in dev environments -pub async fn retry_on_timeout(fetch_fn: F) -> Result -where - F: Fn() -> Fut, - Fut: Future>, -{ - let mut fetch_attempt = 0; - loop { - fetch_attempt += 1; - match fetch_fn().await { - Ok(result) => return Ok(result), - Err(err) => { - let error_message = err.to_string(); - let is_timeout = - error_message.contains("timed out") || error_message.contains("timeout"); - - if is_timeout && fetch_attempt < MAX_FETCH_RETRIES { - debug!( - "Fetch request timed out (attempt {fetch_attempt}/{MAX_FETCH_RETRIES}), retrying in {FETCH_RETRY_DELAY:?}..." - ); - sleep(FETCH_RETRY_DELAY).await; - continue; - } - - return Err(err); - } - } - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/src/run/mod.rs b/src/run/mod.rs index f6419645..c30bb36d 100644 --- a/src/run/mod.rs +++ b/src/run/mod.rs @@ -4,6 +4,7 @@ use crate::config::CodSpeedConfig; use crate::executor; use crate::executor::Config; use crate::prelude::*; +use crate::run::uploader::UploadResult; use crate::run_environment::interfaces::RepositoryProvider; use crate::runner_mode::RunnerMode; use clap::{Args, ValueEnum}; @@ -201,15 +202,15 @@ pub async fn run( executor::ExecutorCommand::Run, ); - let run_environment_metadata = execution_context.provider.get_run_environment_metadata()?; - let poll_results_fn = |run_id: String| { - poll_results::poll_results(api_client, &run_environment_metadata, run_id, output_json) + let poll_results_fn = async |upload_result: &UploadResult| { + poll_results::poll_results(api_client, upload_result, output_json).await }; executor::execute_benchmarks( executor.as_ref(), &mut execution_context, setup_cache_dir, poll_results_fn, + api_client, ) .await?; @@ -226,6 +227,7 @@ impl clap::ValueEnum for RepositoryProvider { match self { Self::GitLab => Some(clap::builder::PossibleValue::new("gitlab").aliases(["gl"])), Self::GitHub => Some(clap::builder::PossibleValue::new("github").aliases(["gh"])), + Self::Project => None, } } } diff --git a/src/run/poll_results.rs b/src/run/poll_results.rs index bb0c6fa7..942f0b89 100644 --- a/src/run/poll_results.rs +++ b/src/run/poll_results.rs @@ -6,24 +6,21 @@ use crate::api_client::{ }; use crate::prelude::*; use crate::run::helpers::poll_results::{ - POLLING_INTERVAL, RUN_PROCESSING_MAX_DURATION, build_benchmark_table, retry_on_timeout, + POLLING_INTERVAL, RUN_PROCESSING_MAX_DURATION, build_benchmark_table, }; -use crate::run_environment::RunEnvironmentMetadata; +use crate::run::uploader::UploadResult; #[allow(clippy::borrowed_box)] pub async fn poll_results( api_client: &CodSpeedAPIClient, - run_environment_metadata: &RunEnvironmentMetadata, - run_id: String, + upload_result: &UploadResult, output_json: bool, ) -> Result<()> { let start = Instant::now(); - let owner = run_environment_metadata.owner.as_str(); - let name = run_environment_metadata.repository.as_str(); let fetch_local_run_report_vars = FetchLocalRunReportVars { - owner: owner.to_owned(), - name: name.to_owned(), - run_id: run_id.to_owned(), + owner: upload_result.owner.clone(), + name: upload_result.repository.clone(), + run_id: upload_result.run_id.clone(), }; start_group!("Fetching the results"); @@ -33,12 +30,9 @@ pub async fn poll_results( bail!("Polling results timed out"); } - let fetch_result = retry_on_timeout(|| async { - api_client - .fetch_local_run_report(fetch_local_run_report_vars.clone()) - .await - }) - .await?; + let fetch_result = api_client + .fetch_local_run_report(fetch_local_run_report_vars.clone()) + .await?; match fetch_result { FetchLocalRunReportResponse { run, .. } @@ -91,7 +85,7 @@ pub async fn poll_results( // We could make use of structured logging for this https://docs.rs/log/latest/log/#structured-logging log_json!(format!( "{{\"event\": \"run_finished\", \"run_id\": \"{}\"}}", - run_id + upload_result.run_id )); } diff --git a/src/run/uploader/mod.rs b/src/run/uploader/mod.rs index 7b60a8f5..a99c2629 100644 --- a/src/run/uploader/mod.rs +++ b/src/run/uploader/mod.rs @@ -5,4 +5,4 @@ mod upload_metadata; pub use interfaces::*; pub use profile_archive::ProfileArchive; -pub use upload::upload; +pub use upload::{UploadResult, upload}; diff --git a/src/run/uploader/upload.rs b/src/run/uploader/upload.rs index 0a7c725a..d119c26e 100644 --- a/src/run/uploader/upload.rs +++ b/src/run/uploader/upload.rs @@ -1,3 +1,4 @@ +use crate::api_client::CodSpeedAPIClient; use crate::executor::Config; use crate::run::{ executor::{ExecutionContext, ExecutorName}, @@ -234,13 +235,17 @@ async fn upload_profile_archive( Ok(()) } +#[derive(Clone)] pub struct UploadResult { pub run_id: String, + pub owner: String, + pub repository: String, } pub async fn upload( execution_context: &mut ExecutionContext, executor_name: ExecutorName, + api_client: &CodSpeedAPIClient, ) -> Result { let profile_archive = create_profile_archive(execution_context, executor_name.clone()).await?; @@ -258,12 +263,16 @@ pub async fn upload( .await?; } - let upload_metadata = execution_context.provider.get_upload_metadata( - &execution_context.config, - &execution_context.system_info, - &profile_archive, - executor_name, - )?; + let upload_metadata = execution_context + .provider + .get_upload_metadata( + &execution_context.config, + &execution_context.system_info, + &profile_archive, + executor_name, + api_client, + ) + .await?; debug!("Upload metadata: {upload_metadata:#?}"); info!( "Linked repository: {}\n", @@ -293,6 +302,8 @@ pub async fn upload( Ok(UploadResult { run_id: upload_data.run_id, + owner: upload_metadata.run_environment_metadata.owner.clone(), + repository: upload_metadata.run_environment_metadata.repository.clone(), }) } @@ -349,9 +360,10 @@ mod tests { ], async { let codspeed_config = CodSpeedConfig::default(); + let api_client = CodSpeedAPIClient::create_test_client(); let mut execution_context = ExecutionContext::try_from((config, &codspeed_config)) .expect("Failed to create ExecutionContext for test"); - upload(&mut execution_context, ExecutorName::Valgrind) + upload(&mut execution_context, ExecutorName::Valgrind, &api_client) .await .unwrap(); }, diff --git a/src/run_environment/interfaces.rs b/src/run_environment/interfaces.rs index 807f7def..9054c5ac 100644 --- a/src/run_environment/interfaces.rs +++ b/src/run_environment/interfaces.rs @@ -8,6 +8,7 @@ pub enum RepositoryProvider { #[default] GitHub, GitLab, + Project, } #[derive(Deserialize, Serialize, Debug, Clone, PartialEq)] diff --git a/src/run_environment/local/provider.rs b/src/run_environment/local/provider.rs index 8ec8e5f9..b06df851 100644 --- a/src/run_environment/local/provider.rs +++ b/src/run_environment/local/provider.rs @@ -2,6 +2,7 @@ use async_trait::async_trait; use git2::Repository; use simplelog::SharedLogger; +use crate::api_client::CodSpeedAPIClient; use crate::executor::config::RepositoryOverride; use crate::executor::{Config, ExecutorName}; use crate::local_logger::get_local_logger; @@ -17,14 +18,23 @@ use crate::run_environment::{RunEnvironment, RunPart}; static FAKE_COMMIT_REF: &str = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; +#[derive(Debug)] +enum RepositorySource { + /// We have repository information (from git or from override) + Git { + repository_provider: RepositoryProvider, + owner: String, + repository: String, + ref_: String, + head_ref: Option, + }, + /// Not in a git repo, no override - fetch repository info from API using project name + ApiProject { project_name: String }, +} + #[derive(Debug)] pub struct LocalProvider { - repository_provider: RepositoryProvider, - owner: String, - repository: String, - pub ref_: String, - pub head_ref: Option, - pub base_ref: Option, + source: RepositorySource, pub event: RunEvent, pub repository_root_path: String, } @@ -36,26 +46,35 @@ impl TryFrom<&Config> for LocalProvider { let repository_root_path = { let Some(mut path) = find_repository_root(¤t_dir) else { - // We are not in a git repository, use the repository_override with very minimal information - let RepositoryOverride { + // We are not in a git repository + if let Some(RepositoryOverride { owner, repository, repository_provider, - } = config.repository_override.clone().context( - "Could not find repository root and no repository was provided, \ - please make sure you are running the command from inside a git repository or provide repository with --repository flag", - )?; - - return Ok(Self { - repository_provider, - ref_: FAKE_COMMIT_REF.to_string(), - head_ref: None, - base_ref: None, - owner, - repository, - repository_root_path: current_dir.to_string_lossy().to_string(), - event: RunEvent::Local, - }); + }) = config.repository_override.clone() + { + // Use the repository_override with very minimal information + return Ok(Self { + source: RepositorySource::Git { + repository_provider, + ref_: FAKE_COMMIT_REF.to_string(), + head_ref: None, + owner, + repository, + }, + repository_root_path: current_dir.to_string_lossy().to_string(), + event: RunEvent::Local, + }); + } else { + // 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(), + }, + repository_root_path: current_dir.to_string_lossy().to_string(), + event: RunEvent::Local, + }); + } }; // Add a trailing slash to the path @@ -94,12 +113,13 @@ impl TryFrom<&Config> for LocalProvider { }; Ok(Self { - repository_provider, - ref_, - head_ref, - base_ref: None, - owner, - repository, + source: RepositorySource::Git { + repository_provider, + ref_, + head_ref, + owner, + repository, + }, event: RunEvent::Local, repository_root_path, }) @@ -115,7 +135,16 @@ impl RunEnvironmentDetector for LocalProvider { #[async_trait(?Send)] impl RunEnvironmentProvider for LocalProvider { fn get_repository_provider(&self) -> RepositoryProvider { - self.repository_provider.clone() + match &self.source { + RepositorySource::Git { + repository_provider, + .. + } => repository_provider.clone(), + RepositorySource::ApiProject { .. } => { + // Placeholder, will be updated from API + RepositoryProvider::GitHub + } + } } fn get_logger(&self) -> Box { @@ -127,33 +156,75 @@ impl RunEnvironmentProvider for LocalProvider { } fn get_run_environment_metadata(&self) -> Result { - Ok(RunEnvironmentMetadata { - base_ref: self.base_ref.clone(), - head_ref: self.head_ref.clone(), - event: self.event.clone(), - gh_data: None, - gl_data: None, - sender: None, - owner: self.owner.clone(), - repository: self.repository.clone(), - ref_: self.ref_.clone(), - repository_root_path: self.repository_root_path.clone(), - }) + match &self.source { + RepositorySource::Git { + owner, + repository, + ref_, + head_ref, + .. + } => Ok(RunEnvironmentMetadata { + base_ref: None, + head_ref: head_ref.clone(), + event: self.event.clone(), + gh_data: None, + gl_data: None, + sender: None, + owner: owner.clone(), + repository: repository.clone(), + ref_: ref_.clone(), + repository_root_path: self.repository_root_path.clone(), + }), + RepositorySource::ApiProject { .. } => Ok(RunEnvironmentMetadata { + base_ref: None, + head_ref: None, + event: self.event.clone(), + gh_data: None, + gl_data: None, + sender: None, + owner: String::new(), + repository: String::new(), + ref_: FAKE_COMMIT_REF.to_string(), + repository_root_path: self.repository_root_path.clone(), + }), + } } - fn get_upload_metadata( + async fn get_upload_metadata( &self, config: &Config, system_info: &SystemInfo, profile_archive: &ProfileArchive, executor_name: ExecutorName, + api_client: &CodSpeedAPIClient, ) -> Result { - let run_environment_metadata = self.get_run_environment_metadata()?; + let mut run_environment_metadata = self.get_run_environment_metadata()?; + let mut repository_provider = self.get_repository_provider(); + + // If we need to fetch repository info from the API + if let RepositorySource::ApiProject { project_name } = &self.source { + debug!("Fetching repository info from API for project: {project_name}"); + let repo_info = api_client + .get_or_create_project_repository( + crate::api_client::GetOrCreateProjectRepositoryVars { + name: project_name.clone(), + }, + ) + .await?; + + debug!("Received repository info: {repo_info:?}"); + + // Update the metadata with the fetched values + run_environment_metadata.owner = repo_info.owner; + run_environment_metadata.repository = repo_info.name; + + repository_provider = repo_info.provider; + } Ok(UploadMetadata { version: Some(LATEST_UPLOAD_METADATA_VERSION), tokenless: config.token.is_none(), - repository_provider: self.get_repository_provider(), + repository_provider, commit_hash: run_environment_metadata.ref_.clone(), run_environment_metadata, profile_md5: profile_archive.hash.clone(), diff --git a/src/run_environment/provider.rs b/src/run_environment/provider.rs index 6aff25d0..192360d9 100644 --- a/src/run_environment/provider.rs +++ b/src/run_environment/provider.rs @@ -2,6 +2,7 @@ use async_trait::async_trait; use git2::Repository; use simplelog::SharedLogger; +use crate::api_client::CodSpeedAPIClient; use crate::executor::{Config, ExecutorName}; use crate::prelude::*; use crate::run::check_system::SystemInfo; @@ -81,14 +82,15 @@ pub trait RunEnvironmentProvider { /// let provider = MyCIProvider::new(); /// let config = Config::new(); /// let instruments = Instruments::new(); - /// let metadata = provider.get_upload_metadata(&config, "abc123").unwrap(); + /// let metadata = provider.get_upload_metadata(&config, "abc123").await.unwrap(); /// ``` - fn get_upload_metadata( + async fn get_upload_metadata( &self, config: &Config, system_info: &SystemInfo, profile_archive: &ProfileArchive, executor_name: ExecutorName, + _api_client: &CodSpeedAPIClient, ) -> Result { let run_environment_metadata = self.get_run_environment_metadata()?;