diff --git a/migrations/20250703200547_broken-error.sql b/migrations/20250703200547_broken-error.sql new file mode 100644 index 0000000..8f6dfde --- /dev/null +++ b/migrations/20250703200547_broken-error.sql @@ -0,0 +1,4 @@ +-- Add migration script here + +ALTER TABLE finished_nightly + ADD COLUMN broken_error BOOLEAN DEFAULT NULL; diff --git a/src/build.rs b/src/build.rs index 1e4aa13..32fa1e1 100644 --- a/src/build.rs +++ b/src/build.rs @@ -62,7 +62,7 @@ async fn background_builder_inner(db: &Db, nightly_cache: &mut NightlyCache) -> .wrap_err_with(|| format!("building targets for toolchain {nightly}")); if let Err(err) = result { error!(%nightly, %mode, ?err, "Failed to build nightly"); - db.finish_nightly_as_broken(&nightly, mode) + db.finish_nightly_as_broken(&nightly, mode, &format!("{err:?}")) .await .wrap_err("marking nightly as broken")?; } diff --git a/src/db.rs b/src/db.rs index 719eeb3..42c1811 100644 --- a/src/db.rs +++ b/src/db.rs @@ -73,6 +73,14 @@ pub struct FinishedNightly { pub mode: BuildMode, } +#[derive(sqlx::FromRow, Debug, PartialEq, Eq, Hash)] +pub struct FinishedNightlyWithBroken { + pub nightly: String, + pub mode: BuildMode, + pub is_broken: bool, + pub broken_error: Option, +} + impl Db { pub async fn open(path: &str) -> Result { let db_opts = SqliteConnectOptions::from_str(path) @@ -120,6 +128,16 @@ impl Db { .wrap_err("getting history for single nightly") } + pub async fn nightly_info(&self, nightly: &str) -> Result> { + sqlx::query_as::<_, FinishedNightlyWithBroken>( + "SELECT nightly, mode, is_broken, broken_error FROM finished_nightly WHERE nightly = ?", + ) + .bind(nightly) + .fetch_all(&self.conn) + .await + .wrap_err("getting finished_nightly for single nightly") + } + pub async fn target_list(&self) -> Result> { #[derive(sqlx::FromRow)] struct TargetName { @@ -204,10 +222,16 @@ impl Db { Ok(()) } - pub async fn finish_nightly_as_broken(&self, nightly: &str, mode: BuildMode) -> Result<()> { - sqlx::query("INSERT INTO finished_nightly (nightly, mode, is_broken) VALUES (?, ?, TRUE)") + pub async fn finish_nightly_as_broken( + &self, + nightly: &str, + mode: BuildMode, + error: &str, + ) -> Result<()> { + sqlx::query("INSERT INTO finished_nightly (nightly, mode, is_broken, broken_error) VALUES (?, ?, TRUE, ?)") .bind(nightly) .bind(mode) + .bind(error) .execute(&self.conn) .await .wrap_err("inserting finished broken nightly")?; diff --git a/src/web.rs b/src/web.rs index d0911d5..4bb4221 100644 --- a/src/web.rs +++ b/src/web.rs @@ -156,47 +156,66 @@ async fn web_nightly(State(state): State, Query(query): Query, Option)>, core_failures: usize, std_failures: usize, + core_broken: Option, + std_broken: Option, } match state.db.history_for_nightly(&query.nightly).await { - Ok(builds) => { - let mut builds_grouped = - HashMap::, Option)>::new(); - for build in &builds { - let v = builds_grouped.entry(build.target.clone()).or_default(); - match build.mode { - BuildMode::Core => v.0 = Some(build.clone()), - BuildMode::MiriStd => v.1 = Some(build.clone()), - } - } - - let mut std_failures = 0; - let mut core_failures = 0; - for build in builds { - if build.status == Status::Error { + Ok(builds) => match state.db.nightly_info(&query.nightly).await { + Ok(info) => { + let mut builds_grouped = + HashMap::, Option)>::new(); + for build in &builds { + let v = builds_grouped.entry(build.target.clone()).or_default(); match build.mode { - BuildMode::Core => core_failures += 1, - BuildMode::MiriStd => std_failures += 1, + BuildMode::Core => v.0 = Some(build.clone()), + BuildMode::MiriStd => v.1 = Some(build.clone()), } } + + let mut std_failures = 0; + let mut core_failures = 0; + for build in builds { + if build.status == Status::Error { + match build.mode { + BuildMode::Core => core_failures += 1, + BuildMode::MiriStd => std_failures += 1, + } + } + } + + let mut builds = builds_grouped + .into_iter() + .map(|(k, (v1, v2))| (k, v1, v2)) + .collect::>(); + builds.sort_by_cached_key(|build| build.0.clone()); + + let core_broken = info + .iter() + .find(|info| info.mode == BuildMode::Core && info.is_broken) + .and_then(|info| info.broken_error.clone()); + let std_broken = info + .iter() + .find(|info| info.mode == BuildMode::MiriStd && info.is_broken) + .and_then(|info| info.broken_error.clone()); + + let page = NightlyPage { + nightly: query.nightly, + version: crate::VERSION, + builds, + std_failures, + core_failures, + core_broken, + std_broken, + }; + + Html(page.render().unwrap()).into_response() } - - let mut builds = builds_grouped - .into_iter() - .map(|(k, (v1, v2))| (k, v1, v2)) - .collect::>(); - builds.sort_by_cached_key(|build| build.0.clone()); - - let page = NightlyPage { - nightly: query.nightly, - version: crate::VERSION, - builds, - std_failures, - core_failures, - }; - - Html(page.render().unwrap()).into_response() - } + Err(err) => { + error!(?err, "Error loading target state"); + StatusCode::INTERNAL_SERVER_ERROR.into_response() + } + }, Err(err) => { error!(?err, "Error loading target state"); StatusCode::INTERNAL_SERVER_ERROR.into_response() diff --git a/templates/nightly.html b/templates/nightly.html index 2c23b4d..e4169e4 100644 --- a/templates/nightly.html +++ b/templates/nightly.html @@ -20,6 +20,25 @@ std builds (on targets that have it) but does not check whether codegen/linking works.

+ {% if let Some(core_broken) = core_broken %} +

+ ⚠️ The core build is broken in general for this nightly, so no data is available ⚠️ +

+ +
+      {{core_broken}}
+    
+ {% endif %} + {% if let Some(std_broken) = std_broken %} +

+ ⚠️ The std build is broken for this nightly, so no data is available ⚠️ +

+ +
+      {{std_broken}}
+    
+ {% endif %} +

core failures