Skip to content

Commit 0fffde9

Browse files
--wip-- [skip ci]
1 parent 286320f commit 0fffde9

4 files changed

Lines changed: 80 additions & 52 deletions

File tree

src/api_client.rs

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,6 @@ nest! {
225225
pub struct CompareRunsHeadRun {
226226
pub id: String,
227227
pub status: RunStatus,
228-
pub url: String,
229228
}
230229
}
231230

@@ -234,11 +233,9 @@ nest! {
234233
#[serde(rename_all = "camelCase")]*
235234
struct CompareRunsData {
236235
repository: struct CompareRunsRepository {
237-
settings: struct CompareRunsSettings {
238-
allowed_regression: f64,
239-
},
240236
paginated_compare_runs: pub struct CompareRunsComparison {
241237
pub impact: Option<f64>,
238+
pub url: String,
242239
pub head_run: CompareRunsHeadRun,
243240
pub result_comparisons: Vec<CompareRunsBenchmarkResult>,
244241
},
@@ -247,10 +244,15 @@ nest! {
247244
}
248245

249246
pub struct CompareRunsResponse {
250-
pub allowed_regression: f64,
251247
pub comparison: CompareRunsComparison,
252248
}
253249

250+
pub enum CompareRunsOutcome {
251+
Success(CompareRunsResponse),
252+
BaseRunNotFound,
253+
ExecutorMismatch,
254+
}
255+
254256
#[derive(Serialize, Clone)]
255257
#[serde(rename_all = "camelCase")]
256258
pub struct GetOrCreateProjectRepositoryVars {
@@ -321,8 +323,7 @@ impl CodSpeedAPIClient {
321323
}
322324
}
323325

324-
/// Returns `None` if the base run was not found.
325-
pub async fn compare_runs(&self, vars: CompareRunsVars) -> Result<Option<CompareRunsResponse>> {
326+
pub async fn compare_runs(&self, vars: CompareRunsVars) -> Result<CompareRunsOutcome> {
326327
let response = self
327328
.gql_client
328329
.query_with_vars_unwrap::<CompareRunsData, CompareRunsVars>(
@@ -331,15 +332,19 @@ impl CodSpeedAPIClient {
331332
)
332333
.await;
333334
match response {
334-
Ok(response) => Ok(Some(CompareRunsResponse {
335-
allowed_regression: response.repository.settings.allowed_regression,
335+
Ok(response) => Ok(CompareRunsOutcome::Success(CompareRunsResponse {
336336
comparison: response.repository.paginated_compare_runs,
337337
})),
338338
Err(err) if err.contains_error_code("UNAUTHENTICATED") => {
339339
bail!("Your session has expired, please login again using `codspeed auth login`")
340340
}
341-
Err(err) if err.contains_error_code("RUN_NOT_FOUND") => Ok(None),
342-
Err(err) => bail!("Failed to compare runs: {err}"),
341+
Err(err) if err.contains_error_code("RUN_NOT_FOUND") => {
342+
Ok(CompareRunsOutcome::BaseRunNotFound)
343+
}
344+
Err(err) if err.contains_error_code("NOT_FOUND") => {
345+
Ok(CompareRunsOutcome::ExecutorMismatch)
346+
}
347+
Err(err) => bail!("Failed to compare runs: {err:?}"),
343348
}
344349
}
345350

src/queries/CompareRuns.gql

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
11
query CompareRuns($owner: String!, $name: String!, $baseRunId: String!, $headRunId: String!) {
22
repository(owner: $owner, name: $name) {
3-
settings {
4-
allowedRegression
5-
}
63
paginatedCompareRuns(baseRunId: $baseRunId, headRunId: $headRunId) {
74
impact
5+
url
86
headRun {
97
id
108
status
11-
url
129
}
1310
resultComparisons {
1411
benchmark {

src/upload/benchmark_display.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ use tabled::settings::style::HorizontalLine;
1111
use tabled::settings::{Alignment, Color, Modify, Padding, Style};
1212
use tabled::{Table, Tabled};
1313

14+
/// Changes below this threshold are displayed as "~0%" to avoid noise.
15+
const CHANGE_DISPLAY_EPSILON: f64 = 0.005;
16+
1417
fn format_with_thousands_sep(n: u64) -> String {
1518
let s = n.to_string();
1619
let mut result = String::new();
@@ -366,15 +369,17 @@ pub fn build_comparison_table(results: &[CompareRunsBenchmarkResult]) -> String
366369
};
367370

368371
let change_str = match result.change {
372+
Some(c) if c.abs() < CHANGE_DISPLAY_EPSILON => {
373+
format!("{}", style("~0%").dim())
374+
}
369375
Some(c) if c > 0.0 => {
370376
let pct = (c * 100.0).round();
371377
format!("{}", style(format!("+{pct}%")).red().bold())
372378
}
373-
Some(c) if c < 0.0 => {
379+
Some(c) => {
374380
let pct = (c * 100.0).round();
375381
format!("{}", style(format!("{pct}%")).green().bold())
376382
}
377-
Some(_) => format!("{}", style("0%").dim()),
378383
None => "-".to_string(),
379384
};
380385

src/upload/poll_results.rs

Lines changed: 56 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ use super::benchmark_display::{
88
build_benchmark_table, build_comparison_table, build_detailed_summary,
99
};
1010
use crate::api_client::{
11-
CodSpeedAPIClient, CompareRunsResponse, CompareRunsVars, FetchLocalRunResponse,
12-
FetchLocalRunVars, RunStatus,
11+
CodSpeedAPIClient, CompareRunsOutcome, CompareRunsResponse, CompareRunsVars,
12+
FetchLocalRunResponse, FetchLocalRunVars, RunStatus,
1313
};
1414
use crate::local_logger::{start_spinner, stop_spinner};
1515
use crate::prelude::*;
@@ -48,12 +48,18 @@ pub async fn poll_results(
4848
stop_spinner();
4949

5050
match compare_result? {
51-
Some(response) => {
51+
CompareRunsOutcome::Success(response) => {
5252
return display_comparison_results(upload_result, options, response).await;
5353
}
54-
None => {
54+
// Fall back to single run display when comparison is not possible
55+
CompareRunsOutcome::BaseRunNotFound => {
5556
warn!(
56-
"Base run ID \"{base_run_id}\" was not found, falling back to single-run results"
57+
"Base run ID \"{base_run_id}\" was not found, we cannot compare results against it."
58+
);
59+
}
60+
CompareRunsOutcome::ExecutorMismatch => {
61+
warn!(
62+
"Base run ID \"{base_run_id}\" uses a different executor, we cannot compare results against it."
5763
);
5864
}
5965
}
@@ -118,18 +124,34 @@ async fn poll_compare_runs(
118124
api_client: &CodSpeedAPIClient,
119125
upload_result: &UploadResult,
120126
base_run_id: &str,
121-
) -> Result<Option<CompareRunsResponse>> {
127+
) -> Result<CompareRunsOutcome> {
122128
let vars = CompareRunsVars {
123129
owner: upload_result.owner.clone(),
124130
name: upload_result.repository.clone(),
125131
base_run_id: base_run_id.to_string(),
126132
head_run_id: upload_result.run_id.clone(),
127133
};
128-
poll_until_processed(
129-
|| api_client.compare_runs(vars.clone()),
130-
|r: &CompareRunsResponse| &r.comparison.head_run.status,
131-
)
132-
.await
134+
135+
let start = Instant::now();
136+
debug!("Waiting for results to be processed...");
137+
138+
loop {
139+
if start.elapsed() > RUN_PROCESSING_MAX_DURATION {
140+
bail!("Polling results timed out after 5 minutes. Please try again later.");
141+
}
142+
143+
match api_client.compare_runs(vars.clone()).await? {
144+
outcome @ (CompareRunsOutcome::BaseRunNotFound
145+
| CompareRunsOutcome::ExecutorMismatch) => return Ok(outcome),
146+
CompareRunsOutcome::Success(response) => match &response.comparison.head_run.status {
147+
RunStatus::Pending | RunStatus::Processing => sleep(POLLING_INTERVAL).await,
148+
RunStatus::Failure => {
149+
bail!("Run failed to be processed, try again in a few minutes")
150+
}
151+
_ => return Ok(CompareRunsOutcome::Success(response)),
152+
},
153+
}
154+
}
133155
}
134156

135157
async fn display_single_run_results(
@@ -175,7 +197,7 @@ async fn display_single_run_results(
175197
style("View full report:").dim(),
176198
style(&response.run.url).blue().bold().underlined(),
177199
style("To compare future runs against this one, use:").dim(),
178-
style(format!("codspeed run --base {run_id} <command>")).cyan(),
200+
style(format!("--base {run_id}")).cyan(),
179201
);
180202
}
181203

@@ -189,28 +211,6 @@ async fn display_comparison_results(
189211
) -> Result<()> {
190212
let comparison = &response.comparison;
191213

192-
if let Some(impact) = comparison.impact {
193-
let rounded_impact = (impact * 100.0).round();
194-
let (arrow, impact_text) = if impact > 0.0 {
195-
(
196-
style("\u{f062}").green(),
197-
style(format!("+{rounded_impact}%")).green().bold(),
198-
)
199-
} else if impact < 0.0 {
200-
(
201-
style("\u{f063}").red(),
202-
style(format!("{rounded_impact}%")).red().bold(),
203-
)
204-
} else {
205-
(
206-
style("\u{25CF}").dim(),
207-
style(format!("{rounded_impact}%")).dim().bold(),
208-
)
209-
};
210-
let allowed = (response.allowed_regression * 100.0).round();
211-
info!("{arrow} Impact: {impact_text} (allowed regression: -{allowed}%)");
212-
}
213-
214214
if options.output_json {
215215
log_json!(format!(
216216
"{{\"event\": \"run_finished\", \"run_id\": \"{}\"}}",
@@ -226,6 +226,27 @@ async fn display_comparison_results(
226226
end_group!();
227227
start_opened_group!("Benchmark results");
228228

229+
if let Some(impact) = comparison.impact {
230+
let rounded_impact = (impact * 100.0).round();
231+
let (arrow, impact_text) = if impact > 0.0 {
232+
(
233+
style("\u{f062}").green(),
234+
style(format!("+{rounded_impact}%")).green().bold(),
235+
)
236+
} else if impact < 0.0 {
237+
(
238+
style("\u{f063}").red(),
239+
style(format!("{rounded_impact}%")).red().bold(),
240+
)
241+
} else {
242+
(
243+
style("\u{25CF}").dim(),
244+
style(format!("{rounded_impact}%")).dim().bold(),
245+
)
246+
};
247+
info!("{arrow} Impact: {impact_text}");
248+
}
249+
229250
let table = build_comparison_table(&comparison.result_comparisons);
230251
info!("{table}\n");
231252

@@ -243,7 +264,7 @@ async fn display_comparison_results(
243264
info!(
244265
"\n{} {}",
245266
style("View comparison report:").dim(),
246-
style(&comparison.head_run.url).blue().bold().underlined()
267+
style(&comparison.url).blue().bold().underlined()
247268
);
248269
}
249270

0 commit comments

Comments
 (0)