use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use anyhow::{Context, Result};
use super::index::{IndexMetadata, PackageIndex};
pub fn prepare_index_repo(repo_url: &str, name: &str, verbose: bool) -> Result<PathBuf> {
let ccgo_home = PackageIndex::new().ccgo_home_path();
let work_dir = ccgo_home.join("registry").join("publish").join(name);
if work_dir.exists() {
println!("đĨ Updating existing index clone...");
let mut cmd = Command::new("git");
cmd.current_dir(&work_dir);
cmd.args(["pull", "--rebase"]);
if !verbose {
cmd.stdout(Stdio::null()).stderr(Stdio::null());
}
let status = cmd.status().context("Failed to pull index repository")?;
if !status.success() {
println!("â ī¸ Pull failed, re-cloning...");
std::fs::remove_dir_all(&work_dir)?;
return clone_or_init(repo_url, name, &work_dir, verbose);
}
Ok(work_dir)
} else {
clone_or_init(repo_url, name, &work_dir, verbose)
}
}
fn clone_or_init(repo_url: &str, name: &str, work_dir: &Path, verbose: bool) -> Result<PathBuf> {
println!("đĨ Cloning index repository...");
if let Some(parent) = work_dir.parent() {
std::fs::create_dir_all(parent)?;
}
let mut cmd = Command::new("git");
cmd.args(["clone", "--depth", "1", repo_url, work_dir.to_str().unwrap()]);
if !verbose {
cmd.stdout(Stdio::null()).stderr(Stdio::null());
}
let status = cmd
.status()
.context("Failed to clone index repository")?;
if !status.success() {
println!("đ Initializing new index repository...");
std::fs::create_dir_all(work_dir)?;
Command::new("git")
.current_dir(work_dir)
.args(["init"])
.status()
.context("Failed to init git repository")?;
Command::new("git")
.current_dir(work_dir)
.args(["remote", "add", "origin", repo_url])
.status()
.context("Failed to add git remote")?;
let metadata = IndexMetadata {
version: 1,
name: name.to_string(),
description: format!("{} package index", name),
homepage: None,
package_count: 0,
updated_at: chrono::Utc::now().to_rfc3339(),
};
let json = serde_json::to_string_pretty(&metadata)?;
std::fs::write(work_dir.join("index.json"), json)?;
}
Ok(work_dir.to_path_buf())
}
pub fn update_index_metadata(index_path: &Path, name: &str) -> Result<()> {
let metadata_path = index_path.join("index.json");
let mut metadata: IndexMetadata = if metadata_path.exists() {
let content = std::fs::read_to_string(&metadata_path)?;
serde_json::from_str(&content).unwrap_or_else(|_| IndexMetadata {
version: 1,
name: name.to_string(),
description: format!("{} package index", name),
homepage: None,
package_count: 0,
updated_at: String::new(),
})
} else {
IndexMetadata {
version: 1,
name: name.to_string(),
description: format!("{} package index", name),
homepage: None,
package_count: 0,
updated_at: String::new(),
}
};
let mut count = 0;
for entry in walkdir::WalkDir::new(index_path)
.into_iter()
.filter_map(|e| e.ok())
{
if entry.path().extension().and_then(|s| s.to_str()) == Some("json")
&& entry.file_name() != "index.json"
{
count += 1;
}
}
metadata.package_count = count;
metadata.updated_at = chrono::Utc::now().to_rfc3339();
let json = serde_json::to_string_pretty(&metadata)?;
std::fs::write(metadata_path, json)?;
println!("đ Index metadata updated: {} package(s)", count);
Ok(())
}
pub fn commit_changes(index_path: &Path, message: &str, verbose: bool) -> Result<()> {
let mut cmd = Command::new("git");
cmd.current_dir(index_path).args(["add", "-A"]);
if !verbose {
cmd.stdout(Stdio::null());
}
cmd.status().context("Failed to stage changes")?;
let output = Command::new("git")
.current_dir(index_path)
.args(["status", "--porcelain"])
.output()
.context("Failed to check git status")?;
if output.stdout.is_empty() {
println!("âšī¸ No changes to commit");
return Ok(());
}
let mut cmd = Command::new("git");
cmd.current_dir(index_path).args(["commit", "-m", message]);
if !verbose {
cmd.stdout(Stdio::null());
}
let status = cmd.status().context("Failed to commit changes")?;
if status.success() {
println!("â
Committed: {}", message);
}
Ok(())
}
pub fn push_changes(index_path: &Path, verbose: bool) -> Result<()> {
let mut cmd = Command::new("git");
cmd.current_dir(index_path).args(["push", "origin", "HEAD"]);
if !verbose {
cmd.stderr(Stdio::null());
}
let status = cmd.status().context("Failed to push changes")?;
if !status.success() {
anyhow::bail!("Failed to push to remote");
}
Ok(())
}