Skip to main content

sparrow/
release_prep.rs

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}