use miette::{Context, IntoDiagnostic, miette};
use super::{FrozenMode, InstallOptions, run};
pub(super) struct ScratchDir(std::path::PathBuf);
impl ScratchDir {
pub(super) fn path(&self) -> &std::path::Path {
&self.0
}
}
impl Drop for ScratchDir {
fn drop(&mut self) {
let _ = std::fs::remove_dir_all(&self.0);
}
}
pub(super) fn prepare_scratch_copy(
src: &std::path::Path,
spec: &str,
) -> miette::Result<ScratchDir> {
let mut hasher = std::collections::hash_map::DefaultHasher::new();
use std::hash::{Hash, Hasher};
src.hash(&mut hasher);
std::process::id().hash(&mut hasher);
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_nanos())
.unwrap_or(0)
.hash(&mut hasher);
let dst = std::env::temp_dir().join(format!("aube-git-prep-{:x}", hasher.finish()));
if dst.exists() {
let _ = std::fs::remove_dir_all(&dst);
}
std::fs::create_dir_all(&dst)
.map_err(|e| miette!("git dep {spec}: create scratch dir {}: {e}", dst.display()))?;
let scratch = ScratchDir(dst);
let out = std::process::Command::new("cp")
.arg("-a")
.arg(format!("{}/.", src.display()))
.arg(scratch.path())
.output()
.map_err(|e| miette!("git dep {spec}: spawn cp for scratch copy: {e}"))?;
if !out.status.success() {
return Err(miette!(
"git dep {spec}: scratch copy failed: {}",
String::from_utf8_lossy(&out.stderr).trim()
));
}
let _ = std::fs::remove_dir_all(scratch.path().join(".git"));
Ok(scratch)
}
const GIT_PREPARE_MAX_DEPTH: u32 = 4;
pub(super) async fn run_git_dep_prepare(
clone_dir: &std::path::Path,
spec: &str,
ignore_scripts: bool,
depth: u32,
) -> miette::Result<()> {
if depth >= GIT_PREPARE_MAX_DEPTH {
return Err(miette!(
"git dep {spec}: `prepare` nesting exceeded {GIT_PREPARE_MAX_DEPTH} levels"
));
}
let mut opts = InstallOptions::with_mode(super::super::chained_frozen_mode(FrozenMode::Prefer));
opts.project_dir = Some(clone_dir.to_path_buf());
opts.ignore_scripts = ignore_scripts;
opts.git_prepare_depth = depth + 1;
let spec = spec.to_string();
tokio::task::spawn_blocking(move || {
let runtime = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.into_diagnostic()
.wrap_err("failed to build nested git prepare runtime")?;
runtime.block_on(run(opts))
})
.await
.into_diagnostic()
.wrap_err_with(|| format!("git dep {spec}: nested install task failed"))?
.wrap_err_with(|| format!("git dep {spec}: nested install for `prepare` failed"))
}