f5044fb099
- Replace direct Rust build with cargo-chef multi-stage build pattern - Switch base image from debian:bookworm-slim to ubuntu:26.04 - Add .codegraph to .dockerignore and data to .gitignore - Introduce Dockerfile.fast for faster builds without optimization - Add comprehensive .env configuration file with cluster settings - Create docker-compose.yaml for multi-node cluster setup - Add cluster routing test case for distributed operations - Remove unnecessary success status checks in repository maintenance - Fix error handling in git command executions by properly propagating errors - Add repository move protection to prevent self-destruct operations - Simplify conditional logic in actor message validation - Update remote pack client calls with proper error handling parameters
192 lines
5.7 KiB
Rust
192 lines
5.7 KiB
Rust
use crate::pb::*;
|
|
|
|
use super::git_cmd;
|
|
|
|
pub(crate) fn maintenance_response(out: std::process::Output) -> RepositoryMaintenanceResponse {
|
|
RepositoryMaintenanceResponse {
|
|
ok: true,
|
|
stdout: String::from_utf8_lossy(&out.stdout).into_owned(),
|
|
stderr: String::from_utf8_lossy(&out.stderr).into_owned(),
|
|
}
|
|
}
|
|
|
|
/// Get approximate repository size using git count-objects instead of
|
|
/// recursively scanning the filesystem (which is O(n) and very slow for large repos).
|
|
fn dir_size(gb: &crate::bare::GitBare) -> u64 {
|
|
let out = git_cmd(gb, &["count-objects", "-v"]).ok();
|
|
let text = out
|
|
.as_ref()
|
|
.map(|o| String::from_utf8_lossy(&o.stdout).into_owned())
|
|
.unwrap_or_default();
|
|
|
|
let mut loose_size_kb = 0u64;
|
|
let mut pack_size_kb = 0u64;
|
|
let mut garbage_size_kb = 0u64;
|
|
|
|
for line in text.lines() {
|
|
let line = line.trim();
|
|
if let Some(val) = line.strip_prefix("size: ") {
|
|
loose_size_kb = val.trim().parse().unwrap_or(0);
|
|
} else if let Some(val) = line.strip_prefix("size-pack: ") {
|
|
pack_size_kb = val.trim().parse().unwrap_or(0);
|
|
} else if let Some(val) = line.strip_prefix("size-garbage: ") {
|
|
garbage_size_kb = val.trim().parse().unwrap_or(0);
|
|
}
|
|
}
|
|
|
|
// count-objects reports sizes in KiB; convert to bytes
|
|
(loose_size_kb + pack_size_kb + garbage_size_kb) * 1024
|
|
}
|
|
|
|
fn count_refs(gb: &crate::bare::GitBare) -> u64 {
|
|
let out = git_cmd(gb, &["for-each-ref", "--format=%(refname)"]).ok();
|
|
out.map(|o| {
|
|
String::from_utf8_lossy(&o.stdout)
|
|
.lines()
|
|
.filter(|l| !l.is_empty())
|
|
.count() as u64
|
|
})
|
|
.unwrap_or(0)
|
|
}
|
|
|
|
fn file_len(path: &std::path::Path) -> u64 {
|
|
std::fs::metadata(path).map(|m| m.len()).unwrap_or(0)
|
|
}
|
|
|
|
pub(crate) fn get_statistics(gb: &crate::bare::GitBare) -> RepositoryStatistics {
|
|
let size_bytes = dir_size(gb);
|
|
|
|
let mut loose_object_count: u64 = 0;
|
|
let mut packed_object_count: u64 = 0;
|
|
let mut packfile_count: u64 = 0;
|
|
if let Ok(out) = git_cmd(gb, &["count-objects", "-v"]) {
|
|
for line in String::from_utf8_lossy(&out.stdout).lines() {
|
|
let line = line.trim();
|
|
if let Some(v) = line.strip_prefix("count: ") {
|
|
loose_object_count = v.trim().parse().unwrap_or(0);
|
|
} else if let Some(v) = line.strip_prefix("in-pack: ") {
|
|
packed_object_count = v.trim().parse().unwrap_or(0);
|
|
} else if let Some(v) = line.strip_prefix("packs: ") {
|
|
packfile_count = v.trim().parse().unwrap_or(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
let reference_count = count_refs(gb);
|
|
let commit_graph_size_bytes = file_len(&gb.bare_dir.join("objects/info/commit-graph"));
|
|
let multi_pack_index_size_bytes = file_len(&gb.bare_dir.join("objects/pack/multi-pack-index"));
|
|
|
|
RepositoryStatistics {
|
|
size_bytes,
|
|
loose_object_count,
|
|
packed_object_count,
|
|
packfile_count,
|
|
reference_count,
|
|
commit_graph_size_bytes,
|
|
multi_pack_index_size_bytes,
|
|
}
|
|
}
|
|
|
|
pub(crate) fn check_health(
|
|
gb: &crate::bare::GitBare,
|
|
connectivity_only: bool,
|
|
) -> Result<RepositoryHealthResponse, tonic::Status> {
|
|
tracing::info!(
|
|
repo = %gb.bare_dir.display(),
|
|
connectivity_only = connectivity_only,
|
|
"running health check"
|
|
);
|
|
let mut args: Vec<&str> = vec!["fsck"];
|
|
if connectivity_only {
|
|
args.push("--connectivity-only");
|
|
}
|
|
let out = git_cmd(gb, &args)?;
|
|
let text = String::from_utf8_lossy(&out.stdout);
|
|
let mut warnings = Vec::new();
|
|
let mut errors = Vec::new();
|
|
for line in text.lines() {
|
|
if line.starts_with("error:") {
|
|
errors.push(line.to_string());
|
|
} else if line.starts_with("warning:") {
|
|
warnings.push(line.to_string());
|
|
}
|
|
}
|
|
Ok(RepositoryHealthResponse {
|
|
ok: true,
|
|
warnings,
|
|
errors,
|
|
statistics: None,
|
|
})
|
|
}
|
|
|
|
pub(crate) fn run_gc(
|
|
gb: &crate::bare::GitBare,
|
|
prune: bool,
|
|
aggressive: bool,
|
|
) -> Result<RepositoryMaintenanceResponse, tonic::Status> {
|
|
tracing::info!(
|
|
repo = %gb.bare_dir.display(),
|
|
prune = prune,
|
|
aggressive = aggressive,
|
|
"running garbage collection"
|
|
);
|
|
let mut args: Vec<&str> = vec!["gc"];
|
|
if prune {
|
|
args.push("--prune=now");
|
|
}
|
|
if aggressive {
|
|
args.push("--aggressive");
|
|
}
|
|
let out = git_cmd(gb, &args)?;
|
|
Ok(maintenance_response(out))
|
|
}
|
|
|
|
pub(crate) fn run_repack(
|
|
gb: &crate::bare::GitBare,
|
|
full: bool,
|
|
write_bitmaps: bool,
|
|
write_multi_pack_index: bool,
|
|
) -> Result<RepositoryMaintenanceResponse, tonic::Status> {
|
|
tracing::info!(
|
|
repo = %gb.bare_dir.display(),
|
|
full = full,
|
|
write_bitmaps = write_bitmaps,
|
|
write_multi_pack_index = write_multi_pack_index,
|
|
"running repack"
|
|
);
|
|
let mut args: Vec<&str> = vec!["repack", "-d"];
|
|
if full {
|
|
args.push("-a");
|
|
}
|
|
if write_bitmaps {
|
|
args.push("--write-bitmap-index");
|
|
}
|
|
if write_multi_pack_index {
|
|
args.push("--write-midx");
|
|
}
|
|
let out = git_cmd(gb, &args)?;
|
|
Ok(maintenance_response(out))
|
|
}
|
|
|
|
pub(crate) fn run_commit_graph_write(
|
|
gb: &crate::bare::GitBare,
|
|
split: bool,
|
|
replace: bool,
|
|
) -> Result<RepositoryMaintenanceResponse, tonic::Status> {
|
|
tracing::info!(
|
|
repo = %gb.bare_dir.display(),
|
|
split = split,
|
|
replace = replace,
|
|
"writing commit-graph"
|
|
);
|
|
let mut args: Vec<&str> = vec!["commit-graph", "write"];
|
|
if split {
|
|
args.push("--split");
|
|
}
|
|
if !replace {
|
|
args.push("--append");
|
|
}
|
|
let out = git_cmd(gb, &args)?;
|
|
Ok(maintenance_response(out))
|
|
}
|