Skip to content

Commit 567dd80

Browse files
author
Pika Forge
committed
Merge branch 'pika-git-ci-polish-1' into master
2 parents a8183d6 + 49db522 commit 567dd80

2 files changed

Lines changed: 102 additions & 16 deletions

File tree

crates/pika-news/src/web.rs

Lines changed: 99 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -997,7 +997,7 @@ fn branch_page_notices(state: &AppState) -> Vec<PageNoticeView> {
997997
&mut notices,
998998
&mut seen,
999999
"warning",
1000-
"The summary generator is unhealthy. New tutorials across the forge may be delayed until it recovers.",
1000+
"Forge health warning: the tutorial generator worker is unhealthy. New tutorials on any branch may be delayed until it recovers; this forge-wide warning does not mean this branch's last tutorial generation attempt failed.",
10011001
);
10021002
}
10031003
if health.ci.state == "error" {
@@ -2547,15 +2547,10 @@ fn ci_timing_summary(
25472547
let started_at = started_at.and_then(parse_ci_timestamp);
25482548
let finished_at = finished_at.and_then(parse_ci_timestamp);
25492549

2550-
let queued = match (created_at, started_at) {
2551-
(Some(created_at), Some(started_at)) => {
2552-
compact_duration_part("queued", started_at.signed_duration_since(created_at))
2553-
}
2554-
(Some(created_at), None) if finished_at.is_none() => {
2555-
compact_duration_part("queued", now.signed_duration_since(created_at))
2556-
}
2557-
_ => None,
2558-
};
2550+
let queued = created_at.and_then(|created_at| {
2551+
let queued_end = started_at.or(finished_at).unwrap_or(now);
2552+
compact_duration_part("queued", queued_end.signed_duration_since(created_at))
2553+
});
25592554

25602555
let ran = started_at.and_then(|started_at| {
25612556
let end = finished_at.unwrap_or(now);
@@ -5214,6 +5209,16 @@ mod tests {
52145209
.expect("finished summary"),
52155210
"queued 14s · ran 31s"
52165211
);
5212+
assert_eq!(
5213+
super::ci_timing_summary(
5214+
"2026-03-24T12:00:00Z",
5215+
None,
5216+
Some("2026-03-24T12:00:14Z"),
5217+
now,
5218+
)
5219+
.expect("finished-while-never-started summary"),
5220+
"queued 14s"
5221+
);
52175222
}
52185223

52195224
#[test]
@@ -7284,6 +7289,80 @@ paths = ["README.md", "feature.txt", "ci/forge-lanes.toml"]
72847289
assert!(rendered.contains("target apple-host unhealthy"));
72857290
}
72867291

7292+
#[test]
7293+
fn branch_ci_rendering_shows_queued_duration_for_terminal_lane_without_start_time() {
7294+
let dir = tempfile::tempdir().expect("create temp dir");
7295+
let db_path = dir.path().join("pika-news.db");
7296+
let store = Store::open(&db_path).expect("open store");
7297+
let branch = store
7298+
.upsert_branch_record(&branch_upsert_input(
7299+
"feature/queued-terminal",
7300+
"head-queued",
7301+
))
7302+
.expect("insert branch");
7303+
store
7304+
.queue_branch_ci_run_for_head(
7305+
branch.branch_id,
7306+
"head-queued",
7307+
&[crate::ci_manifest::ForgeLane {
7308+
id: "pika".to_string(),
7309+
title: "pre-merge-pika".to_string(),
7310+
entrypoint: "just checks::pre-merge-pika".to_string(),
7311+
command: vec!["just".to_string(), "checks::pre-merge-pika".to_string()],
7312+
paths: vec![],
7313+
concurrency_group: None,
7314+
staged_linux_target: None,
7315+
}],
7316+
)
7317+
.expect("queue ci");
7318+
store
7319+
.with_connection(|conn| {
7320+
conn.execute(
7321+
"UPDATE branch_ci_runs
7322+
SET status = 'failed',
7323+
created_at = '2026-03-24T12:00:00Z',
7324+
started_at = NULL,
7325+
finished_at = '2026-03-24T12:00:14Z'
7326+
WHERE branch_id = ?1",
7327+
rusqlite::params![branch.branch_id],
7328+
)?;
7329+
conn.execute(
7330+
"UPDATE branch_ci_run_lanes
7331+
SET status = 'failed',
7332+
created_at = '2026-03-24T12:00:00Z',
7333+
started_at = NULL,
7334+
finished_at = '2026-03-24T12:00:14Z',
7335+
log_text = 'boom'
7336+
WHERE lane_id = 'pika'",
7337+
[],
7338+
)?;
7339+
Ok::<(), anyhow::Error>(())
7340+
})
7341+
.expect("mark queued lane terminal");
7342+
7343+
let detail = store
7344+
.get_branch_detail(branch.branch_id)
7345+
.expect("branch detail")
7346+
.expect("detail");
7347+
let ci_runs = store
7348+
.list_branch_ci_runs(branch.branch_id, 8)
7349+
.expect("branch ci runs");
7350+
let now = Utc
7351+
.with_ymd_and_hms(2026, 3, 24, 12, 0, 45)
7352+
.single()
7353+
.expect("valid timestamp");
7354+
7355+
let summary_html =
7356+
super::render_branch_ci_summary_html_at(&detail, &ci_runs, &[], false, now)
7357+
.expect("render branch ci summary html");
7358+
let live_html = super::render_branch_ci_live_html_at(&detail, &ci_runs, &[], now)
7359+
.expect("render branch ci live html");
7360+
7361+
assert!(summary_html.contains("queued 14s"));
7362+
assert!(live_html.contains("queued 14s"));
7363+
assert!(!live_html.contains("queued 14s · ran"));
7364+
}
7365+
72877366
#[test]
72887367
fn review_mode_ci_links_preserve_inbox_context() {
72897368
let dir = tempfile::tempdir().expect("create temp dir");
@@ -7376,16 +7455,22 @@ paths = ["README.md", "feature.txt", "ci/forge-lanes.toml"]
73767455
false,
73777456
vec![PageNoticeView {
73787457
tone: "warning".to_string(),
7379-
message: "The summary generator is unhealthy. New tutorials across the forge may be delayed until it recovers.".to_string(),
7458+
message: "Forge health warning: the tutorial generator worker is unhealthy. New tutorials on any branch may be delayed until it recovers; this forge-wide warning does not mean this branch's last tutorial generation attempt failed.".to_string(),
73807459
}],
73817460
)
73827461
.expect("render detail template")
73837462
.render()
73847463
.expect("render detail html");
73857464

7386-
assert!(rendered.contains("The summary generator is unhealthy."));
7387-
assert!(rendered.contains("Branch Tutorial Generation Failed"));
7388-
assert!(rendered.contains("This branch tutorial is unavailable because generation failed."));
7465+
assert!(
7466+
rendered.contains("Forge health warning: the tutorial generator worker is unhealthy.")
7467+
);
7468+
assert!(rendered.contains("forge-wide warning does not mean"));
7469+
assert!(rendered.contains("Branch-Specific Tutorial Generation Failed"));
7470+
assert!(rendered.contains("This failure is specific to the current branch head."));
7471+
assert!(rendered.contains(
7472+
"This branch tutorial is unavailable because generation for this branch head failed."
7473+
));
73897474
}
73907475

73917476
#[test]

crates/pika-news/templates/detail.html

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,8 @@ <h2>Changed Files</h2>
312312
{% match error_message %}
313313
{% when Some with (msg) %}
314314
<section class="panel">
315-
<h2>Branch Tutorial Generation Failed</h2>
315+
<h2>Branch-Specific Tutorial Generation Failed</h2>
316+
<p>This failure is specific to the current branch head.</p>
316317
<p>{{ msg }}</p>
317318
</section>
318319
{% when None %}{% endmatch %}
@@ -355,7 +356,7 @@ <h3>{{ step.title }}</h3>
355356
<section class="panel">
356357
<h2>Summary</h2>
357358
{% if tutorial_status == "failed" %}
358-
<p>This branch tutorial is unavailable because generation failed. Check the branch tutorial error above for details.</p>
359+
<p>This branch tutorial is unavailable because generation for this branch head failed. That is separate from any forge-wide generator worker warning.</p>
359360
{% else %}
360361
<p>Summary is not ready yet. Refresh shortly.</p>
361362
{% endif %}

0 commit comments

Comments
 (0)