SNAPKITTYWEST's picture
chore: push full sov-kernel-monster content from local build
9425aed verified
Raw
History Blame Contribute Delete
8.61 kB
use anyhow::{Context, Result};
use sha2::{Digest, Sha256};
use std::process::Command;
use crate::bucket::{Author, FileEntry, MemoryBucket, WormSeal};
/// Extract memory buckets from a git commit range.
pub fn extract_range(repo: &str, from: Option<&str>, to: Option<&str>) -> Result<Vec<MemoryBucket>> {
let to_hash = to.unwrap_or("HEAD");
let range = match from {
Some(f) => format!("{}..{}", f, to_hash),
None => to_hash.to_string(),
};
let output = Command::new("git")
.arg("-C")
.arg(repo)
.arg("log")
.arg("--format=%H|%P|%an|%ae|%ai|%s")
.arg(&range)
.output()
.context("Failed to run git log")?;
if !output.status.success() {
anyhow::bail!("git log failed: {}", String::from_utf8_lossy(&output.stderr));
}
let stdout = String::from_utf8(output.stdout)?;
let mut buckets = Vec::new();
for (i, line) in stdout.lines().enumerate() {
let parts: Vec<&str> = line.splitn(6, '|').collect();
if parts.len() < 6 {
continue;
}
let hash = parts[0];
let parent = if parts[1].is_empty() { "" } else { parts[1].split_whitespace().next().unwrap_or("") };
let author_name = parts[2];
let _author_email = parts[3];
let timestamp = parts[4];
let message = parts[5];
// Get diff stat
let diff_stat = get_diff_stat(repo, hash)?;
// Parse conventional commit message
let parsed = parse_conventional_commit(message);
// Get branch
let branch = get_branch(repo, hash)?;
// Generate bucket ID
let id = format!("mem_{:06}", i);
// Compute file entries
let files = compute_file_entries(&diff_stat);
// Create the bucket
let bucket = MemoryBucket {
id: id.clone(),
git_hash: hash.to_string(),
parent_hash: parent.to_string(),
timestamp: timestamp.to_string(),
author: Author {
id: author_name.to_string(),
pubkey: format!("ed25519:{}", author_name),
},
branch,
bucket_type: parsed.bucket_type,
summary: parsed.summary,
keywords: parsed.keywords,
entities: extract_entities_from_files(&files),
files,
related: parsed.related,
trust: "pending".to_string(),
immutable: true,
worm_seal: WormSeal {
algorithm: "Ed25519".to_string(),
signature: String::new(),
signer: "Plasma_Gate".to_string(),
audit_ref: uuid::Uuid::new_v4().to_string(),
},
};
buckets.push(bucket);
}
Ok(buckets)
}
fn get_diff_stat(repo: &str, hash: &str) -> Result<String> {
let output = Command::new("git")
.arg("-C")
.arg(repo)
.arg("diff")
.arg("--stat")
.arg(format!("{}~1", hash))
.arg(hash)
.output()
.context("Failed to get diff stat")?;
Ok(String::from_utf8(output.stdout)?)
}
fn get_branch(repo: &str, hash: &str) -> Result<String> {
let output = Command::new("git")
.arg("-C")
.arg(repo)
.arg("branch")
.arg("--contains")
.arg(hash)
.output()
.context("Failed to get branch")?;
let stdout = String::from_utf8(output.stdout)?;
for line in stdout.lines() {
let line = line.trim().trim_start_matches('*');
if !line.is_empty() {
return Ok(line.to_string());
}
}
Ok("main".to_string())
}
struct ParsedCommit {
bucket_type: String,
summary: String,
keywords: Vec<String>,
related: Vec<String>,
breaking: bool,
}
fn parse_conventional_commit(message: &str) -> ParsedCommit {
let lines: Vec<&str> = message.lines().collect();
let first_line = lines.first().unwrap_or(&"");
let (bucket_type, summary) = if let Some(colon_pos) = first_line.find(':') {
let before_colon = &first_line[..colon_pos];
let after_colon = first_line[colon_pos + 1..].trim();
let (type_part, scope) = if let Some(paren_start) = before_colon.find('(') {
if let Some(paren_end) = before_colon.find(')') {
(&before_colon[..paren_start], Some(&before_colon[paren_start + 1..paren_end]))
} else {
(before_colon, None)
}
} else {
(before_colon, None)
};
let bucket_type = match type_part {
"feat" => "implementation",
"fix" => "repair",
"docs" => "architecture",
"style" => "architecture",
"refactor" => "implementation",
"perf" => "implementation",
"test" => "audit",
"build" => "architecture",
"ci" => "audit",
"chore" => "audit",
"revert" => "repair",
"security" => "audit",
"decision" => "decision",
"audit" => "audit",
"fiscal" => "fiscal_decision",
_ => "audit",
};
let summary = match scope {
Some(s) => format!("{}: {}", s, after_colon),
None => after_colon.to_string(),
};
(bucket_type.to_string(), summary)
} else {
("audit".to_string(), first_line.to_string())
};
let breaking = message.contains("BREAKING CHANGE:") || message.contains("BREAKING:");
let keywords: Vec<String> = summary
.split_whitespace()
.map(|w| w.to_lowercase())
.filter(|w| !STOP_WORDS.contains(&w.as_str()))
.collect();
let related: Vec<String> = message
.lines()
.filter(|l| l.starts_with("Refs:") || l.starts_with("Ref:"))
.flat_map(|l| {
l.split_whitespace()
.filter(|w| w.starts_with('#'))
.map(|w| format!("issue_{}", &w[1..]))
.collect::<Vec<_>>()
})
.collect();
ParsedCommit {
bucket_type,
summary,
keywords,
related,
breaking,
}
}
fn compute_file_entries(diff_stat: &str) -> Vec<FileEntry> {
diff_stat
.lines()
.filter(|l| l.contains('|'))
.filter_map(|l| {
let parts: Vec<&str> = l.splitn(2, '|').collect();
if parts.len() == 2 {
let path = parts[0].trim().to_string();
let role = classify_file_role(&path);
let diff_hash = format!("{:x}", Sha256::digest(parts[1].as_bytes()));
Some(FileEntry { path, role, diff_hash })
} else {
None
}
})
.collect()
}
fn classify_file_role(path: &str) -> String {
if path.ends_with(".rs") || path.ends_with(".cpp") || path.ends_with(".hs") || path.ends_with(".lean") {
if path.contains("test") || path.contains("_test.") {
"test".to_string()
} else {
"core".to_string()
}
} else if path.ends_with(".md") || path.ends_with(".txt") {
"doc".to_string()
} else if path.ends_with(".toml")
|| path.ends_with(".json")
|| path.ends_with(".yml")
|| path.ends_with(".yaml")
|| path.ends_with(".pl")
{
"config".to_string()
} else if path.ends_with(".new") {
"new".to_string()
} else {
"modified".to_string()
}
}
fn extract_entities_from_files(files: &[FileEntry]) -> Vec<String> {
let mut entities = Vec::new();
for file in files {
if let Some(name) = file.path.rsplit('/').next() {
if let Some(stem) = name.split('.').next() {
let entity = stem.to_lowercase();
if !entities.contains(&entity) {
entities.push(entity);
}
}
}
}
entities
}
const STOP_WORDS: &[&str] = &[
"the", "a", "an", "is", "are", "was", "were", "be", "been", "being",
"have", "has", "had", "do", "does", "did", "will", "would", "could",
"should", "may", "might", "shall", "can", "to", "of", "in", "for",
"on", "with", "at", "by", "from", "as", "and", "but", "or", "not",
"so", "if", "when", "while", "this", "that", "it", "its",
];