Files
compliance-scanner-agent/compliance-agent/src/pipeline/git.rs
Sharang Parnerkar 03ee69834d
All checks were successful
CI / Format (push) Successful in 3s
CI / Clippy (push) Successful in 2m15s
CI / Security Audit (push) Successful in 1m34s
CI / Tests (push) Successful in 3m4s
Fix formatting and clippy warnings across workspace
- Run cargo fmt on all crates
- Fix regex patterns using unsupported lookahead in patterns.rs
- Replace unwrap() calls with compile_regex() helper
- Fix never type fallback in GitHub tracker
- Fix redundant field name in findings page
- Allow enum_variant_names for Dioxus Route enum
- Fix &mut Vec -> &mut [T] clippy lint in sbom.rs
- Mark unused-but-intended APIs with #[allow(dead_code)]

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 17:41:03 +01:00

97 lines
2.9 KiB
Rust

use std::path::{Path, PathBuf};
use git2::{FetchOptions, Repository};
use crate::error::AgentError;
pub struct GitOps {
base_path: PathBuf,
}
impl GitOps {
pub fn new(base_path: &str) -> Self {
Self {
base_path: PathBuf::from(base_path),
}
}
pub fn clone_or_fetch(&self, git_url: &str, repo_name: &str) -> Result<PathBuf, AgentError> {
let repo_path = self.base_path.join(repo_name);
if repo_path.exists() {
self.fetch(&repo_path)?;
} else {
std::fs::create_dir_all(&repo_path)?;
Repository::clone(git_url, &repo_path)?;
tracing::info!("Cloned {git_url} to {}", repo_path.display());
}
Ok(repo_path)
}
fn fetch(&self, repo_path: &Path) -> Result<(), AgentError> {
let repo = Repository::open(repo_path)?;
let mut remote = repo.find_remote("origin")?;
let mut fetch_opts = FetchOptions::new();
remote.fetch(&[] as &[&str], Some(&mut fetch_opts), None)?;
// Fast-forward to origin/HEAD
let fetch_head = repo.find_reference("FETCH_HEAD")?;
let fetch_commit = repo.reference_to_annotated_commit(&fetch_head)?;
let head_ref = repo.head()?;
let head_name = head_ref.name().unwrap_or("HEAD");
repo.reference(head_name, fetch_commit.id(), true, "fast-forward")?;
repo.checkout_head(Some(git2::build::CheckoutBuilder::default().force()))?;
tracing::info!("Fetched and fast-forwarded {}", repo_path.display());
Ok(())
}
pub fn get_head_sha(repo_path: &Path) -> Result<String, AgentError> {
let repo = Repository::open(repo_path)?;
let head = repo.head()?;
let commit = head.peel_to_commit()?;
Ok(commit.id().to_string())
}
pub fn has_new_commits(repo_path: &Path, last_sha: Option<&str>) -> Result<bool, AgentError> {
let current_sha = Self::get_head_sha(repo_path)?;
match last_sha {
Some(sha) if sha == current_sha => Ok(false),
_ => Ok(true),
}
}
#[allow(dead_code)]
pub fn get_changed_files(
repo_path: &Path,
old_sha: &str,
new_sha: &str,
) -> Result<Vec<String>, AgentError> {
let repo = Repository::open(repo_path)?;
let old_commit = repo.find_commit(git2::Oid::from_str(old_sha)?)?;
let new_commit = repo.find_commit(git2::Oid::from_str(new_sha)?)?;
let old_tree = old_commit.tree()?;
let new_tree = new_commit.tree()?;
let diff = repo.diff_tree_to_tree(Some(&old_tree), Some(&new_tree), None)?;
let mut files = Vec::new();
diff.foreach(
&mut |delta, _| {
if let Some(path) = delta.new_file().path() {
files.push(path.to_string_lossy().to_string());
}
true
},
None,
None,
None,
)?;
Ok(files)
}
}