#![allow(dead_code)]
pub mod bitmap;
pub mod cobs;
pub mod signing;
use std::path::Path;
use std::process::{Command, Output};
use base64::Engine as _;
/// copies of `plan.rs`'s `repo_key` and `repo_token`, since the bin has no lib target to link.
pub fn repo_key(repo_did: &str) -> String {
let mut key = String::new();
for byte in repo_did.bytes() {
match byte {
b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'_' | b'.' => key.push(byte as char),
_ => key.push_str(&format!("%{byte:02X}")),
}
}
key
}
pub fn repo_token(repo_did: &str) -> String {
use sha2::Digest as _;
knot_types::lowercase_hex(&sha2::Sha256::digest(repo_did.as_bytes()))[..32].to_string()
}
pub fn git_command(dir: &Path) -> Command {
let mut command = Command::new("git");
command
.current_dir(dir)
.stdin(std::process::Stdio::null())
.env("GIT_CONFIG_GLOBAL", "/dev/null")
.env("GIT_CONFIG_SYSTEM", "/dev/null")
// a detached `maintenance --auto` can prune `.git/objects` dirs while a test copies them.
.env("GIT_CONFIG_COUNT", "1")
.env("GIT_CONFIG_KEY_0", "maintenance.auto")
.env("GIT_CONFIG_VALUE_0", "false")
.env("GIT_AUTHOR_NAME", "Test")
.env("GIT_AUTHOR_EMAIL", "test@example.test")
.env("GIT_COMMITTER_NAME", "Test")
.env("GIT_COMMITTER_EMAIL", "test@example.test");
without_repo_env(&mut command);
command
}
pub fn without_repo_env(command: &mut Command) -> &mut Command {
for name in [
"GIT_DIR",
"GIT_WORK_TREE",
"GIT_INDEX_FILE",
"GIT_OBJECT_DIRECTORY",
"GIT_ALTERNATE_OBJECT_DIRECTORIES",
"GIT_NAMESPACE",
"GIT_CEILING_DIRECTORIES",
] {
command.env_remove(name);
}
command
}
pub fn git_raw(dir: &Path, args: impl IntoIterator<Item = impl AsRef<std::ffi::OsStr>>) -> Output {
git_command(dir).args(args).output().expect("run git")
}
pub fn git(dir: &Path, args: impl IntoIterator<Item = impl AsRef<std::ffi::OsStr>>) -> String {
let output = git_raw(dir, args);
assert_success(&output, "git");
String::from_utf8_lossy(&output.stdout).trim().to_string()
}
pub fn commit_graph_covers(repo: &Path, oid: &str) -> bool {
let git = knot_git::Repo::open(repo).expect("open repo");
gix::commitgraph::Graph::from_info_dir(&git.objects_dir().join("info")).is_ok_and(|graph| {
graph
.lookup(gix::ObjectId::from_hex(oid.as_bytes()).expect("oid"))
.is_some()
})
}
pub fn commit(repo: &Path, path: &str, contents: &str, message: &str) {
let full = repo.join(path);
if let Some(parent) = full.parent() {
std::fs::create_dir_all(parent).unwrap();
}
std::fs::write(&full, contents).unwrap();
git(repo, ["add", "-A"]);
git(repo, ["commit", "-q", "-m", message]);
}
pub fn knot_static_cmd(
config: &Path,
key_env: &str,
master_key: &[u8; 32],
args: impl IntoIterator<Item = impl AsRef<std::ffi::OsStr>>,
) -> Command {
let mut cmd = Command::new(env!("CARGO_BIN_EXE_knot-static"));
cmd.arg("--config")
.arg(config)
.args(args)
.env(
key_env,
base64::engine::general_purpose::STANDARD.encode(master_key),
)
.env("GIT_CONFIG_GLOBAL", "/dev/null")
.env("GIT_CONFIG_SYSTEM", "/dev/null");
cmd
}
pub fn knot_static_raw(
config: &Path,
key_env: &str,
master_key: &[u8; 32],
args: impl IntoIterator<Item = impl AsRef<std::ffi::OsStr>>,
extra_env: &[(&str, &str)],
) -> Output {
let mut cmd = knot_static_cmd(config, key_env, master_key, args);
for (k, v) in extra_env {
cmd.env(k, v);
}
cmd.output().expect("run knot-static")
}
pub fn knot_static(
config: &Path,
key_env: &str,
master_key: &[u8; 32],
args: impl IntoIterator<Item = impl AsRef<std::ffi::OsStr>>,
) -> Output {
let output = knot_static_raw(config, key_env, master_key, args, &[]);
assert_success(&output, "knot-static");
output
}
pub fn assert_success(output: &Output, context: &str) {
assert!(
output.status.success(),
"{context} failed: stdout={} stderr={}",
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr),
);
}