From 985dba6a02dd44508ee4898852e2fc14a490686a Mon Sep 17 00:00:00 2001 From: gl0w Date: Thu, 10 Sep 2026 08:54:47 +0100 Subject: [PATCH] m6/finish-line: repo-head spacing + footer provenance --- src/agents.rs | 6 +- src/build.rs | 53 ++++++++++--- src/commits.rs | 25 +++++- src/forge.rs | 103 +++++++++++++++++++------ src/inbox.rs | 18 ++++- src/layout.rs | 122 +++++++++++++++++++++++++++++ src/releases.rs | 41 ++++++++-- src/wiki.rs | 18 +++-- static/style.css | 15 +++- tests/code_space.rs | 184 ++++++++++++++++++++++++++++++++++++++++++++ tests/provenance.rs | 161 ++++++++++++++++++++++++++++++++++++++ 11 files changed, 687 insertions(+), 59 deletions(-) create mode 100644 tests/code_space.rs create mode 100644 tests/provenance.rs diff --git a/src/agents.rs b/src/agents.rs index f0f3646..d4e49f1 100644 --- a/src/agents.rs +++ b/src/agents.rs @@ -15,7 +15,7 @@ use std::path::Path; use crate::build::markdown_to_html; -use crate::layout::{esc, page_site}; +use crate::layout::{esc, page_site_with_provenance}; /// License grant quoted verbatim from `DRAFT.md` (receiver-friendly v1). /// Single source: [`crate::inbox::LICENSE_TEXT`] — the forms, this page, @@ -114,12 +114,14 @@ pub fn render( // source so the raw `agents.md` and rendered `agents.html` agree. The // markdown renderer escapes raw HTML again, so `&` etc. stay safe. let html_body = markdown_to_html(&md); - let html = page_site( + let html = page_site_with_provenance( &format!("agents — {site_name}"), nav, &html_body, site_name, description, + branch, + short_sha, ); std::fs::create_dir_all(out).map_err(|e| format!("hu13: create out dir: {e}"))?; std::fs::write(out.join("agents.html"), html) diff --git a/src/build.rs b/src/build.rs index 96c819f..5d176ba 100644 --- a/src/build.rs +++ b/src/build.rs @@ -941,8 +941,15 @@ pub fn run_build(repo: &Path, out: &Path, opts: &BuildOptions) -> Result Result Result Result Result\n"); - let index = crate::layout::page_site(&name, &nav, &body, &name, desc_opt); + let index = crate::layout::page_site_with_provenance( + &name, &nav, &body, &name, desc_opt, &branch, &short_sha, + ); std::fs::write(out.join("index.html"), index) .map_err(|e| format!("hu13: write index.html: {e}"))?; diff --git a/src/commits.rs b/src/commits.rs index 7075113..60566f2 100644 --- a/src/commits.rs +++ b/src/commits.rs @@ -9,7 +9,7 @@ use std::path::Path; use crate::gitx; -use crate::layout::{diff_html, esc, page_site}; +use crate::layout::{diff_html, esc, page_site_with_provenance}; /// Short display form of a full sha (first 7 hex chars, git-style). #[must_use] @@ -25,12 +25,15 @@ pub fn load(repo: &Path, branch: &str, max_log: usize) -> Result.html` per commit from an /// already-loaded log (see [`load`]). +#[allow(clippy::too_many_arguments)] pub fn render_loaded( repo: &Path, out: &Path, log: &[gitx::Commit], site_name: &str, nav: &str, + branch: &str, + snap_sha: &str, description: Option<&str>, ) -> Result { let mut body = String::from("

Commits

\n"); @@ -50,12 +53,14 @@ pub fn render_loaded( } body.push_str("\n\n"); } - let html = page_site( + let html = page_site_with_provenance( &format!("commits — {site_name}"), nav, &body, site_name, description, + branch, + snap_sha, ); std::fs::create_dir_all(out).map_err(|e| format!("hu13: create out dir: {e}"))?; std::fs::write(out.join("commits.html"), html) @@ -75,12 +80,14 @@ pub fn render_loaded( esc(&message), diff_html(&diff), ); - let html = page_site( + let html = page_site_with_provenance( &format!("commit {} — {site_name}", short_sha(&c.sha)), nav, &detail, site_name, description, + branch, + snap_sha, ); std::fs::write(out.join("commit").join(format!("{}.html", c.sha)), html) .map_err(|e| format!("hu13: write commit page {}: {e}", short_sha(&c.sha)))?; @@ -103,7 +110,17 @@ pub fn render( description: Option<&str>, ) -> Result { let log = load(repo, branch, max_log)?; - render_loaded(repo, out, &log, site_name, nav, description) + let snap_sha = gitx::rev_parse_short(repo, branch).unwrap_or_default(); + render_loaded( + repo, + out, + &log, + site_name, + nav, + branch, + &snap_sha, + description, + ) } #[cfg(test)] diff --git a/src/forge.rs b/src/forge.rs index b41ed7a..040b552 100644 --- a/src/forge.rs +++ b/src/forge.rs @@ -23,7 +23,7 @@ use std::path::Path; use serde::Deserialize; use crate::build::markdown_to_html; -use crate::layout::{diff_html, esc, page_site}; +use crate::layout::{diff_html, esc, page_site_with_provenance}; /// `.forge/` files larger than this are skipped (schema SEC-01: 256 KiB). pub const FORGE_MAX_BYTES: u64 = 256 * 1024; @@ -830,6 +830,8 @@ fn render_issue_page( issue: &Issue, site_name: &str, nav: &str, + branch: &str, + short_sha: &str, description: Option<&str>, ) -> String { let mut body = format!( @@ -879,16 +881,25 @@ fn render_issue_page( body.push_str("\n"); } body.push_str("

