Skip to main content

release_kit/
embedded.rs

1//! The compile-time payload: everything the binary serves or lands.
2//!
3//! `include_dir!` embeds each authored root at compile time, so the binary
4//! and the canon it carries cannot drift. Which roots exist is declared
5//! once, in [`crate::payload_roots`], read here, by `build.rs` for change
6//! tracking, and by the packaging test; a test below holds this module to
7//! that inventory.
8
9use include_dir::{Dir, include_dir};
10
11pub use crate::payload_roots::PAYLOAD_ROOTS;
12
13/// The technology-agnostic method chapters.
14pub static METHOD: Dir<'static> = include_dir!("$CARGO_MANIFEST_DIR/method");
15
16/// The per-technology bindings.
17pub static BINDINGS: Dir<'static> = include_dir!("$CARGO_MANIFEST_DIR/bindings");
18
19/// The human-facing runbooks `rk guide` renders.
20pub static RUNBOOKS: Dir<'static> = include_dir!("$CARGO_MANIFEST_DIR/runbooks");
21
22/// The per-forge documents answering the fifth axis.
23pub static FORGES: Dir<'static> = include_dir!("$CARGO_MANIFEST_DIR/forges");
24
25/// The setup scripts, one subtree per forge, executed by `rk setup` and
26/// landed nowhere.
27pub static SETUP: Dir<'static> = include_dir!("$CARGO_MANIFEST_DIR/setup");
28
29/// The deterministic files `rk init` lands, one subtree per technology,
30/// laid out exactly as they land in a target repository.
31pub static SNIPPETS: Dir<'static> = include_dir!("$CARGO_MANIFEST_DIR/snippets");
32
33/// The agent skills, one directory per skill.
34pub static SKILLS: Dir<'static> = include_dir!("$CARGO_MANIFEST_DIR/skills");
35
36/// The artifacts every skill shares, installed once outside the skill roots.
37pub static SKILL_SHARED: Dir<'static> = include_dir!("$CARGO_MANIFEST_DIR/skill-shared");
38
39/// The pinned-tool registry.
40pub static VERSIONS: &str = include_str!("../versions.toml");
41
42/// The root license statement naming both halves.
43pub static LICENSE: &str = include_str!("../LICENSE");
44
45/// The MIT text covering the distribution.
46pub static LICENSE_MIT: &str = include_str!("../LICENSE-MIT");
47
48/// The CC BY 4.0 text covering the method.
49pub static LICENSE_CC_BY: &str = include_str!("../LICENSE-CC-BY-4.0");
50
51/// The sentinel marker a landed file may carry; `rk init --apply` reports
52/// every line holding one so nothing lands half-configured silently.
53pub const SENTINEL: &str = "TODO(release-kit)";
54
55/// Collect every file under `dir`, depth-first, as `(path, contents)` with
56/// the path relative to the embedded root, sorted by path.
57pub(crate) fn walk<'a>(dir: &Dir<'a>) -> Vec<(String, &'a [u8])> {
58    let mut out = Vec::new();
59    for file in dir.files() {
60        out.push((file.path().to_string_lossy().into_owned(), file.contents()));
61    }
62    for sub in dir.dirs() {
63        out.extend(walk(sub));
64    }
65    out.sort_by(|a, b| a.0.cmp(&b.0));
66    out
67}
68
69/// The files one payload root carries, as `(path, bytes)` with the path
70/// carrying the root as its first segment, or `None` for a name the
71/// inventory does not declare.
72#[must_use]
73pub fn root_files(root: &str) -> Option<Vec<(String, &'static [u8])>> {
74    let dir = match root {
75        "method" => &METHOD,
76        "bindings" => &BINDINGS,
77        "runbooks" => &RUNBOOKS,
78        "forges" => &FORGES,
79        "snippets" => &SNIPPETS,
80        "setup" => &SETUP,
81        "skills" => &SKILLS,
82        "skill-shared" => &SKILL_SHARED,
83        "versions.toml" => return Some(vec![(root.to_owned(), VERSIONS.as_bytes())]),
84        _ => return None,
85    };
86    Some(
87        walk(dir)
88            .into_iter()
89            .map(|(path, bytes)| (format!("{root}/{path}"), bytes))
90            .collect(),
91    )
92}
93
94/// Every artifact the payload carries, root by root in inventory order,
95/// sorted by path within each root.
96///
97/// The license files are deliberately absent: they are crate metadata the
98/// registry requires, not authored payload, and `rk license` serves them.
99#[must_use]
100pub fn artifacts() -> Vec<(String, &'static [u8])> {
101    PAYLOAD_ROOTS
102        .iter()
103        .filter_map(|root| root_files(root))
104        .flatten()
105        .collect()
106}
107
108#[cfg(test)]
109mod tests {
110    #![allow(clippy::expect_used)]
111
112    use super::{PAYLOAD_ROOTS, artifacts, root_files};
113
114    /// The inventory and this module must name the same roots: a root
115    /// embedded here but absent from the inventory would be served without
116    /// change tracking, and a development build would then carry stale
117    /// bytes; a root declared but not embedded is a name `rk payload`
118    /// would report and nothing would serve.
119    #[test]
120    fn the_inventory_and_the_embed_declare_the_same_roots() {
121        let source = include_str!("embedded.rs");
122        let mut embedded: Vec<String> = source
123            .lines()
124            .filter_map(|line| {
125                let (_, rest) = line.split_once("include_dir!(\"$CARGO_MANIFEST_DIR/")?;
126                let (root, _) = rest.split_once('"')?;
127                Some(root.to_owned())
128            })
129            .collect();
130        embedded.extend(source.lines().filter_map(|line| {
131            let (_, rest) = line.split_once("include_str!(\"../")?;
132            let (name, _) = rest.split_once('"')?;
133            (!name.starts_with("LICENSE")).then(|| name.to_owned())
134        }));
135        embedded.sort();
136        let mut declared: Vec<String> = PAYLOAD_ROOTS.iter().map(ToString::to_string).collect();
137        declared.sort();
138        assert_eq!(
139            embedded, declared,
140            "src/embedded.rs and src/payload_roots.rs disagree on the payload roots"
141        );
142    }
143
144    #[test]
145    fn every_declared_root_serves_at_least_one_file() {
146        for root in PAYLOAD_ROOTS {
147            let files = root_files(root).expect("a declared root resolves");
148            assert!(!files.is_empty(), "{root}: the root carries no file");
149            for (path, _) in &files {
150                assert!(
151                    path == root || path.starts_with(&format!("{root}/")),
152                    "{path}: an artifact path must carry its root"
153                );
154            }
155        }
156        assert!(root_files("no-such-root").is_none());
157    }
158
159    #[test]
160    fn the_artifact_list_is_stable_and_complete() {
161        let listed = artifacts();
162        let total: usize = PAYLOAD_ROOTS
163            .iter()
164            .map(|root| root_files(root).expect("a declared root resolves").len())
165            .sum();
166        assert_eq!(listed.len(), total);
167        assert!(
168            listed.iter().any(|(path, _)| path == "versions.toml"),
169            "the single-file root must appear as itself"
170        );
171    }
172}