feat: schema version tracking, SHA256 integrity, setup scripts, bug fixes

This commit is contained in:
Accusys
2026-05-15 18:06:36 +08:00
parent 0e73d2a2ce
commit c41f7e0c6e
567 changed files with 55195 additions and 24 deletions

View File

@@ -1,5 +1,6 @@
use anyhow::{Context, Result};
use libc;
use std::collections::HashMap;
use std::path::PathBuf;
use std::process::Stdio;
use std::time::Duration;
@@ -45,6 +46,41 @@ impl RetryConfig {
}
}
/// Load SHA256 checksums from checksums.sha256 manifest.
/// Returns a map of relative path → expected hash.
fn load_checksums(scripts_dir: &PathBuf) -> HashMap<String, String> {
let path = scripts_dir.join("checksums.sha256");
let mut map = HashMap::new();
match std::fs::read_to_string(&path) {
Ok(content) => {
for line in content.lines() {
let line = line.trim();
if line.is_empty() {
continue;
}
if let Some((hash, rel_path)) = line.split_once(' ') {
let hash = hash.trim().to_string();
let rel_path = rel_path.trim().to_string();
if !hash.is_empty() && !rel_path.is_empty() {
map.insert(rel_path, hash);
}
}
}
tracing::info!(
"[CHECKSUMS] Loaded {} entries from checksums.sha256",
map.len()
);
}
Err(e) => {
tracing::warn!(
"[CHECKSUMS] Could not load checksums.sha256: {} — integrity checks disabled",
e
);
}
}
map
}
pub fn validate_python_env() -> Result<()> {
let manifest = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let venv_python = manifest.join("venv").join("bin").join("python");
@@ -85,6 +121,7 @@ pub fn validate_python_env() -> Result<()> {
pub struct PythonExecutor {
venv_python: PathBuf,
scripts_dir: PathBuf,
checksums: HashMap<String, String>,
}
impl PythonExecutor {
@@ -104,12 +141,47 @@ impl PythonExecutor {
anyhow::bail!("Scripts directory not found at {:?}", scripts_dir);
}
// Load SHA256 checksums manifest
let checksums = load_checksums(&scripts_dir);
Ok(Self {
venv_python,
scripts_dir,
checksums,
})
}
/// Verify a script's SHA256 against the checksums manifest before execution.
fn verify_script_integrity(&self, script_name: &str) -> Result<()> {
let script_path = self.scripts_dir.join(script_name);
let rel_path = format!("./{}", script_name);
if let Some(expected_hash) = self.checksums.get(&rel_path) {
let output = std::process::Command::new("shasum")
.arg("-a").arg("256")
.arg(&script_path)
.output()
.context("Failed to run shasum for integrity check")?;
let actual = String::from_utf8_lossy(&output.stdout);
let actual_hash = actual.split(' ').next().unwrap_or("").trim().to_string();
if actual_hash.is_empty() || actual_hash != *expected_hash {
anyhow::bail!(
"Script integrity check FAILED for {}: hash mismatch\n Expected: {}\n Actual: {}\n Run: bash scripts/setup/check_momentry.sh",
script_name, expected_hash, actual_hash
);
}
tracing::debug!("[INTEGRITY] {} checksum OK", script_name);
} else {
tracing::warn!(
"[INTEGRITY] {} not in checksums.sha256 manifest — skipping verification",
script_name
);
}
Ok(())
}
pub fn validate_env(&self) -> Result<()> {
let rt = tokio::runtime::Runtime::new()?;
let output = rt
@@ -147,6 +219,10 @@ impl PythonExecutor {
anyhow::bail!("Script not found: {:?}", script_path);
}
// Verify script integrity via SHA256 checksum before execution
self.verify_script_integrity(script_name)
.context("Pre-execution integrity check failed — possible version mismatch or corruption")?;
// 標記輸出檔為處理中add .tmp suffix
let output_path = args.get(1).map(|p| std::path::PathBuf::from(p));
let tmp_path = output_path.as_ref().map(|p| {