1use anyhow::Context;
2use std::path::{Path, PathBuf};
3
4pub fn prepare_release_docs(root: &Path) -> anyhow::Result<Vec<PathBuf>> {
5 let launch_dir = root.join("docs").join("launch");
6 std::fs::create_dir_all(&launch_dir)?;
7 let ring_release = launch_dir.join("ring-release.md");
8 let migration = root.join("docs").join("migration-v0.9.2.md");
9 std::fs::create_dir_all(migration.parent().unwrap_or(root))?;
10
11 let perf = read_optional(root.join("artifacts").join("perf-report.md"));
12 let changelog = read_optional(root.join("CHANGELOG.md"));
13 std::fs::write(&ring_release, ring_release_markdown(&perf, &changelog))
14 .with_context(|| format!("failed to write {}", ring_release.display()))?;
15 std::fs::write(&migration, migration_markdown())
16 .with_context(|| format!("failed to write {}", migration.display()))?;
17
18 Ok(vec![ring_release, migration])
19}
20
21pub fn planned_release_doc_paths(root: &Path) -> Vec<PathBuf> {
22 vec![
23 root.join("docs").join("launch").join("ring-release.md"),
24 root.join("docs").join("migration-v0.9.2.md"),
25 ]
26}
27
28fn ring_release_markdown(perf: &str, changelog: &str) -> String {
29 format!(
30 "# Sparrow v0.9.2 \"The Ring\"\n\n\
31 ## Demo 30s\n\n\
32 1. Run `sparrow audit` to map the repo and catch stubs.\n\
33 2. Run `sparrow test` to detect and execute the project runner.\n\
34 3. Open `sparrow console --fast` for a lean cockpit startup.\n\n\
35 ## Measured Before/After\n\n\
36 The release notes use only measured values from `artifacts/perf-report.md`.\n\n\
37 ```text\n{}\n```\n\n\
38 ## What Changed\n\n\
39 ```text\n{}\n```\n\n\
40 ## Install\n\n\
41 ```powershell\ncargo install sparrow-cli\n```\n",
42 excerpt(perf, 5000),
43 excerpt(changelog, 4000)
44 )
45}
46
47fn migration_markdown() -> &'static str {
48 "# Migration Notes: Sparrow v0.9.2\n\n\
49 No breaking config migration is expected for v0.9.2.\n\n\
50 - Existing `config.toml` files continue to load.\n\
51 - Existing transcripts remain additive-serde compatible.\n\
52 - `sparrow console` keeps normal browser auto-open behavior.\n\
53 - `sparrow console --fast` is new and intentionally does not auto-open a browser.\n"
54}
55
56fn read_optional(path: PathBuf) -> String {
57 std::fs::read_to_string(path).unwrap_or_else(|_| "(not available yet)".into())
58}
59
60fn excerpt(input: &str, max_chars: usize) -> String {
61 input.chars().take(max_chars).collect()
62}
63
64#[cfg(test)]
65mod tests {
66 use super::*;
67
68 #[test]
69 fn release_prep_writes_expected_docs() {
70 let temp = tempfile::tempdir().unwrap();
71 std::fs::create_dir_all(temp.path().join("artifacts")).unwrap();
72 std::fs::write(temp.path().join("artifacts/perf-report.md"), "perf values").unwrap();
73 std::fs::write(temp.path().join("CHANGELOG.md"), "change values").unwrap();
74 let files = prepare_release_docs(temp.path()).unwrap();
75 assert_eq!(files.len(), 2);
76 assert!(files[0].ends_with("ring-release.md"));
77 assert!(
78 std::fs::read_to_string(&files[0])
79 .unwrap()
80 .contains("perf values")
81 );
82 assert!(
83 std::fs::read_to_string(&files[1])
84 .unwrap()
85 .contains("No breaking config migration")
86 );
87 }
88}