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