diff --git a/.sqlx/query-5b6e45f40cd1fcbba53dcd54c2447f1bd43d2fd58c6b836643c04e93cb08f182.json b/.sqlx/query-20e978cf93a568649b762fb1f93c63ad7d64bfa34b5663ace054d30113ab833e.json similarity index 81% rename from .sqlx/query-5b6e45f40cd1fcbba53dcd54c2447f1bd43d2fd58c6b836643c04e93cb08f182.json rename to .sqlx/query-20e978cf93a568649b762fb1f93c63ad7d64bfa34b5663ace054d30113ab833e.json index 5678b194e..7e974f7ff 100644 --- a/.sqlx/query-5b6e45f40cd1fcbba53dcd54c2447f1bd43d2fd58c6b836643c04e93cb08f182.json +++ b/.sqlx/query-20e978cf93a568649b762fb1f93c63ad7d64bfa34b5663ace054d30113ab833e.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT\n builds.id as \"id: BuildId\",\n builds.rustc_version,\n builds.docsrs_version,\n builds.build_status as \"build_status: BuildStatus\",\n COALESCE(builds.build_finished, builds.build_started) as build_time,\n CASE\n WHEN builds.build_started IS NULL\n -- for old builds, `build_started` is empty.\n THEN NULL\n ELSE\n CASE\n -- for in-progress builds we show the duration until now\n WHEN builds.build_status = 'in_progress' THEN (CURRENT_TIMESTAMP - builds.build_started)\n -- there are broken builds where the status is `error`, and `build_finished` is NULL\n WHEN builds.build_finished IS NULL THEN NULL\n -- for finished builds we can show the full duration\n ELSE (builds.build_finished - builds.build_started)\n END\n END AS \"build_duration?: Duration\",\n builds.errors\n FROM builds\n INNER JOIN releases ON releases.id = builds.rid\n INNER JOIN crates ON releases.crate_id = crates.id\n WHERE\n crates.name = $1 AND\n releases.version = $2\n ORDER BY builds.id DESC", + "query": "SELECT\n builds.id as \"id: BuildId\",\n builds.rustc_version,\n builds.docsrs_version,\n builds.build_status as \"build_status: BuildStatus\",\n COALESCE(builds.build_finished, builds.build_started) as build_time,\n CASE\n WHEN builds.build_started IS NULL\n -- for old builds, `build_started` is empty.\n THEN NULL\n ELSE\n CASE\n -- for in-progress builds we show the duration until now\n WHEN builds.build_status = 'in_progress' THEN (CURRENT_TIMESTAMP - builds.build_started)\n -- there are broken builds where the status is `error`, and `build_finished` is NULL\n WHEN builds.build_finished IS NULL THEN NULL\n -- for finished builds we can show the full duration\n ELSE (builds.build_finished - builds.build_started)\n END\n END AS \"build_duration?: Duration\",\n builds.memory_peak,\n builds.errors\n FROM builds\n INNER JOIN releases ON releases.id = builds.rid\n INNER JOIN crates ON releases.crate_id = crates.id\n WHERE\n crates.name = $1 AND\n releases.version = $2\n ORDER BY builds.id DESC", "describe": { "columns": [ { @@ -46,6 +46,11 @@ }, { "ordinal": 6, + "name": "memory_peak", + "type_info": "Int8" + }, + { + "ordinal": 7, "name": "errors", "type_info": "Text" } @@ -63,8 +68,9 @@ false, null, null, + true, true ] }, - "hash": "5b6e45f40cd1fcbba53dcd54c2447f1bd43d2fd58c6b836643c04e93cb08f182" + "hash": "20e978cf93a568649b762fb1f93c63ad7d64bfa34b5663ace054d30113ab833e" } diff --git a/crates/bin/cratesfyi/.sqlx/query-5b6e45f40cd1fcbba53dcd54c2447f1bd43d2fd58c6b836643c04e93cb08f182.json b/crates/bin/cratesfyi/.sqlx/query-20e978cf93a568649b762fb1f93c63ad7d64bfa34b5663ace054d30113ab833e.json similarity index 81% rename from crates/bin/cratesfyi/.sqlx/query-5b6e45f40cd1fcbba53dcd54c2447f1bd43d2fd58c6b836643c04e93cb08f182.json rename to crates/bin/cratesfyi/.sqlx/query-20e978cf93a568649b762fb1f93c63ad7d64bfa34b5663ace054d30113ab833e.json index 5678b194e..7e974f7ff 100644 --- a/crates/bin/cratesfyi/.sqlx/query-5b6e45f40cd1fcbba53dcd54c2447f1bd43d2fd58c6b836643c04e93cb08f182.json +++ b/crates/bin/cratesfyi/.sqlx/query-20e978cf93a568649b762fb1f93c63ad7d64bfa34b5663ace054d30113ab833e.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT\n builds.id as \"id: BuildId\",\n builds.rustc_version,\n builds.docsrs_version,\n builds.build_status as \"build_status: BuildStatus\",\n COALESCE(builds.build_finished, builds.build_started) as build_time,\n CASE\n WHEN builds.build_started IS NULL\n -- for old builds, `build_started` is empty.\n THEN NULL\n ELSE\n CASE\n -- for in-progress builds we show the duration until now\n WHEN builds.build_status = 'in_progress' THEN (CURRENT_TIMESTAMP - builds.build_started)\n -- there are broken builds where the status is `error`, and `build_finished` is NULL\n WHEN builds.build_finished IS NULL THEN NULL\n -- for finished builds we can show the full duration\n ELSE (builds.build_finished - builds.build_started)\n END\n END AS \"build_duration?: Duration\",\n builds.errors\n FROM builds\n INNER JOIN releases ON releases.id = builds.rid\n INNER JOIN crates ON releases.crate_id = crates.id\n WHERE\n crates.name = $1 AND\n releases.version = $2\n ORDER BY builds.id DESC", + "query": "SELECT\n builds.id as \"id: BuildId\",\n builds.rustc_version,\n builds.docsrs_version,\n builds.build_status as \"build_status: BuildStatus\",\n COALESCE(builds.build_finished, builds.build_started) as build_time,\n CASE\n WHEN builds.build_started IS NULL\n -- for old builds, `build_started` is empty.\n THEN NULL\n ELSE\n CASE\n -- for in-progress builds we show the duration until now\n WHEN builds.build_status = 'in_progress' THEN (CURRENT_TIMESTAMP - builds.build_started)\n -- there are broken builds where the status is `error`, and `build_finished` is NULL\n WHEN builds.build_finished IS NULL THEN NULL\n -- for finished builds we can show the full duration\n ELSE (builds.build_finished - builds.build_started)\n END\n END AS \"build_duration?: Duration\",\n builds.memory_peak,\n builds.errors\n FROM builds\n INNER JOIN releases ON releases.id = builds.rid\n INNER JOIN crates ON releases.crate_id = crates.id\n WHERE\n crates.name = $1 AND\n releases.version = $2\n ORDER BY builds.id DESC", "describe": { "columns": [ { @@ -46,6 +46,11 @@ }, { "ordinal": 6, + "name": "memory_peak", + "type_info": "Int8" + }, + { + "ordinal": 7, "name": "errors", "type_info": "Text" } @@ -63,8 +68,9 @@ false, null, null, + true, true ] }, - "hash": "5b6e45f40cd1fcbba53dcd54c2447f1bd43d2fd58c6b836643c04e93cb08f182" + "hash": "20e978cf93a568649b762fb1f93c63ad7d64bfa34b5663ace054d30113ab833e" } diff --git a/crates/bin/docs_rs_web/.sqlx/query-5b6e45f40cd1fcbba53dcd54c2447f1bd43d2fd58c6b836643c04e93cb08f182.json b/crates/bin/docs_rs_web/.sqlx/query-20e978cf93a568649b762fb1f93c63ad7d64bfa34b5663ace054d30113ab833e.json similarity index 81% rename from crates/bin/docs_rs_web/.sqlx/query-5b6e45f40cd1fcbba53dcd54c2447f1bd43d2fd58c6b836643c04e93cb08f182.json rename to crates/bin/docs_rs_web/.sqlx/query-20e978cf93a568649b762fb1f93c63ad7d64bfa34b5663ace054d30113ab833e.json index 5678b194e..7e974f7ff 100644 --- a/crates/bin/docs_rs_web/.sqlx/query-5b6e45f40cd1fcbba53dcd54c2447f1bd43d2fd58c6b836643c04e93cb08f182.json +++ b/crates/bin/docs_rs_web/.sqlx/query-20e978cf93a568649b762fb1f93c63ad7d64bfa34b5663ace054d30113ab833e.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT\n builds.id as \"id: BuildId\",\n builds.rustc_version,\n builds.docsrs_version,\n builds.build_status as \"build_status: BuildStatus\",\n COALESCE(builds.build_finished, builds.build_started) as build_time,\n CASE\n WHEN builds.build_started IS NULL\n -- for old builds, `build_started` is empty.\n THEN NULL\n ELSE\n CASE\n -- for in-progress builds we show the duration until now\n WHEN builds.build_status = 'in_progress' THEN (CURRENT_TIMESTAMP - builds.build_started)\n -- there are broken builds where the status is `error`, and `build_finished` is NULL\n WHEN builds.build_finished IS NULL THEN NULL\n -- for finished builds we can show the full duration\n ELSE (builds.build_finished - builds.build_started)\n END\n END AS \"build_duration?: Duration\",\n builds.errors\n FROM builds\n INNER JOIN releases ON releases.id = builds.rid\n INNER JOIN crates ON releases.crate_id = crates.id\n WHERE\n crates.name = $1 AND\n releases.version = $2\n ORDER BY builds.id DESC", + "query": "SELECT\n builds.id as \"id: BuildId\",\n builds.rustc_version,\n builds.docsrs_version,\n builds.build_status as \"build_status: BuildStatus\",\n COALESCE(builds.build_finished, builds.build_started) as build_time,\n CASE\n WHEN builds.build_started IS NULL\n -- for old builds, `build_started` is empty.\n THEN NULL\n ELSE\n CASE\n -- for in-progress builds we show the duration until now\n WHEN builds.build_status = 'in_progress' THEN (CURRENT_TIMESTAMP - builds.build_started)\n -- there are broken builds where the status is `error`, and `build_finished` is NULL\n WHEN builds.build_finished IS NULL THEN NULL\n -- for finished builds we can show the full duration\n ELSE (builds.build_finished - builds.build_started)\n END\n END AS \"build_duration?: Duration\",\n builds.memory_peak,\n builds.errors\n FROM builds\n INNER JOIN releases ON releases.id = builds.rid\n INNER JOIN crates ON releases.crate_id = crates.id\n WHERE\n crates.name = $1 AND\n releases.version = $2\n ORDER BY builds.id DESC", "describe": { "columns": [ { @@ -46,6 +46,11 @@ }, { "ordinal": 6, + "name": "memory_peak", + "type_info": "Int8" + }, + { + "ordinal": 7, "name": "errors", "type_info": "Text" } @@ -63,8 +68,9 @@ false, null, null, + true, true ] }, - "hash": "5b6e45f40cd1fcbba53dcd54c2447f1bd43d2fd58c6b836643c04e93cb08f182" + "hash": "20e978cf93a568649b762fb1f93c63ad7d64bfa34b5663ace054d30113ab833e" } diff --git a/crates/bin/docs_rs_web/src/handlers/builds.rs b/crates/bin/docs_rs_web/src/handlers/builds.rs index b90ae9989..723dd2028 100644 --- a/crates/bin/docs_rs_web/src/handlers/builds.rs +++ b/crates/bin/docs_rs_web/src/handlers/builds.rs @@ -33,6 +33,7 @@ pub(crate) struct Build { build_status: BuildStatus, build_time: Option>, build_duration: Option, + memory_peak: Option, errors: Option, } @@ -210,6 +211,7 @@ async fn get_builds( ELSE (builds.build_finished - builds.build_started) END END AS "build_duration?: Duration", + builds.memory_peak, builds.errors FROM builds INNER JOIN releases ON releases.id = builds.rid @@ -275,7 +277,9 @@ mod tests { .collect(); assert_eq!(rows.len(), 1); - assert_eq!(rows[0].chars().filter(|&c| c == '—').count(), 3); + // Should have 4 mdashes: rustc_version, docsrs_version, build_time, build_duration + // (peak_memory_bytes shows "100 MB" from the dummy value) + assert_eq!(rows[0].chars().filter(|&c| c == '—').count(), 4); Ok(()) }); @@ -328,6 +332,49 @@ mod tests { }); } + #[test] + fn build_list_shows_memory() { + async_wrapper(|env| async move { + // Use a specific memory value: 256 MiB = 256 * 1024 * 1024 = 268435456 bytes + // filesizeformat uses decimal (1000-based), so this will display as ~268.43 MB + let test_memory_bytes: u64 = 256 * 1024 * 1024; + + env.fake_release() + .await + .name("foo") + .version("0.1.0") + .builds(vec![ + FakeBuild::default() + .rustc_version("rustc (blabla 2019-01-01)") + .docsrs_version("docs.rs 1.0.0") + .memory_peak(test_memory_bytes), + ]) + .create() + .await?; + + let response = env.web_app().await.get("/crate/foo/0.1.0/builds").await?; + let page = kuchikiki::parse_html().one(response.text().await?); + + // Check that memory column exists with tooltip + let memory_cells: Vec<_> = page + .select("div.memory[title='Peak memory usage']") + .unwrap() + .collect(); + assert_eq!(memory_cells.len(), 1, "Should have one memory cell"); + + // Check that the specific memory value is displayed + // 256 MiB (268435456 bytes) is formatted as "268.43 MB" by filesizeformat + let memory_text = memory_cells[0].text_contents().trim().to_string(); + assert!( + memory_text.contains("268") && memory_text.contains("MB"), + "Memory column should contain '268.xx MB' (from 256 MiB input), got: '{}'", + memory_text + ); + + Ok(()) + }); + } + #[tokio::test(flavor = "multi_thread")] async fn build_trigger_rebuild_missing_config() -> Result<()> { let env = TestEnvironment::builder() diff --git a/crates/bin/docs_rs_web/templates/crate/builds.html b/crates/bin/docs_rs_web/templates/crate/builds.html index 2ef062f90..bc877a19a 100644 --- a/crates/bin/docs_rs_web/templates/crate/builds.html +++ b/crates/bin/docs_rs_web/templates/crate/builds.html @@ -40,14 +40,14 @@ {{ crate::icons::IconX.render_solid(false, false, "") }} {%- endif -%} {#- -#} -
+
{%- if let Some(rustc_version) = build.rustc_version -%} {{ rustc_version }} {%- else -%} — {%- endif -%}
{#- -#} -
+
{%- if let Some(docsrs_version) = build.docsrs_version -%} {{ docsrs_version }} {%- else -%} @@ -61,13 +61,21 @@ — {%- endif -%}
-
+
{%- if let Some(build_duration) = build.build_duration -%} took {{ build_duration|format_duration }} {%- else -%} — {%- endif -%}
+
+ {%- if let Some(memory_peak) = build.memory_peak -%} + {{ crate::icons::IconMemory.render_solid(false, false, "") }} + {{ memory_peak|filesizeformat }} + {%- else -%} + — + {%- endif -%} +
{#- -#} {%- else -%} @@ -76,22 +84,25 @@
{{- crate::icons::IconGear.render_solid(false, true, "") -}}
{#- -#} -
{#- -#} +
{#- -#} in progress {#- -#}
-
+
{#- -#}
{#- -#} -
+
{%- if let Some(build_duration) = build.build_duration -%} since {{ build_duration|format_duration }} {%- else -%} — {%- endif -%}
+
+ — +
{%- endif -%} diff --git a/crates/bin/docs_rs_web/templates/style/style.scss b/crates/bin/docs_rs_web/templates/style/style.scss index bd8e6fe46..d7c3d59f3 100644 --- a/crates/bin/docs_rs_web/templates/style/style.scss +++ b/crates/bin/docs_rs_web/templates/style/style.scss @@ -387,7 +387,8 @@ div.recent-releases-container { } .date, - .duration { + .duration, + .memory { font-weight: normal; @media #{$media-sm} { @@ -395,6 +396,13 @@ div.recent-releases-container { } } + .memory { + @media #{$media-sm} { + padding-left: 1em; + white-space: nowrap; + } + } + div.pagination { text-align: center; margin: 1em; diff --git a/crates/lib/docs_rs_test_fakes/src/legacy.rs b/crates/lib/docs_rs_test_fakes/src/legacy.rs index 899ba4fd4..33ebe89e3 100644 --- a/crates/lib/docs_rs_test_fakes/src/legacy.rs +++ b/crates/lib/docs_rs_test_fakes/src/legacy.rs @@ -95,6 +95,7 @@ pub struct FakeBuild { rustc_version: String, docsrs_version: String, build_status: BuildStatus, + memory_peak: Option, } const DEFAULT_CONTENT: &[u8] = @@ -692,6 +693,13 @@ impl FakeBuild { } } + pub fn memory_peak(self, memory_peak: u64) -> Self { + Self { + memory_peak: Some(memory_peak), + ..self + } + } + async fn create( &self, conn: &mut sqlx::PgConnection, @@ -708,7 +716,7 @@ impl FakeBuild { &self.docsrs_version, self.build_status, Some(42), - Some(23), + self.memory_peak, None::<&SimpleBuildError>, ) .await?; @@ -752,6 +760,7 @@ impl Default for FakeBuild { rustc_version: "rustc 2.0.0-nightly (000000000 1970-01-01)".into(), docsrs_version: "docs.rs 1.0.0 (000000000 1970-01-01)".into(), build_status: BuildStatus::Success, + memory_peak: Some(23), } } }