#![allow(dead_code)]
use std::path::{Path, PathBuf};
use std::process::{Command, Output};
pub fn prikk(repo: &Path) -> Command {
let mut cmd = Command::new(env!("CARGO_BIN_EXE_prikk"));
cmd.current_dir(repo);
cmd
}
pub fn ok(output: &Output, what: &str) {
assert!(
output.status.success(),
"{what} failed (status {:?})\nstdout: {}\nstderr: {}",
output.status.code(),
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr),
);
}
pub fn unique_suffix() -> String {
static SEQUENCE: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
let nanos = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos();
let sequence = SEQUENCE.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
format!("{}-{nanos}-{sequence}", std::process::id())
}
pub fn unique_repo(tag: &str) -> PathBuf {
let mut dir = std::env::temp_dir();
dir.push(format!("prikk-cli-dc67-{tag}-{}", unique_suffix()));
std::fs::create_dir_all(&dir).unwrap();
dir
}
pub const AUTHOR_KEY_ID: &str = "dc67-test-author";
pub const AUTHOR_SEED_HEX: &str =
"3300445566778899001122334455667788990011223344556677889900112233";
pub const MAINTAINER_KEY_ID: &str = "dc67-test-maintainer";
pub const MAINTAINER_SEED: [u8; 32] = [
0x71, 0x71, 0x82, 0x82, 0x93, 0x93, 0xa4, 0xa4, 0xb5, 0xb5, 0xc6, 0xc6, 0xd7, 0xd7, 0xe8, 0xe8,
0xf9, 0xf9, 0x0a, 0x0a, 0x1b, 0x1b, 0x2c, 0x2c, 0x3d, 0x3d, 0x4e, 0x4e, 0x5f, 0x5f, 0x60, 0x60,
];
pub fn hex(bytes: &[u8]) -> String {
bytes.iter().map(|byte| format!("{byte:02x}")).collect()
}
pub fn maintainer_public_key_hex() -> String {
use prikk_store::MaintainerSigner;
let signer =
prikk_store::Ed25519MaintainerSigner::from_seed(MAINTAINER_KEY_ID, &MAINTAINER_SEED)
.expect("fixed maintainer seed derives a valid signer");
hex(&signer.public_key_bytes())
}
pub fn init(repo: &Path) {
ok(&prikk(repo).arg("init").output().unwrap(), "init");
}
pub fn commit(repo: &Path, ref_name: &str, message: &str) -> Output {
prikk(repo)
.env("PRIKK_AUTHOR_KEY_ID", AUTHOR_KEY_ID)
.env("PRIKK_AUTHOR_SEED", AUTHOR_SEED_HEX)
.args(["commit", "--ref", ref_name, "-m", message])
.output()
.unwrap()
}
pub fn trust_maintainer(repo: &Path) {
let _ = prikk(repo)
.args([
"trust",
"maintainer",
"add",
"--key-id",
MAINTAINER_KEY_ID,
"--public-key",
&maintainer_public_key_hex(),
])
.output()
.unwrap();
}
pub fn seal(repo: &Path, ref_name: &str) -> Output {
trust_maintainer(repo);
prikk(repo)
.env("PRIKK_MAINTAINER_KEY_ID", MAINTAINER_KEY_ID)
.env("PRIKK_MAINTAINER_SEED", hex(&MAINTAINER_SEED))
.args(["seal", "--allow-no-audit", "--ref", ref_name])
.output()
.unwrap()
}
pub fn generation(repo: &Path, ref_name: &str, path: &str, content: &[u8], message: &str) {
std::fs::write(repo.join(path), content).unwrap();
ok(
&commit(repo, ref_name, message),
&format!("commit: {message}"),
);
ok(&seal(repo, ref_name), &format!("seal: {message}"));
}
pub fn branch_create(repo: &Path, name: &str, from: &str) -> Output {
trust_maintainer(repo);
prikk(repo)
.env("PRIKK_MAINTAINER_KEY_ID", MAINTAINER_KEY_ID)
.env("PRIKK_MAINTAINER_SEED", hex(&MAINTAINER_SEED))
.args(["branch", "create", name, "--from", from])
.output()
.unwrap()
}
pub fn branch_close(repo: &Path, name: &str) -> Output {
trust_maintainer(repo);
prikk(repo)
.env("PRIKK_MAINTAINER_KEY_ID", MAINTAINER_KEY_ID)
.env("PRIKK_MAINTAINER_SEED", hex(&MAINTAINER_SEED))
.args(["branch", "close", name])
.output()
.unwrap()
}
pub fn tag_create(repo: &Path, name: &str, target: &str) -> Output {
trust_maintainer(repo);
prikk(repo)
.env("PRIKK_MAINTAINER_KEY_ID", MAINTAINER_KEY_ID)
.env("PRIKK_MAINTAINER_SEED", hex(&MAINTAINER_SEED))
.args(["tag", "create", name, "--target", target])
.output()
.unwrap()
}
pub fn verify(repo: &Path) -> Output {
prikk(repo).arg("verify").output().unwrap()
}
pub fn append_torn_ref_log_tail(container_path: &Path) {
const MAGIC: &[u8; 8] = b"PREFCON1";
const HEADER_LEN: usize = 8 + 2 + 32 + 8 + 32;
let bytes = std::fs::read(container_path).unwrap();
let start = bytes
.windows(MAGIC.len())
.rposition(|window| window == MAGIC)
.expect("ref log container has at least one real record to duplicate");
let end = (start + HEADER_LEN + 8).min(bytes.len());
assert!(
end < bytes.len(),
"duplicated span must land inside the real record's body, not consume it entirely, \
or the result would be a complete record rather than a torn one"
);
let torn = bytes[start..end].to_vec();
let mut file = std::fs::OpenOptions::new()
.append(true)
.open(container_path)
.unwrap();
use std::io::Write as _;
file.write_all(&torn).unwrap();
}
pub fn copy_dir_recursive(src: &Path, dst: &Path) {
std::fs::create_dir_all(dst).unwrap();
for entry in std::fs::read_dir(src).unwrap() {
let entry = entry.unwrap();
let file_type = entry.file_type().unwrap();
let dst_path = dst.join(entry.file_name());
if file_type.is_dir() {
copy_dir_recursive(&entry.path(), &dst_path);
} else {
std::fs::copy(entry.path(), &dst_path).unwrap();
}
}
}
pub fn rebuild_from_sealed_history(repo: &Path, tag: &str) -> PathBuf {
let materialize_root = unique_repo(&format!("{tag}-materialize"));
std::fs::create_dir_all(materialize_root.join(".prikk")).unwrap();
copy_dir_recursive(&repo.join(".prikk"), &materialize_root.join(".prikk"));
let out = prikk(&materialize_root)
.arg("checkout")
.arg("--patch-materialize")
.output()
.unwrap();
ok(&out, "checkout --patch-materialize");
materialize_root
}