#[cfg(unix)]
use std::path::PathBuf;
#[cfg(unix)]
use color_eyre::eyre::eyre;
#[cfg(unix)]
use color_eyre::eyre::{Result, WrapErr};
#[cfg(unix)]
use crate::dirs;
#[cfg(unix)]
use crate::run;
#[cfg(unix)]
use crate::utils;
pub const AUR_BASE_URL: &str = "https://aur.archlinux.org/cgit/aur.git/snapshot/";
#[cfg(unix)]
pub const AUR_DOMAIN: &str = "aur.archlinux.org";
#[cfg(unix)]
pub fn handle_aur_git_download(
url: &str,
) -> Result<()> {
if !url.starts_with(AUR_BASE_URL) {
return Err(eyre!("Not an AUR URL"));
}
let pkgbase = url
.strip_prefix(AUR_BASE_URL)
.ok_or_else(|| eyre!("Invalid AUR URL format: {}", url))?
.strip_prefix("/")
.ok_or_else(|| eyre!("Invalid AUR URL format: {}", url))?
.strip_suffix(".tar.gz")
.ok_or_else(|| eyre!("AUR URL should end with .tar.gz: {}", url))?;
let (git_path, is_host_git) = find_git_command()?;
log::info!("Downloading AUR package {} using git", pkgbase);
let build_dir = dirs().user_aur_builds.clone();
let clone_dir = build_dir.join(pkgbase);
std::fs::create_dir_all(&build_dir)
.with_context(|| format!("Failed to create build directory: {}", build_dir.display()))?;
clone_or_fetch_aur_repo(&git_path, pkgbase, &clone_dir, is_host_git)?;
log::info!("Successfully downloaded AUR package {} to git directory {}", pkgbase, clone_dir.display());
Ok(())
}
#[cfg(unix)]
pub fn find_git_command() -> Result<(PathBuf, bool)> {
if let Some(git_path) = utils::find_command_in_paths("git") {
return Ok((git_path, true));
}
let env_root = dirs::get_default_env_root()?;
let git_path = run::find_command_in_env_path("git", &env_root)
.map_err(|_| eyre!("git command not found in host OS or environment"))?;
Ok((git_path, false))
}
#[cfg(unix)]
pub fn clone_or_fetch_aur_repo(
git_path: &std::path::Path,
pkgbase: &str,
clone_dir: &std::path::Path,
is_host_git: bool,
) -> Result<()> {
let git_url = format!("https://{}/{}.git", AUR_DOMAIN, pkgbase);
if clone_dir.exists() && !clone_dir.join(".git").exists() {
log::warn!(
"Cleaning non-git directory before cloning AUR repo: {}",
clone_dir.display()
);
std::fs::remove_dir_all(clone_dir)
.with_context(|| format!("Failed to remove non-git dir {}", clone_dir.display()))?;
}
let repo_exists = clone_dir.join(".git").exists();
let env_root = dirs::get_default_env_root().unwrap_or_else(|_| PathBuf::from("/"));
let base_run_options = run::RunOptions {
command: git_path.to_string_lossy().to_string(),
skip_namespace_isolation: is_host_git,
timeout: 300,
..Default::default()
};
if repo_exists {
log::info!("Git repository exists at {}, fetching updates", clone_dir.display());
let fetch_options = run::RunOptions {
args: vec![
"-C".to_string(),
clone_dir.to_string_lossy().to_string(),
"fetch".to_string(),
"origin".to_string(),
],
..base_run_options.clone()
};
run::fork_and_execute(&env_root, &fetch_options)
.with_context(|| format!("Failed to fetch git repository: {}", git_url))?;
let checkout_options = run::RunOptions {
args: vec![
"-C".to_string(),
clone_dir.to_string_lossy().to_string(),
"checkout".to_string(),
"HEAD".to_string(),
],
..base_run_options.clone()
};
if let Err(e) = run::fork_and_execute(&env_root, &checkout_options) {
log::warn!(
"Checkout HEAD failed in {}: {}. Trying git reset --hard + checkout.",
clone_dir.display(),
e
);
let reset_options = run::RunOptions {
args: vec![
"-C".to_string(),
clone_dir.to_string_lossy().to_string(),
"reset".to_string(),
"--hard".to_string(),
],
..base_run_options.clone()
};
run::fork_and_execute(&env_root, &reset_options)
.with_context(|| format!("Failed to reset repository: {}", clone_dir.display()))?;
run::fork_and_execute(&env_root, &checkout_options)
.with_context(|| format!("Failed to checkout HEAD in repository: {}", clone_dir.display()))?;
}
} else {
log::info!("Cloning git repository from {}", git_url);
let clone_parent = clone_dir.parent()
.ok_or_else(|| eyre!("clone_dir has no parent: {}", clone_dir.display()))?;
let clone_options = run::RunOptions {
args: vec![
"-C".to_string(),
clone_parent.to_string_lossy().to_string(),
"clone".to_string(),
"-q".to_string(),
"-c".to_string(),
"init.defaultBranch=master".to_string(),
git_url.clone(),
clone_dir.to_string_lossy().to_string(),
],
..base_run_options
};
run::fork_and_execute(&env_root, &clone_options)
.with_context(|| format!("Failed to clone git repository: {}", git_url))?;
}
Ok(())
}