Skip to content

Commit fee5724

Browse files
committed
feat(cli): add cratesfyi command to queue rebuilds for specific broken nightly dates
1 parent ef9f424 commit fee5724

File tree

3 files changed

+259
-2
lines changed

3 files changed

+259
-2
lines changed

src/bin/cratesfyi.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
use anyhow::{Context as _, Result, anyhow};
2+
use chrono::NaiveDate;
23
use clap::{Parser, Subcommand, ValueEnum};
34
use docs_rs::{
45
Config, Context, Index, PackageKind, RustwideBuilder,
56
db::{self, CrateId, Overrides, add_path_into_database, types::version::Version},
6-
start_background_metrics_webserver, start_web_server,
7+
queue_rebuilds_faulty_rustdoc, start_background_metrics_webserver, start_web_server,
78
utils::{
89
ConfigName, daemon::start_background_service_metric_collector, get_config,
910
get_crate_pattern_and_priority, list_crate_priorities, queue_builder,
@@ -274,6 +275,17 @@ enum QueueSubcommand {
274275
#[arg(long, conflicts_with("reference"))]
275276
head: bool,
276277
},
278+
279+
/// Queue rebuilds for broken nightly versions of rustdoc
280+
RebuildBrokenNightly {
281+
/// Start date of nightly builds to rebuild (inclusive)
282+
#[arg(name = "START", short = 's', long = "start")]
283+
start_nightly_date: NaiveDate,
284+
285+
/// End date of nightly builds to rebuild (exclusive, optional)
286+
#[arg(name = "END", short = 'e', long = "end")]
287+
end_nightly_date: Option<NaiveDate>,
288+
},
277289
}
278290

279291
impl QueueSubcommand {
@@ -316,6 +328,15 @@ impl QueueSubcommand {
316328
}
317329

318330
Self::DefaultPriority { subcommand } => subcommand.handle_args(ctx)?,
331+
332+
Self::RebuildBrokenNightly { start_nightly_date, end_nightly_date } => {
333+
ctx.runtime.block_on(async move {
334+
let mut conn = ctx.pool.get_async().await?;
335+
let queued_rebuilds_amount = queue_rebuilds_faulty_rustdoc(&mut conn, &ctx.async_build_queue, &start_nightly_date, &end_nightly_date).await?;
336+
println!("Queued {queued_rebuilds_amount} rebuilds for broken nightly versions of rustdoc");
337+
Ok::<(), anyhow::Error>(())
338+
})?
339+
}
319340
}
320341
Ok(())
321342
}

