934858bebf
- Add repo_path parameter to cached_response and cached_vec_response functions - Implement structured cache key format with namespace, repo_path, and request proto - Replace global cache with Moka in-memory cache using weight-based eviction - Set 256MB memory cap with 10-minute TTL and 2-minute TTI policy - Add metrics collection for cache operations and evictions - Implement efficient repo-scoped invalidation using key structure - Add detailed documentation comments explaining cache architecture - Remove outdated dependencies and update dependency versions - Add error handling for encoding failures in cache operations - Optimize Vec responses with length-delimited encoding and pre-allocation
96 lines
3.2 KiB
Rust
96 lines
3.2 KiB
Rust
//! Bundle applicator for restoring snapshots to git repositories.
|
|
//!
|
|
//! Uses `git bundle unbundle` to apply pack data to a bare repository.
|
|
|
|
use std::path::{Path, PathBuf};
|
|
|
|
pub struct BundleApplicator {
|
|
pub repo_path: PathBuf,
|
|
}
|
|
|
|
impl BundleApplicator {
|
|
pub fn new(repo_path: PathBuf) -> Self {
|
|
Self { repo_path }
|
|
}
|
|
|
|
pub fn apply_bundle(&self, data: &[u8]) -> Result<(), String> {
|
|
let mut child = std::process::Command::new("git")
|
|
.args([
|
|
"--git-dir",
|
|
&self.repo_path.to_string_lossy(),
|
|
"bundle",
|
|
"unbundle",
|
|
"-",
|
|
])
|
|
.stdin(std::process::Stdio::piped())
|
|
.stdout(std::process::Stdio::piped())
|
|
.stderr(std::process::Stdio::piped())
|
|
.spawn()
|
|
.map_err(|e| format!("spawn git bundle unbundle: {e}"))?;
|
|
use std::io::Write;
|
|
if let Some(ref mut stdin) = child.stdin {
|
|
stdin
|
|
.write_all(data)
|
|
.map_err(|e| format!("write bundle: {e}"))?;
|
|
}
|
|
let output = child
|
|
.wait_with_output()
|
|
.map_err(|e| format!("wait bundle: {e}"))?;
|
|
if !output.status.success() {
|
|
return Err(String::from_utf8_lossy(&output.stderr).into_owned());
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
/// Apply bundle from a file path (for streaming writes).
|
|
pub fn apply_bundle_from_file(&self, path: &Path) -> Result<(), String> {
|
|
let file = std::fs::File::open(path).map_err(|e| format!("open bundle file: {e}"))?;
|
|
let mut child = std::process::Command::new("git")
|
|
.args([
|
|
"--git-dir",
|
|
&self.repo_path.to_string_lossy(),
|
|
"bundle",
|
|
"unbundle",
|
|
"-",
|
|
])
|
|
.stdin(std::process::Stdio::piped())
|
|
.stdout(std::process::Stdio::piped())
|
|
.stderr(std::process::Stdio::piped())
|
|
.spawn()
|
|
.map_err(|e| format!("spawn git bundle unbundle: {e}"))?;
|
|
|
|
// Stream file contents to stdin in a background thread
|
|
let mut stdin = child.stdin.take().ok_or("no stdin")?;
|
|
let file_handle = file;
|
|
let writer = std::thread::spawn(move || -> Result<(), String> {
|
|
use std::io::{Read, Write};
|
|
let mut reader = std::io::BufReader::new(file_handle);
|
|
let mut buf = vec![0u8; 65536];
|
|
loop {
|
|
match reader.read(&mut buf) {
|
|
Ok(0) => break,
|
|
Ok(n) => {
|
|
stdin
|
|
.write_all(&buf[..n])
|
|
.map_err(|e| format!("write to stdin: {e}"))?;
|
|
}
|
|
Err(e) => return Err(format!("read bundle file: {e}")),
|
|
}
|
|
}
|
|
Ok(())
|
|
});
|
|
|
|
let output = child
|
|
.wait_with_output()
|
|
.map_err(|e| format!("wait bundle: {e}"))?;
|
|
|
|
// Wait for writer thread
|
|
let _ = writer.join().map_err(|_| "writer thread panicked")?;
|
|
|
|
if !output.status.success() {
|
|
return Err(String::from_utf8_lossy(&output.stderr).into_owned());
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|