xbp 10.46.0

XBP is a zero-config build pack that can also interact with proxies, kafka, sockets, synthetic monitors.
Documentation
pub mod command;
pub mod network;
pub mod network_hetzner;
pub mod nginx;
pub mod pm2;

use std::process::Output;

/// Process-wide override for network config path roots (tests only).
/// Shared by `network` and `network_hetzner` so path mapping stays consistent.
pub(crate) const NETWORK_TEST_ROOT_ENV: &str = "XBP_NETWORK_TEST_ROOT";

/// Serialize tests that mutate [`NETWORK_TEST_ROOT_ENV`]. Both network modules
/// used separate mutexes, which raced under `cargo test` and flaked backend detection.
#[cfg(test)]
pub(crate) fn network_test_env_lock() -> std::sync::MutexGuard<'static, ()> {
    use std::sync::{Mutex, OnceLock};
    static LOCK: OnceLock<Mutex<()>> = OnceLock::new();
    LOCK.get_or_init(|| Mutex::new(()))
        .lock()
        .unwrap_or_else(|poisoned| poisoned.into_inner())
}

pub(crate) fn decode_output(bytes: &[u8]) -> String {
    let text = String::from_utf8_lossy(bytes).trim().to_string();
    if text.is_empty() {
        "(empty)".to_string()
    } else {
        text
    }
}

pub(crate) fn command_debug_log(
    debug: bool,
    program: &str,
    args: &[&str],
    output: &Output,
    logger: impl FnOnce(String),
) {
    if !debug {
        return;
    }
    logger(format!(
        "{} {} -> status={} stdout='{}' stderr='{}'",
        program,
        args.join(" "),
        output.status,
        decode_output(&output.stdout),
        decode_output(&output.stderr)
    ));
}

pub(crate) fn command_failure_message(
    program: &str,
    args: &[&str],
    output: &Output,
    hint: Option<&str>,
) -> String {
    let stderr = decode_output(&output.stderr);
    let stdout = decode_output(&output.stdout);
    let mut message = format!(
        "{} {} failed with status {}.\nstdout: {}\nstderr: {}",
        program,
        args.join(" "),
        output.status,
        stdout,
        stderr
    );
    if let Some(hint) = hint {
        message.push_str(&format!("\nhelp: {}", hint));
    }
    message
}