flowrs-engine/crates/flowrs-registry/build.rs
flowRS 2b6f30d348 flowRS engine, cut from flowRS f907e0756a7c
Built alone from an empty CARGO_HOME, its tests passed, and flowrsc checked, ran and exported a
document from the conformance corpus; the export built alone.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-10 08:54:33 -05:00

110 lines
4.7 KiB
Rust

//! The first-party keys, and the content address of every registry package flowRS's hosts
//! carry, computed from the very files cargo compiles into them.
//!
//! The keys: `FIRST_PARTY` is the id of each public key file in `registry/keys/`, so a host
//! trusts exactly what the repository publishes. Replacing the key is replacing the file.
//!
//! The packages:
//!
//! A host admits a package it carries only when the index holds a signed record for the
//! exact hash of that package's tree. The hash is taken here, at build time, from the tree
//! on disk ([`archive::read_tree`], the same reading `flowrsc pkg pack` does), so it is the
//! hash of the code this build compiles: edit one byte of a carried package without
//! verifying it again and every host refuses it with a hash mismatch, which is the contract
//! working. Each manifest is read here too and handed to the crate as JSON, so the browser
//! needs no TOML reader.
#[path = "src/archive.rs"]
#[allow(dead_code)]
mod archive;
use std::path::Path;
/// The packages the hosts carry, relative to the repository root.
const CARRIED: &[&str] = &[
"crates/flowrs-node",
"packages/dialect-opentap",
"packages/dialect-teststand",
"crates/flowrs-neuroviz",
];
/// The ids in `registry/keys/*.json`, in file order. A file whose id is not `v2:` and 64 hex
/// digits stops the build; the signature check itself holds the key to its id at run time.
fn first_party(root: &Path) -> Vec<String> {
let dir = root.join("registry/keys");
println!("cargo:rerun-if-changed={}", dir.display());
let mut files: Vec<_> = std::fs::read_dir(&dir)
.unwrap_or_else(|e| panic!("{}: {e}", dir.display()))
.flatten()
.map(|e| e.path())
.filter(|p| p.extension().is_some_and(|x| x == "json"))
.collect();
files.sort();
files
.iter()
.map(|p| {
let v: serde_json::Value = serde_json::from_str(
&std::fs::read_to_string(p).unwrap_or_else(|e| panic!("{}: {e}", p.display())),
)
.unwrap_or_else(|e| panic!("{}: {e}", p.display()));
let id = v["id"].as_str().unwrap_or_default().to_string();
let hex = id.strip_prefix("v2:").unwrap_or_default();
assert!(
hex.len() == 64 && hex.bytes().all(|b| b.is_ascii_hexdigit()),
"{}: `{id}` is not a key id",
p.display()
);
id
})
.collect()
}
fn main() {
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=src/archive.rs");
let here = std::env::var("CARGO_MANIFEST_DIR").expect("cargo sets CARGO_MANIFEST_DIR");
let root = Path::new(&here)
.parent()
.and_then(Path::parent)
.expect("the crate is at <root>/crates/flowrs-registry");
let out_dir = std::env::var("OUT_DIR").expect("cargo sets OUT_DIR");
let ids = first_party(root);
let mut keys = String::from(
"/// The first-party keys: the id of each public key file in `registry/keys/`.\n\
pub const FIRST_PARTY: &[&str] = &[\n",
);
for id in &ids {
keys.push_str(&format!(" {id:?},\n"));
}
keys.push_str("];\n");
std::fs::write(Path::new(&out_dir).join("first_party.rs"), keys).expect("OUT_DIR is writable");
// Only `carried` reads the table below, and only a host that carries packages needs their
// trees to exist: the public engine, which has `flowrsc pkg` but carries none, builds
// without them.
if std::env::var_os("CARGO_FEATURE_CARRIED").is_none() {
return;
}
let mut out = String::from(
"/// Generated by build.rs: each carried package's directory, content address and\n\
/// manifest (as JSON).\n\
pub const CARRIED: &[Carried] = &[\n",
);
for rel in CARRIED.iter().copied() {
let dir = root.join(rel);
println!("cargo:rerun-if-changed={}", dir.display());
let entries = archive::read_tree(&dir).unwrap_or_else(|e| panic!("{rel}: {e}"));
let bytes = archive::encode(&entries).unwrap_or_else(|e| panic!("{rel}: {e}"));
let hash = archive::hash(&bytes);
let toml_text = std::fs::read_to_string(dir.join("flowrs-package.toml"))
.unwrap_or_else(|e| panic!("{rel}/flowrs-package.toml: {e}"));
let value: toml::Value =
toml::from_str(&toml_text).unwrap_or_else(|e| panic!("{rel}/flowrs-package.toml: {e}"));
let json = serde_json::to_string(&value).expect("a TOML value is JSON");
out.push_str(&format!(
" Carried {{ dir: {rel:?}, hash: {hash:?}, manifest: {json:?} }},\n"
));
}
out.push_str("];\n");
let dest = Path::new(&out_dir).join("carried.rs");
std::fs::write(&dest, out).expect("OUT_DIR is writable");
}