diff --git a/src/test/fakes.rs b/src/test/fakes.rs index 13f5ede88..5e4f651a6 100644 --- a/src/test/fakes.rs +++ b/src/test/fakes.rs @@ -71,6 +71,11 @@ impl<'db> FakeRelease<'db> { self } + pub(crate) fn build_result_successful(mut self, new: bool) -> Self { + self.build_result.successful = new; + self + } + pub(crate) fn create(self) -> Result { let tempdir = tempdir::TempDir::new("docs.rs-fake")?; diff --git a/src/web/crate_details.rs b/src/web/crate_details.rs index 91016d0df..a214f50c3 100644 --- a/src/web/crate_details.rs +++ b/src/web/crate_details.rs @@ -31,6 +31,7 @@ pub struct CrateDetails { rustdoc: Option, // this is description_long in database release_time: time::Timespec, build_status: bool, + last_successful_build: Option, rustdoc_status: bool, repository_url: Option, homepage_url: Option, @@ -69,6 +70,7 @@ impl ToJson for CrateDetails { m.insert("release_time".to_string(), duration_to_str(self.release_time).to_json()); m.insert("build_status".to_string(), self.build_status.to_json()); + m.insert("last_successful_build".to_string(), self.last_successful_build.to_json()); m.insert("rustdoc_status".to_string(), self.rustdoc_status.to_json()); m.insert("repository_url".to_string(), self.repository_url.to_json()); m.insert("homepage_url".to_string(), self.homepage_url.to_json()); @@ -172,6 +174,7 @@ impl CrateDetails { rustdoc: rows.get(0).get(8), release_time: rows.get(0).get(9), build_status: rows.get(0).get(10), + last_successful_build: None, rustdoc_status: rows.get(0).get(11), repository_url: rows.get(0).get(12), homepage_url: rows.get(0).get(13), @@ -215,6 +218,23 @@ impl CrateDetails { crate_details.owners.push((row.get(0), row.get(1))); } + // retrieve last successful build if build failed + if !crate_details.build_status { + let rows = conn.query( + "SELECT version + FROM releases + INNER JOIN crates ON releases.crate_id = crates.id + WHERE build_status = true AND yanked = false AND crates.name = $1 + ORDER BY release_time desc + LIMIT 1;", + &[&name], + ).unwrap(); + + if rows.len() >= 1 { + crate_details.last_successful_build = Some(rows.get(0).get(0)); + } + } + Some(crate_details) } } @@ -252,3 +272,75 @@ pub fn crate_details_handler(req: &mut Request) -> IronResult { } } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::test::TestDatabase; + use failure::Error; + + fn create_release(db: &TestDatabase, package: &str, version: &str, successful: bool) -> Result { + db.fake_release() + .name(package) + .version(version) + .build_result_successful(successful) + .create() + } + + fn assert_last_successful_build_equals( + db: &TestDatabase, + package: &str, + version: &str, + expected_last_successful_build: Option<&str>, + ) -> Result<(), Error> { + + let details = CrateDetails::new(&db.conn(), package, version) + .ok_or(failure::err_msg("could not fetch crate details"))?; + + assert_eq!( + details.last_successful_build, + expected_last_successful_build.map(|s| s.to_string()), + ); + Ok(()) + } + + #[test] + fn test_last_successful_build_when_last_release_failed() { + crate::test::with_database(|db| { + create_release(&db, "foo", "0.0.1", true)?; + create_release(&db, "foo", "0.0.2", true)?; + create_release(&db, "foo", "0.0.3", false)?; + + assert_last_successful_build_equals(&db, "foo", "0.0.1", None)?; + assert_last_successful_build_equals(&db, "foo", "0.0.2", None)?; + assert_last_successful_build_equals(&db, "foo", "0.0.3", Some("0.0.2"))?; + Ok(()) + }); + } + + #[test] + fn test_last_successful_build_when_all_releases_failed() { + crate::test::with_database(|db| { + create_release(&db, "foo", "0.0.1", false)?; + create_release(&db, "foo", "0.0.2", false)?; + + assert_last_successful_build_equals(&db, "foo", "0.0.1", None)?; + assert_last_successful_build_equals(&db, "foo", "0.0.2", None)?; + Ok(()) + }); + } + + #[test] + fn test_last_successful_build_when_an_intermittent_release_failed() { + crate::test::with_database(|db| { + create_release(&db, "foo", "0.0.1", true)?; + create_release(&db, "foo", "0.0.2", false)?; + create_release(&db, "foo", "0.0.3", true)?; + + assert_last_successful_build_equals(&db, "foo", "0.0.1", None)?; + assert_last_successful_build_equals(&db, "foo", "0.0.2", Some("0.0.3"))?; + assert_last_successful_build_equals(&db, "foo", "0.0.3", None)?; + Ok(()) + }); + } +} diff --git a/templates/crate_details.hbs b/templates/crate_details.hbs index 309301e0d..f7f2060aa 100644 --- a/templates/crate_details.hbs +++ b/templates/crate_details.hbs @@ -63,6 +63,9 @@ {{else}} {{#unless build_status}}
docs.rs failed to build {{name}}-{{version}}
Please check the build logs and, if you believe this is docs.rs' fault, open an issue.
+ {{#if last_successful_build}} +
Visit the last successful build: {{name}}-{{last_successful_build}}
+ {{/if}} {{else}} {{#unless rustdoc_status}}
{{name}}-{{version}} doesn't have any documentation.
diff --git a/templates/style.scss b/templates/style.scss index b4382edcc..41580684a 100644 --- a/templates/style.scss +++ b/templates/style.scss @@ -706,10 +706,10 @@ div.cratesfyi-package-container-rustdoc { margin-bottom: 10px; } -div.warning { +div.info { font-family: $font-family-sans; border-radius: 4px; - background-color: lighten($color-type, 45%); + background-color: $color-background-code; padding: .4em 1em; text-align: center; margin-bottom: 10px; @@ -720,6 +720,11 @@ div.warning { } } +div.warning { + @extend div.info; + background-color: lighten($color-type, 45%); +} + div.search-page-search-form { padding: .4em 1em; text-align: center;