use std::path::Path;
use tracing::info;
use super::account_clone::{AccountCloneEnv, account_clone_env};
use super::{ensure_worktrees_gitignored, migrate_old_layout_aside};
use crate::core::gh_account_registry::RegistryPin;
use crate::core::gh_org_accounts::{OrgAccounts, OrgAccountsError};
use crate::core::remote_url_redact::{redact_stored_url, redact_url};
pub fn ensure_base_clone(
origin_url: &str,
base_path: &Path,
account: Option<&str>,
) -> Result<(), String> {
ensure_base_clone_with(
origin_url,
base_path,
account,
|| {
crate::core::gh_account_registry::read_pin(
&crate::project::registry_data_dir(),
origin_url,
)
},
OrgAccounts::load_default,
account_clone_env,
)
}
pub(crate) fn ensure_base_clone_with(
origin_url: &str,
base_path: &Path,
account: Option<&str>,
pin: impl FnOnce() -> Result<Option<RegistryPin>, String>,
load: impl FnOnce() -> Result<OrgAccounts, OrgAccountsError>,
clone_env: impl FnOnce(&str) -> Result<AccountCloneEnv, String>,
) -> Result<(), String> {
if base_path.join(".git").exists() {
info!(
path = %base_path.display(),
"inproject: base clone already present, reusing"
);
ensure_worktrees_gitignored(base_path)?;
return Ok(());
}
let aliases = crate::session_manager::ssh_host_alias::SshHostAliases::for_current_user();
let resolved = crate::core::gh_org_accounts::resolve_gh_account_with(
account, origin_url, &aliases, pin, load,
)
.map_err(|e| format!("inproject: cannot choose the gh account to clone with: {e}"))?;
let env = match resolved.as_ref().map(|r| r.login.as_str()) {
Some(login) => {
Some(clone_env(login).map_err(|e| format!("inproject: cannot clone as {login}: {e}"))?)
}
None => None,
};
migrate_old_layout_aside(base_path)?;
crate::core::provisioning_stage::emit(
crate::core::provisioning_stage::ProvisioningStage::CloningRepo,
);
info!(
url = %redact_stored_url(origin_url),
dest = %base_path.display(),
"inproject: cloning base repo"
);
if let Some(parent) = base_path.parent() {
std::fs::create_dir_all(parent).map_err(|e| {
format!(
"inproject: failed to create parent dir {}: {e}",
parent.display()
)
})?;
}
let cwd = base_path.parent().unwrap_or(std::path::Path::new("/"));
let mut cmd = trusty_common::git::command();
cmd.args(["clone", "--no-local", origin_url])
.arg(base_path)
.current_dir(cwd);
if let Some(env) = &env {
env.apply(&mut cmd);
}
let out = cmd
.output()
.map_err(|e| format!("inproject: git clone failed to spawn: {e}"))?;
if !out.status.success() {
let stderr = String::from_utf8_lossy(&out.stderr);
return Err(format!(
"inproject: git clone failed ({}): {}",
out.status,
redact_url(&stderr)
));
}
info!(dest = %base_path.display(), "inproject: base clone complete");
ensure_worktrees_gitignored(base_path)?;
crate::core::harness_exclude::ensure_and_log(base_path); crate::core::git_maintenance::disable_and_log(base_path);
crate::core::push_guard::install_and_log(base_path);
Ok(())
}
#[cfg(test)]
#[path = "base_clone_tests.rs"]
mod tests;