From 96087f7705ea74b5217c89dc463467c79524d053 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 7 Nov 2023 18:40:45 +0100 Subject: [PATCH] Revert "Redesign status page" --- database/src/lib.rs | 1 - database/src/pool.rs | 2 +- database/src/pool/postgres.rs | 65 ++- database/src/pool/sqlite.rs | 77 ++-- site/frontend/package-lock.json | 32 -- site/frontend/package.json | 1 - site/frontend/src/pages/status/data.ts | 54 +-- site/frontend/src/pages/status/expansion.ts | 19 - site/frontend/src/pages/status/page.vue | 475 ++++---------------- site/src/api.rs | 21 +- site/src/github.rs | 4 +- site/src/request_handlers/status_page.rs | 58 +-- 12 files changed, 191 insertions(+), 618 deletions(-) delete mode 100644 site/frontend/src/pages/status/expansion.ts diff --git a/database/src/lib.rs b/database/src/lib.rs index 663d41d61..48f7ad9fb 100644 --- a/database/src/lib.rs +++ b/database/src/lib.rs @@ -710,7 +710,6 @@ pub struct CompileBenchmark { #[derive(Debug)] pub struct ArtifactCollection { - pub artifact: ArtifactId, pub duration: Duration, pub end_time: DateTime, } diff --git a/database/src/pool.rs b/database/src/pool.rs index 41e429828..1e40c2bb8 100644 --- a/database/src/pool.rs +++ b/database/src/pool.rs @@ -158,7 +158,7 @@ pub trait Connection: Send + Sync { async fn in_progress_steps(&self, aid: &ArtifactId) -> Vec; - async fn last_n_artifact_collections(&self, n: u32) -> Vec; + async fn last_artifact_collection(&self) -> Option; /// Returns the sha of the parent commit, if available. /// diff --git a/database/src/pool/postgres.rs b/database/src/pool/postgres.rs index f8d744947..aabd5df3f 100644 --- a/database/src/pool/postgres.rs +++ b/database/src/pool/postgres.rs @@ -1216,31 +1216,21 @@ where }) .collect() } - async fn last_n_artifact_collections(&self, n: u32) -> Vec { + async fn last_artifact_collection(&self) -> Option { self.conn() - .query( - "select art.name, art.date, art.type, acd.date_recorded, acd.duration \ - from artifact_collection_duration as acd \ - join artifact as art on art.id = acd.aid \ + .query_opt( + "select date_recorded, duration \ + from artifact_collection_duration \ order by date_recorded desc \ - limit $1;", - &[&n], + limit 1;", + &[], ) .await .unwrap() - .into_iter() - .map(|r| { - let sha = r.get::<_, String>(0); - let date = r.get::<_, Option>>(1); - let ty = r.get::<_, String>(2); - - ArtifactCollection { - artifact: parse_artifact_id(&ty, &sha, date), - end_time: r.get(3), - duration: Duration::from_secs(r.get::<_, i32>(4) as u64), - } + .map(|r| ArtifactCollection { + end_time: r.get(0), + duration: Duration::from_secs(r.get::<_, i32>(1) as u64), }) - .collect() } async fn parent_of(&self, sha: &str) -> Option { self.conn() @@ -1384,7 +1374,23 @@ where .unwrap()?; let date = row.get::<_, Option>>(0); let ty = row.get::<_, String>(1); - Some(parse_artifact_id(&ty, artifact, date)) + + match ty.as_str() { + "master" => Some(ArtifactId::Commit(Commit { + sha: artifact.to_owned(), + date: Date(date.expect("date present for master commits")), + r#type: CommitType::Master, + })), + "try" => Some(ArtifactId::Commit(Commit { + sha: artifact.to_owned(), + date: date + .map(Date) + .unwrap_or_else(|| Date::ymd_hms(2000, 1, 1, 0, 0, 0)), + r#type: CommitType::Try, + })), + "release" => Some(ArtifactId::Tag(artifact.to_owned())), + _ => panic!("unknown artifact type: {:?}", ty), + } } async fn purge_artifact(&self, aid: &ArtifactId) { @@ -1397,22 +1403,3 @@ where .unwrap(); } } - -fn parse_artifact_id(ty: &str, sha: &str, date: Option>) -> ArtifactId { - match ty { - "master" => ArtifactId::Commit(Commit { - sha: sha.to_owned(), - date: Date(date.expect("date present for master commits")), - r#type: CommitType::Master, - }), - "try" => ArtifactId::Commit(Commit { - sha: sha.to_owned(), - date: date - .map(Date) - .unwrap_or_else(|| Date::ymd_hms(2000, 1, 1, 0, 0, 0)), - r#type: CommitType::Try, - }), - "release" => ArtifactId::Tag(sha.to_owned()), - _ => panic!("unknown artifact type: {:?}", ty), - } -} diff --git a/database/src/pool/sqlite.rs b/database/src/pool/sqlite.rs index c775ca439..a8cd21f84 100644 --- a/database/src/pool/sqlite.rs +++ b/database/src/pool/sqlite.rs @@ -577,7 +577,25 @@ impl Connection for SqliteConnection { .optional() .unwrap()?; - Some(parse_artifact_id(ty.as_str(), artifact, date)) + match ty.as_str() { + "master" => Some(ArtifactId::Commit(Commit { + sha: artifact.to_owned(), + date: Date( + Utc.timestamp_opt(date.expect("master has date"), 0) + .unwrap(), + ), + r#type: CommitType::Master, + })), + "try" => Some(ArtifactId::Commit(Commit { + sha: artifact.to_owned(), + date: date + .map(|d| Date(Utc.timestamp_opt(d, 0).unwrap())) + .unwrap_or_else(|| Date::ymd_hms(2000, 1, 1, 0, 0, 0)), + r#type: CommitType::Try, + })), + "release" => Some(ArtifactId::Tag(artifact.to_owned())), + _ => panic!("unknown artifact type: {:?}", ty), + } } async fn record_duration(&self, artifact: ArtifactIdNumber, duration: Duration) { @@ -1148,31 +1166,24 @@ impl Connection for SqliteConnection { .collect() } - async fn last_n_artifact_collections(&self, n: u32) -> Vec { + async fn last_artifact_collection(&self) -> Option { self.raw_ref() - .prepare_cached( - "select art.name, art.date, art.type, acd.date_recorded, acd.duration \ - from artifact_collection_duration as acd \ - join artifact as art on art.id = acd.aid \ + .query_row( + "select date_recorded, duration \ + from artifact_collection_duration \ order by date_recorded desc \ - limit ?;", + limit 1;", + params![], + |r| { + Ok(( + Utc.timestamp_opt(r.get(0)?, 0).unwrap(), + Duration::from_secs(r.get(1)?), + )) + }, ) + .optional() .unwrap() - .query(params![&n]) - .unwrap() - .mapped(|r| { - let sha = r.get::<_, String>(0)?; - let date = r.get::<_, Option>(1)?; - let ty = r.get::<_, String>(2)?; - - Ok(ArtifactCollection { - artifact: parse_artifact_id(&ty, &sha, date), - end_time: Utc.timestamp_opt(r.get(3)?, 0).unwrap(), - duration: Duration::from_secs(r.get(4)?), - }) - }) - .collect::, _>>() - .unwrap() + .map(|(end_time, duration)| ArtifactCollection { end_time, duration }) } async fn parent_of(&self, sha: &str) -> Option { @@ -1241,25 +1252,3 @@ impl Connection for SqliteConnection { .unwrap(); } } - -fn parse_artifact_id(ty: &str, sha: &str, date: Option) -> ArtifactId { - match ty { - "master" => ArtifactId::Commit(Commit { - sha: sha.to_owned(), - date: Date( - Utc.timestamp_opt(date.expect("master has date"), 0) - .unwrap(), - ), - r#type: CommitType::Master, - }), - "try" => ArtifactId::Commit(Commit { - sha: sha.to_owned(), - date: date - .map(|d| Date(Utc.timestamp_opt(d, 0).unwrap())) - .unwrap_or_else(|| Date::ymd_hms(2000, 1, 1, 0, 0, 0)), - r#type: CommitType::Try, - }), - "release" => ArtifactId::Tag(sha.to_owned()), - _ => panic!("unknown artifact type: {:?}", ty), - } -} diff --git a/site/frontend/package-lock.json b/site/frontend/package-lock.json index a7a4bf45e..c52b065cd 100644 --- a/site/frontend/package-lock.json +++ b/site/frontend/package-lock.json @@ -9,7 +9,6 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "date-fns": "^2.30.0", "highcharts": "6.0.7", "msgpack-lite": "^0.1.26", "parcel": "^2.8.3", @@ -136,22 +135,6 @@ "node": ">=6.0.0" } }, - "node_modules/@babel/runtime": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.15.tgz", - "integrity": "sha512-T0O+aa+4w0u06iNmapipJXMV4HoUir03hpx3/YqXXhu9xim3w+dVphjFWl1OH8NbZHw5Lbm9k45drDkgq2VNNA==", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/runtime/node_modules/regenerator-runtime": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", - "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" - }, "node_modules/@babel/types": { "version": "7.21.4", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.4.tgz", @@ -2308,21 +2291,6 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz", "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==" }, - "node_modules/date-fns": { - "version": "2.30.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", - "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", - "dependencies": { - "@babel/runtime": "^7.21.0" - }, - "engines": { - "node": ">=0.11" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/date-fns" - } - }, "node_modules/de-indent": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", diff --git a/site/frontend/package.json b/site/frontend/package.json index 565b05313..c802b28d4 100644 --- a/site/frontend/package.json +++ b/site/frontend/package.json @@ -20,7 +20,6 @@ "vue-tsc": "^1.8.3" }, "dependencies": { - "date-fns": "^2.30.0", "highcharts": "6.0.7", "msgpack-lite": "^0.1.26", "parcel": "^2.8.3", diff --git a/site/frontend/src/pages/status/data.ts b/site/frontend/src/pages/status/data.ts index 6f86ffe4f..90defe6e3 100644 --- a/site/frontend/src/pages/status/data.ts +++ b/site/frontend/src/pages/status/data.ts @@ -1,10 +1,10 @@ -export interface Commit { +interface Commit { sha: string; date: string; type: "Try" | "Master"; } -export interface BenchmarkError { +interface BenchmarkStatus { name: string; error: string; } @@ -16,50 +16,20 @@ interface Step { current_progress: number; } -export type Artifact = - | { - Commit: Commit; - } - | { - Tag: string; - }; - -export type MissingReason = - | { - Master: { - pr: number; - parent_sha: string; - is_try_parent: boolean; - }; - } - | { - Try: { - pr: number; - parent_sha: string; - include: string | null; - exclude: string | null; - runs: number | null; - }; - } - | { - InProgress: MissingReason; - }; - +/** + * The `any` types in the interface below were chosen because the types are quite complex + * on the Rust side (they are modeled with enums encoded in a way that is not so simple to model in + * TS). + */ interface CurrentState { - artifact: Artifact; + artifact: any; progress: Step[]; } -export interface FinishedRun { - artifact: Artifact; - pr: number | null; - errors: BenchmarkError[]; - duration: number; - finished_at: number; -} - export interface StatusResponse { - finished_runs: FinishedRun[]; + last_commit: Commit | null; + benchmarks: BenchmarkStatus[]; + missing: Array<[Commit, any]>; current: CurrentState | null; - missing: Array<[Commit, MissingReason]>; + most_recent_end: number | null; } diff --git a/site/frontend/src/pages/status/expansion.ts b/site/frontend/src/pages/status/expansion.ts deleted file mode 100644 index c5347b40b..000000000 --- a/site/frontend/src/pages/status/expansion.ts +++ /dev/null @@ -1,19 +0,0 @@ -import {ref} from "vue"; - -export function useExpandedStore() { - const expanded = ref(new Set()); - - function isExpanded(sha: string) { - return expanded.value.has(sha); - } - - function toggleExpanded(sha: string) { - if (isExpanded(sha)) { - expanded.value.delete(sha); - } else { - expanded.value.add(sha); - } - } - - return {toggleExpanded, isExpanded}; -} diff --git a/site/frontend/src/pages/status/page.vue b/site/frontend/src/pages/status/page.vue index bd39e03e8..2e75302b8 100644 --- a/site/frontend/src/pages/status/page.vue +++ b/site/frontend/src/pages/status/page.vue @@ -2,25 +2,8 @@ import {getJson} from "../../utils/requests"; import {STATUS_DATA_URL} from "../../urls"; import {withLoading} from "../../utils/loading"; -import {computed, ref, Ref, watch} from "vue"; -import { - Artifact, - BenchmarkError, - Commit, - FinishedRun, - MissingReason, - StatusResponse, -} from "./data"; -import { - addSeconds, - differenceInSeconds, - format, - fromUnixTime, - sub, - subSeconds, -} from "date-fns"; -import {CompileTestCase} from "../compare/compile/common"; -import {useExpandedStore} from "./expansion"; +import {computed, ref, Ref} from "vue"; +import {StatusResponse} from "./data"; async function loadStatus(loading: Ref) { data.value = await withLoading(loading, () => @@ -36,79 +19,53 @@ function formatDuration(seconds: number): string { let s = ""; if (hours > 0) { - s = `${hours}h ${mins < 10 ? "0" + mins : mins}m ${ + s = `${hours}h${mins < 10 ? "0" + mins : mins}m${ secs < 10 ? "0" + secs : secs }s`; } else { - s = `${mins < 10 ? " " + mins : mins}m ${secs < 10 ? "0" + secs : secs}s`; + s = `${mins < 10 ? " " + mins : mins}m${secs < 10 ? "0" + secs : secs}s`; } return s; } -function getArtifactPr(reason: MissingReason): number { - if (reason.hasOwnProperty("InProgress")) { - return getArtifactPr(reason["InProgress"]); - } else if (reason.hasOwnProperty("Master")) { - return reason["Master"].pr; - } else if (reason.hasOwnProperty("Try")) { - return reason["Try"].pr; - } else { - throw new Error(`Unknown missing reason ${reason}`); - } +function commitUrl(commit: {sha: string}): string { + return `${commit.sha.substring(0, 13)}`; } -function getArtifactSha(artifact: Artifact): string { - if (artifact.hasOwnProperty("Commit")) { - return artifact["Commit"].sha; - } else if (artifact.hasOwnProperty("Tag")) { - return artifact["Tag"]; +function formatArtifact(artifact: any): string { + if (artifact.Commit) { + return commitUrl(artifact.Commit); } else { - throw new Error(`Unknown artifact ${artifact}`); + return artifact.Tag; } } -function commitUrlAsHtml(sha: string): string { - return `${sha.substring( - 0, - 13 - )}`; -} - -function pullRequestUrlAsHtml(pr: number | null): string { - if (pr === null) { - return ""; +function formatReason(reason: any): string { + if (typeof reason == "string") { + return reason; + } else if (reason.InProgress) { + return `${formatReason(reason.InProgress)} - in progress`; + } else if (reason["Master"] !== undefined && reason.Master.pr) { + return ` + #${reason["Master"].pr}${ + reason.Master.is_try_parent ? " - Try commit parent" : "" + }`; + } else if (reason["Master"] !== undefined && reason.Master.pr == 0) { + return "Master"; + } else if (reason["Try"] !== undefined && reason.Try.pr) { + return ` + Try for + + #${reason["Try"].pr} + `; + } else { + // Should never happen, but a reasonable fallback + return JSON.stringify(reason); } - return `#${pr}`; -} - -function formatCommitAsHtml(commit: Commit, reason: MissingReason): string { - const url = commitUrlAsHtml(commit.sha); - const type = commit.type === "Try" ? "Try" : "Master"; - const pr = getArtifactPr(reason); - - return `${type} commit ${url} (${pullRequestUrlAsHtml(pr)})`; -} - -interface CurrentRun { - commit: Commit; - missing_reason: MissingReason; - start: Date; - expected_end: Date; - expected_total: number; - progress: number; -} - -interface TimelineEntry { - pr: number | null; - kind: string; - sha: string; - status: string; - end_time: Date; - end_estimated: boolean; - duration: number | null; - errors: BenchmarkError[]; - warning: string | null; - current: boolean; } const timeLeft = computed(() => { @@ -116,143 +73,15 @@ const timeLeft = computed(() => { let left = 0; for (const step of steps) { if (!step.is_done) { - left += Math.max(0, step.expected_duration - step.current_progress); + left += step.expected_duration - step.current_progress; } } return left; }); - -const currentRun: Ref = computed(() => { - const current = data.value?.current ?? null; - if (current === null) return null; - const sha = getArtifactSha(current.artifact); - - const elapsed = current.progress.reduce( - (prev, current) => prev + current.current_progress, - 0 - ); - const start = subSeconds(new Date(), elapsed); - const expected_total = current.progress.reduce( - (prev, current) => prev + current.expected_duration, - 0 - ); - const progress = Math.min(1, elapsed / Math.max(1, expected_total)); - - for (const [commit, reason] of data.value.missing) { - if (commit.sha === sha && reason.hasOwnProperty("InProgress")) { - return { - commit, - missing_reason: reason["InProgress"], - start, - expected_end: addSeconds(new Date(), timeLeft.value), - expected_total, - progress, - }; - } - } - return null; -}); -const lastFinishedRun: Ref = computed(() => { - const finished = data.value?.finished_runs; - if (finished.length === 0) return null; - return finished[0]; -}); -const expectedDuration: Ref = computed(() => { - if (data.value === null) return null; - const current = currentRun.value; - if (current !== null) { - return current.expected_total; - } else { - return lastFinishedRun.value?.duration; - } -}); - -// Timeline of past, current and future runs. -// Ordered from future to past - at the beginning is the item with the least -// priority in the queue, and at the end is the oldest finished run. -const timeline: Ref = computed(() => { - if (data.value === null) return []; - const timeline: TimelineEntry[] = []; - - const current = currentRun.value; - const currentTimeLeft = timeLeft.value ?? 0; - const expectedRunDuration = expectedDuration.value; - - // The next commit to be benchmarked will be at last position of `queued` - const queued = data.value.missing - .filter(([_, reason]) => !reason.hasOwnProperty("InProgress")) - .reverse(); - let queued_before = queued.length - 1; - for (const [commit, reason] of queued) { - const expected_end = addSeconds( - current?.expected_end ?? new Date(), - currentTimeLeft + queued_before * expectedRunDuration - ); - - let kind = commit.type; - if (reason.hasOwnProperty("Master") && reason["Master"].is_try_parent) { - kind += " (try parent)"; - } - - timeline.push({ - pr: getArtifactPr(reason), - kind, - sha: commit.sha, - status: `Queued (#${queued_before + 1})`, - end_time: expected_end, - end_estimated: true, - errors: [], - duration: null, - warning: null, - current: false, - }); - queued_before -= 1; - } - - if (current !== null) { - timeline.push({ - pr: getArtifactPr(current.missing_reason), - kind: current.commit.type, - sha: current.commit.sha, - status: "In progress", - end_time: current.expected_end, - end_estimated: true, - errors: [], - duration: null, - warning: null, - current: true, - }); - } - - for (const run of data.value.finished_runs) { - const kind = run.artifact.hasOwnProperty("Tag") - ? run.artifact["Tag"] - : run.artifact["Commit"].type; - - let warning = null; - if (kind !== "Try" && run.errors.length > 0) { - warning = "Master commit with errors"; - } - timeline.push({ - pr: run.pr, - kind, - sha: getArtifactSha(run.artifact), - status: "Finished", - end_time: fromUnixTime(run.finished_at), - end_estimated: false, - errors: run.errors, - duration: run.duration, - warning, - current: false, - }); - } - - return timeline; +const recentEndDate = computed(() => { + return new Date(data.value.most_recent_end * 1000); }); -const {toggleExpanded: toggleExpandedErrors, isExpanded: hasExpandedErrors} = - useExpandedStore(); - const loading = ref(true); const data: Ref = ref(null); @@ -260,42 +89,14 @@ loadStatus(loading); diff --git a/site/src/api.rs b/site/src/api.rs index ad0ae1190..ffa9d3057 100644 --- a/site/src/api.rs +++ b/site/src/api.rs @@ -233,7 +233,7 @@ pub mod status { use serde::{Deserialize, Serialize}; #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] - pub struct BenchmarkError { + pub struct BenchmarkStatus { pub name: String, pub error: String, } @@ -254,23 +254,14 @@ pub mod status { pub progress: Vec, } - #[derive(Serialize, Debug)] - pub struct FinishedRun { - pub artifact: ArtifactId, - pub pr: Option, - pub errors: Vec, - // In seconds - pub duration: u64, - // Unix timestamp - pub finished_at: u64, - } - #[derive(Serialize, Debug)] pub struct Response { - // Ordered from newest to oldest - pub finished_runs: Vec, - pub current: Option, + pub last_commit: Option, + pub benchmarks: Vec, pub missing: Vec<(Commit, MissingReason)>, + pub current: Option, + // None if no recent end, otherwise seconds since epoch + pub most_recent_end: Option, } } diff --git a/site/src/github.rs b/site/src/github.rs index 85f74c92f..23a0c5c9b 100644 --- a/site/src/github.rs +++ b/site/src/github.rs @@ -332,10 +332,8 @@ async fn estimate_queue_info( // Guess the expected full run duration of a waiting commit let last_duration = conn - .last_n_artifact_collections(1) + .last_artifact_collection() .await - .into_iter() - .next() .map(|collection| collection.duration) .unwrap_or(Duration::ZERO); diff --git a/site/src/request_handlers/status_page.rs b/site/src/request_handlers/status_page.rs index e1e2563e5..a5d0b2b7f 100644 --- a/site/src/request_handlers/status_page.rs +++ b/site/src/request_handlers/status_page.rs @@ -2,16 +2,14 @@ use std::str; use std::sync::Arc; use crate::api::status; -use crate::api::status::FinishedRun; use crate::db::{ArtifactId, Lookup}; use crate::load::SiteCtxt; -// How many historical (finished) runs should be returned from the status API. -const FINISHED_RUN_COUNT: u64 = 5; - pub async fn handle_status_page(ctxt: Arc) -> status::Response { - let missing = ctxt.missing_commits().await; + let idx = ctxt.index.load(); + let last_commit = idx.commits().last().cloned(); + let missing = ctxt.missing_commits().await; // FIXME: no current builds let conn = ctxt.conn().await; let current = if let Some(artifact) = conn.in_progress_artifacts().await.pop() { @@ -35,41 +33,29 @@ pub async fn handle_status_page(ctxt: Arc) -> status::Response { None }; - // FIXME: load at least one master commit with errors, if no master commit is in last N commits? - // FIXME: cache this whole thing, or write a specific SQL query for it - let mut finished_runs = Vec::new(); - let idx = ctxt.index.load(); - - let recent_collections = conn - .last_n_artifact_collections(FINISHED_RUN_COUNT as u32) - .await; - for collection in recent_collections { - let errors = conn - .get_error(collection.artifact.lookup(&idx).unwrap()) - .await; - let mut pr = None; - if let ArtifactId::Commit(ref commit) = collection.artifact { - pr = conn.pr_of(&commit.sha).await; - } - finished_runs.push(FinishedRun { - artifact: collection.artifact, - pr, - errors: errors - .into_iter() - .map(|(name, error)| { - let error = prettify_log(&error).unwrap_or(error); - status::BenchmarkError { name, error } - }) - .collect::>(), - duration: collection.duration.as_secs(), - finished_at: collection.end_time.timestamp() as u64, - }); - } + let errors = if let Some(last) = &last_commit { + conn.get_error(ArtifactId::from(last.clone()).lookup(&idx).unwrap()) + .await + } else { + Default::default() + }; + let benchmark_state = errors + .into_iter() + .map(|(name, error)| { + let error = prettify_log(&error).unwrap_or(error); + status::BenchmarkStatus { name, error } + }) + .collect::>(); status::Response { - finished_runs, + last_commit, + benchmarks: benchmark_state, missing, current, + most_recent_end: conn + .last_artifact_collection() + .await + .map(|d| d.end_time.timestamp()), } }