all issues

\n"); - page_site( + page_site_with_provenance( &format!("#{} {} — {site_name}", issue.number, issue.title), nav, &body, site_name, description, + branch, + short_sha, ) } -fn render_pr_page(pr: &Pr, site_name: &str, nav: &str, description: Option<&str>) -> String { +fn render_pr_page( + pr: &Pr, + site_name: &str, + nav: &str, + branch: &str, + short_sha: &str, + description: Option<&str>, +) -> String { let mut body = format!( "

#{n} {title}

\n", n = pr.number, @@ -963,12 +974,14 @@ fn render_pr_page(pr: &Pr, site_name: &str, nav: &str, description: Option<&str> body.push_str("\n"); } body.push_str("

all pulls

\n"); - page_site( + page_site_with_provenance( &format!("#{} {} — {site_name}", pr.number, pr.title), nav, &body, site_name, description, + branch, + short_sha, ) } @@ -976,6 +989,8 @@ fn render_issues_index( data: &ForgeData, site_name: &str, nav: &str, + branch: &str, + short_sha: &str, description: Option<&str>, ) -> Result { let mut body = @@ -1019,12 +1034,14 @@ fn render_issues_index( } body.push_str("\n"); } - Ok(page_site( + Ok(page_site_with_provenance( &format!("issues — {site_name}"), nav, &body, site_name, description, + branch, + short_sha, )) } @@ -1032,6 +1049,8 @@ fn render_pulls_index( data: &ForgeData, site_name: &str, nav: &str, + branch: &str, + short_sha: &str, description: Option<&str>, ) -> String { let prs = &data.prs; @@ -1059,12 +1078,14 @@ fn render_pulls_index( "

{n} closed

\n", n = data.closed_pr_count(), )); - page_site( + page_site_with_provenance( &format!("pulls — {site_name}"), nav, &body, site_name, description, + branch, + short_sha, ) } @@ -1072,6 +1093,8 @@ fn render_tasks_index( tasks: &[TaskItem], site_name: &str, nav: &str, + branch: &str, + short_sha: &str, description: Option<&str>, ) -> String { let mut body = String::from("

Tasks

\n"); @@ -1107,16 +1130,25 @@ fn render_tasks_index( } } } - page_site( + page_site_with_provenance( &format!("tasks — {site_name}"), nav, &body, site_name, description, + branch, + short_sha, ) } -fn render_ci_index(ci: &CiInfo, site_name: &str, nav: &str, description: Option<&str>) -> String { +fn render_ci_index( + ci: &CiInfo, + site_name: &str, + nav: &str, + branch: &str, + short_sha: &str, + description: Option<&str>, +) -> String { let mut body = String::from("

CI

\n"); body.push_str(&meta_row("runner:", &esc(&ci.runner))); if ci.steps.is_empty() { @@ -1170,12 +1202,14 @@ fn render_ci_index(ci: &CiInfo, site_name: &str, nav: &str, description: Option< } None => body.push_str("

No CI runs yet.

\n"), } - page_site( + page_site_with_provenance( &format!("ci — {site_name}"), nav, &body, site_name, description, + branch, + short_sha, ) } @@ -1193,43 +1227,45 @@ pub fn render_loaded( out: &Path, site_name: &str, nav: &str, + branch: &str, + short_sha: &str, description: Option<&str>, ) -> Result<(), String> { write_page( out, "issues/index.html", - &render_issues_index(data, site_name, nav, description)?, + &render_issues_index(data, site_name, nav, branch, short_sha, description)?, )?; for issue in &data.issues { write_page( out, &format!("issues/{}.html", issue.id), - &render_issue_page(issue, site_name, nav, description), + &render_issue_page(issue, site_name, nav, branch, short_sha, description), )?; } write_page( out, "pulls/index.html", - &render_pulls_index(data, site_name, nav, description), + &render_pulls_index(data, site_name, nav, branch, short_sha, description), )?; for pr in &data.prs { write_page( out, &format!("pulls/{}.html", pr.id), - &render_pr_page(pr, site_name, nav, description), + &render_pr_page(pr, site_name, nav, branch, short_sha, description), )?; } write_page( out, "tasks/index.html", - &render_tasks_index(&data.tasks, site_name, nav, description), + &render_tasks_index(&data.tasks, site_name, nav, branch, short_sha, description), )?; write_page( out, "ci/index.html", - &render_ci_index(&data.ci, site_name, nav, description), + &render_ci_index(&data.ci, site_name, nav, branch, short_sha, description), )?; Ok(()) @@ -1360,7 +1396,8 @@ mod tests { assert_eq!(data.prs[0].reviews[0].state.as_deref(), Some("approved")); let out = mktemp("pr-out"); - render_loaded(&load(&repo), &out, "test", "