2025-12-01 12:48:39 +01:00
|
|
|
use std::{
|
|
|
|
|
env, fs,
|
|
|
|
|
path::{Path, PathBuf},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
use anyhow::{Context as _, Result};
|
|
|
|
|
use tempfile::TempDir;
|
|
|
|
|
|
|
|
|
|
/// Copy the repository stack assets into a scenario-specific temp dir.
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub struct ComposeWorkspace {
|
|
|
|
|
root: TempDir,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ComposeWorkspace {
|
|
|
|
|
/// Clone the stack assets into a temporary directory.
|
|
|
|
|
pub fn create() -> Result<Self> {
|
|
|
|
|
let repo_root = env::var("CARGO_WORKSPACE_DIR")
|
|
|
|
|
.map(PathBuf::from)
|
|
|
|
|
.or_else(|_| {
|
|
|
|
|
Path::new(env!("CARGO_MANIFEST_DIR"))
|
|
|
|
|
.parent()
|
|
|
|
|
.and_then(Path::parent)
|
|
|
|
|
.and_then(Path::parent)
|
|
|
|
|
.map(Path::to_path_buf)
|
|
|
|
|
.context("resolving workspace root from manifest dir")
|
|
|
|
|
})
|
|
|
|
|
.context("locating repository root")?;
|
|
|
|
|
let temp = tempfile::Builder::new()
|
|
|
|
|
.prefix("nomos-testnet-")
|
|
|
|
|
.tempdir()
|
|
|
|
|
.context("creating testnet temp dir")?;
|
|
|
|
|
let stack_source = stack_assets_root(&repo_root);
|
|
|
|
|
if !stack_source.exists() {
|
|
|
|
|
anyhow::bail!(
|
|
|
|
|
"stack assets directory not found at {}",
|
|
|
|
|
stack_source.display()
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
copy_dir_recursive(&stack_source, &temp.path().join("stack"))?;
|
|
|
|
|
let scripts_source = stack_scripts_root(&repo_root);
|
|
|
|
|
if scripts_source.exists() {
|
|
|
|
|
copy_dir_recursive(&scripts_source, &temp.path().join("stack/scripts"))?;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Ensure Prometheus config is a file (Docker bind mount fails if a directory
|
|
|
|
|
// exists).
|
|
|
|
|
let prometheus_src = stack_source.join("monitoring/prometheus.yml");
|
|
|
|
|
let prometheus_dst = temp.path().join("stack/monitoring/prometheus.yml");
|
|
|
|
|
if prometheus_dst.exists() && prometheus_dst.is_dir() {
|
|
|
|
|
fs::remove_dir_all(&prometheus_dst)
|
|
|
|
|
.with_context(|| format!("removing bogus dir {}", prometheus_dst.display()))?;
|
|
|
|
|
}
|
|
|
|
|
if !prometheus_dst.exists() {
|
|
|
|
|
fs::copy(&prometheus_src, &prometheus_dst).with_context(|| {
|
|
|
|
|
format!(
|
|
|
|
|
"copying prometheus.yml {} -> {}",
|
|
|
|
|
prometheus_src.display(),
|
|
|
|
|
prometheus_dst.display()
|
|
|
|
|
)
|
|
|
|
|
})?;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let kzg_source = repo_root.join("testing-framework/assets/stack/kzgrs_test_params");
|
2025-12-07 05:16:52 +01:00
|
|
|
let target = temp.path().join("kzgrs_test_params");
|
2025-12-01 12:48:39 +01:00
|
|
|
if kzg_source.exists() {
|
|
|
|
|
if kzg_source.is_dir() {
|
|
|
|
|
copy_dir_recursive(&kzg_source, &target)?;
|
|
|
|
|
} else {
|
|
|
|
|
fs::copy(&kzg_source, &target).with_context(|| {
|
|
|
|
|
format!("copying {} -> {}", kzg_source.display(), target.display())
|
|
|
|
|
})?;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-12-07 05:16:52 +01:00
|
|
|
// Fail fast if the KZG bundle is missing or empty; DA verifier will panic
|
|
|
|
|
// otherwise.
|
|
|
|
|
if !target.exists()
|
|
|
|
|
|| fs::read_dir(&target)
|
|
|
|
|
.ok()
|
|
|
|
|
.map(|mut it| it.next().is_none())
|
|
|
|
|
.unwrap_or(true)
|
|
|
|
|
{
|
|
|
|
|
anyhow::bail!(
|
|
|
|
|
"KZG params missing in stack assets (expected files in {})",
|
|
|
|
|
kzg_source.display()
|
|
|
|
|
);
|
|
|
|
|
}
|
2025-12-01 12:48:39 +01:00
|
|
|
|
|
|
|
|
Ok(Self { root: temp })
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[must_use]
|
|
|
|
|
/// Root of the temporary workspace on disk.
|
|
|
|
|
pub fn root_path(&self) -> &Path {
|
|
|
|
|
self.root.path()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[must_use]
|
|
|
|
|
/// Path to the copied assets directory.
|
|
|
|
|
pub fn stack_dir(&self) -> PathBuf {
|
|
|
|
|
self.root.path().join("stack")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[must_use]
|
|
|
|
|
/// Consume the workspace and return the underlying temp directory.
|
|
|
|
|
pub fn into_inner(self) -> TempDir {
|
|
|
|
|
self.root
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn stack_assets_root(repo_root: &Path) -> PathBuf {
|
|
|
|
|
let new_layout = repo_root.join("testing-framework/assets/stack");
|
|
|
|
|
if new_layout.exists() {
|
|
|
|
|
new_layout
|
|
|
|
|
} else {
|
|
|
|
|
repo_root.join("testnet")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn stack_scripts_root(repo_root: &Path) -> PathBuf {
|
|
|
|
|
let new_layout = repo_root.join("testing-framework/assets/stack/scripts");
|
|
|
|
|
if new_layout.exists() {
|
|
|
|
|
new_layout
|
|
|
|
|
} else {
|
|
|
|
|
repo_root.join("testnet/scripts")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn copy_dir_recursive(source: &Path, target: &Path) -> Result<()> {
|
|
|
|
|
fs::create_dir_all(target)
|
|
|
|
|
.with_context(|| format!("creating target dir {}", target.display()))?;
|
|
|
|
|
for entry in fs::read_dir(source).with_context(|| format!("reading {}", source.display()))? {
|
|
|
|
|
let entry = entry?;
|
|
|
|
|
let file_type = entry.file_type()?;
|
|
|
|
|
let dest = target.join(entry.file_name());
|
|
|
|
|
if file_type.is_dir() {
|
|
|
|
|
copy_dir_recursive(&entry.path(), &dest)?;
|
|
|
|
|
} else if !file_type.is_dir() {
|
|
|
|
|
fs::copy(entry.path(), &dest).with_context(|| {
|
|
|
|
|
format!("copying {} -> {}", entry.path().display(), dest.display())
|
|
|
|
|
})?;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|