src/build_queue.rs

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use crate::{
1313
utils::{ConfigName, get_config, get_crate_priority, report_error, retry, set_config},
1414
};
1515
use anyhow::Context as _;
16+
use chrono::NaiveDate;
1617
use fn_error_context::context;
1718
use futures_util::{StreamExt, stream::TryStreamExt};
1819
use opentelemetry::metrics::Counter;
@@ -44,6 +45,8 @@ impl BuildQueueMetrics {
4445
/// collapsed in the UI.
4546
/// For normal build priorities we use smaller values.
4647
pub(crate) const REBUILD_PRIORITY: i32 = 20;
48+
// TODO what value should we use here?
49+
pub(crate) const BROKEN_RUSTDOC_REBUILD_PRIORITY: i32 = 30;
4750

4851
#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize)]
4952
pub(crate) struct QueuedCrate {
@@ -775,9 +778,75 @@ pub async fn queue_rebuilds(
775778
Ok(())
776779
}
777780

781+
/// Queue rebuilds for failed crates due to a faulty version of rustdoc
782+
///
783+
/// It is assumed that the version of rustdoc matches the one of rustc, which is persisted in the DB.
784+
/// The priority of the resulting rebuild requests will be lower than previously failed builds.
785+
/// If a crate is already queued to be rebuilt, it will not be requeued.
786+
/// Start date is inclusive, end date is exclusive.
787+
#[instrument(skip_all)]
788+
pub async fn queue_rebuilds_faulty_rustdoc(
789+
conn: &mut sqlx::PgConnection,
790+
build_queue: &AsyncBuildQueue,
791+
start_nightly_date: &NaiveDate,
792+
end_nightly_date: &Option<NaiveDate>,
793+
) -> Result<i32> {
794+
let end_nightly_date =
795+
end_nightly_date.unwrap_or_else(|| start_nightly_date.succ_opt().unwrap());
796+
let mut results = sqlx::query!(
797+
r#"
798+
SELECT c.name,
799+
r.version AS "version: Version"
800+
FROM crates AS c
801+
JOIN releases AS r
802+
ON c.latest_version_id = r.id
803+
AND r.rustdoc_status = TRUE
804+
JOIN LATERAL (
805+
SELECT b.id,
806+
b.build_status,
807+
b.rustc_nightly_date,
808+
COALESCE(b.build_finished, b.build_started) AS last_build_attempt
809+
FROM builds AS b
810+
WHERE b.rid = r.id
811+
ORDER BY last_build_attempt DESC
812+
LIMIT 1
813+
) AS b ON b.build_status = 'failure' AND b.rustc_nightly_date >= $1 AND b.rustc_nightly_date < $2
814+
815+
"#, start_nightly_date, end_nightly_date
816+
)
817+
.fetch(&mut *conn);
818+
819+
let mut results_count = 0;
820+
while let Some(row) = results.next().await {
821+
let row = row?;
822+
823+
if !build_queue
824+
.has_build_queued(&row.name, &row.version)
825+
.await?
826+
{
827+
results_count += 1;
828+
info!(
829+
"queueing rebuild for {} {} (priority {})...",
830+
&row.name, &row.version, BROKEN_RUSTDOC_REBUILD_PRIORITY
831+
);
832+
build_queue
833+
.add_crate(
834+
&row.name,
835+
&row.version,
836+
BROKEN_RUSTDOC_REBUILD_PRIORITY,
837+
None,
838+
)
839+
.await?;
840+
}
841+
}
842+
843+
Ok(results_count)
844+
}
845+
778846
#[cfg(test)]
779847
mod tests {
780848
use super::*;
849+
use crate::db::types::BuildStatus;
781850
use crate::test::{FakeBuild, TestEnvironment, V1, V2};
782851
use chrono::Utc;
783852

@@ -817,6 +886,171 @@ mod tests {
817886
Ok(())
818887
}
819888

889+
/// Verifies whether a rebuild is queued for a crate that previously failed with a nightly version of rustdoc.
890+
#[tokio::test(flavor = "multi_thread")]
891+
async fn test_rebuild_broken_rustdoc_specific_date_simple() -> Result<()> {
892+
let env = TestEnvironment::with_config(
893+
TestEnvironment::base_config()
894+
.max_queued_rebuilds(Some(100))
895+
.build()?,
896+
)
897+
.await?;
898+
899+
for i in 1..5 {
900+
let nightly_date = NaiveDate::from_ymd_opt(2020, 10, i).unwrap();
901+
env.fake_release()
902+
.await
903+
.name(&format!("foo{}", i))
904+
.version(V1)
905+
.builds(vec![
906+
FakeBuild::default()
907+
.rustc_version(
908+
format!(
909+
"rustc 1.84.0-nightly (e7c0d2750 {})",
910+
nightly_date.format("%Y-%m-%d")
911+
)
912+
.as_str(),
913+
)
914+
.build_status(BuildStatus::Failure),
915+
])
916+
.create()
917+
.await?;
918+
}
919+
920+
let build_queue = env.async_build_queue();
921+
assert!(build_queue.queued_crates().await?.is_empty());
922+
923+
let mut conn = env.async_db().async_conn().await;
924+
queue_rebuilds_faulty_rustdoc(
925+
&mut conn,
926+
build_queue,
927+
&NaiveDate::from_ymd_opt(2020, 10, 3).unwrap(),
928+
&None,
929+
)
930+
.await?;
931+
932+
let queue = build_queue.queued_crates().await?;
933+
assert_eq!(queue.len(), 1);
934+
assert_eq!(queue[0].name, "foo3");
935+
assert_eq!(queue[0].version, V1);
936+
assert_eq!(queue[0].priority, BROKEN_RUSTDOC_REBUILD_PRIORITY);
937+
938+
Ok(())
939+
}
940+
941+
/// Verified whether a rebuild is NOT queued since the latest build for the specific crate is marked as successful.
942+
#[tokio::test(flavor = "multi_thread")]
943+
async fn test_rebuild_broken_rustdoc_specific_date_skipped() -> Result<()> {
944+
let env = TestEnvironment::with_config(
945+
TestEnvironment::base_config()
946+
.max_queued_rebuilds(Some(100))
947+
.build()?,
948+
)
949+
.await?;
950+
951+
env.fake_release()
952+
.await
953+
.name("foo")
954+
.version(V1)
955+
.builds(vec![
956+
FakeBuild::default()
957+
.rustc_version(
958+
format!(
959+
"rustc 1.84.0-nightly (e7c0d2750 {})",
960+
NaiveDate::from_ymd_opt(2020, 10, 1)
961+
.unwrap()
962+
.format("%Y-%m-%d")
963+
)
964+
.as_str(),
965+
)
966+
.build_status(BuildStatus::Failure),
967+
FakeBuild::default()
968+
.rustc_version(
969+
format!(
970+
"rustc 1.84.0-nightly (e7c0d2750 {})",
971+
NaiveDate::from_ymd_opt(2020, 10, 1)
972+
.unwrap()
973+
.format("%Y-%m-%d")
974+
)
975+
.as_str(),
976+
)
977+
.build_status(BuildStatus::Success),
978+
])
979+
.create()
980+
.await?;
981+
982+
let build_queue = env.async_build_queue();
983+
assert!(build_queue.queued_crates().await?.is_empty());
984+
985+
let mut conn = env.async_db().async_conn().await;
986+
queue_rebuilds_faulty_rustdoc(
987+
&mut conn,
988+
build_queue,
989+
&NaiveDate::from_ymd_opt(2020, 10, 1).unwrap(),
990+
&None,
991+
)
992+
.await?;
993+
994+
let queue = build_queue.queued_crates().await?;
995+
assert_eq!(queue.len(), 0);
996+
997+
Ok(())
998+
}
999+
1000+
#[tokio::test(flavor = "multi_thread")]
1001+
async fn test_rebuild_broken_rustdoc_date_range() -> Result<()> {
1002+
let env = TestEnvironment::with_config(
1003+
TestEnvironment::base_config()
1004+
.max_queued_rebuilds(Some(100))
1005+
.build()?,
1006+
)
1007+
.await?;
1008+
1009+
for i in 1..6 {
1010+
let nightly_date = NaiveDate::from_ymd_opt(2020, 10, i).unwrap();
1011+
env.fake_release()
1012+
.await
1013+
.name(&format!("foo{}", i))
1014+
.version(V1)
1015+
.builds(vec![
1016+
FakeBuild::default()
1017+
.rustc_version(
1018+
format!(
1019+
"rustc 1.84.0-nightly (e7c0d2750 {})",
1020+
nightly_date.format("%Y-%m-%d")
1021+
)
1022+
.as_str(),
1023+
)
1024+
.build_status(BuildStatus::Failure),
1025+
])
1026+
.create()
1027+
.await?;
1028+
}
1029+
1030+
let build_queue = env.async_build_queue();
1031+
assert!(build_queue.queued_crates().await?.is_empty());
1032+
1033+
let mut conn = env.async_db().async_conn().await;
1034+
queue_rebuilds_faulty_rustdoc(
1035+
&mut conn,
1036+
build_queue,
1037+
&NaiveDate::from_ymd_opt(2020, 10, 3).unwrap(),
1038+
&Some(NaiveDate::from_ymd_opt(2020, 10, 5)).unwrap(),
1039+
)
1040+
.await?;
1041+
1042+
let queue = build_queue.queued_crates().await?;
1043+
assert_eq!(queue.len(), 2);
1044+
assert_eq!(queue[0].name, "foo3");
1045+
assert_eq!(queue[0].version, V1);
1046+
assert_eq!(queue[0].priority, BROKEN_RUSTDOC_REBUILD_PRIORITY);
1047+
assert_eq!(queue[1].name, "foo4");
1048+
assert_eq!(queue[1].version, V1);
1049+
assert_eq!(queue[1].priority, BROKEN_RUSTDOC_REBUILD_PRIORITY);
1050+
1051+
Ok(())
1052+
}
1053+
8201054
#[tokio::test(flavor = "multi_thread")]
8211055
async fn test_still_rebuild_when_full_with_failed() -> Result<()> {
8221056
let env = TestEnvironment::with_config(

src/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
//! documentation of crates for the Rust Programming Language.
33
#![allow(clippy::cognitive_complexity)]
44

5-
pub use self::build_queue::{AsyncBuildQueue, BuildQueue, queue_rebuilds};
5+
pub use self::build_queue::{
6+
AsyncBuildQueue, BuildQueue, queue_rebuilds, queue_rebuilds_faulty_rustdoc,
7+
};
68
pub use self::config::Config;
79
pub use self::context::Context;
810
pub use self::docbuilder::PackageKind;

0 commit comments

Comments
 (0)