fix(agent): remove system archive tool dependency

This commit is contained in:
zipg 2026-07-28 13:21:20 +08:00 committed by GitHub
parent 054702e223
commit 2980e32c9d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 104 additions and 26 deletions

2
Cargo.lock generated
View File

@ -1891,6 +1891,7 @@ dependencies = [
"deadpool-postgres",
"duckdb",
"encoding_rs",
"flate2",
"font-kit",
"futures",
"iana-time-zone",
@ -1920,6 +1921,7 @@ dependencies = [
"sqlparser",
"ssfmt",
"sysinfo",
"tar",
"tempfile",
"tiberius",
"tokio",

View File

@ -95,5 +95,7 @@ async-trait = "0.1"
bytes = "1"
font-kit = { version = "0.14.3", optional = true }
zip = { version = "4", default-features = false, features = ["deflate"] }
flate2 = "1"
tar = "0.4"
sysinfo = { version = "0.32", features = ["system"] }
tempfile = "3"

View File

@ -1724,17 +1724,102 @@ fn db_type_for_jar_offline_entry(registry: &AgentRegistry, name: &str) -> Option
}
fn extract_tar_gz(archive: &Path, dest: &Path) -> Result<(), String> {
std::fs::create_dir_all(dest).map_err(|e| e.to_string())?;
let status = crate::process::new_std_command("tar")
.args(["xzf", &archive.to_string_lossy(), "-C", &dest.to_string_lossy(), "--strip-components=1"])
.status()
.map_err(|e| format!("Failed to extract archive: {e}"))?;
if !status.success() {
return Err("Failed to extract JRE archive".to_string());
let parent = dest.parent().ok_or_else(|| format!("Invalid JRE destination: {}", dest.display()))?;
std::fs::create_dir_all(parent).map_err(|e| format!("Failed to create JRE directory: {e}"))?;
let staging = tempfile::Builder::new()
.prefix(".jre-extract-")
.tempdir_in(parent)
.map_err(|e| format!("Failed to create JRE extraction directory: {e}"))?;
let file = std::fs::File::open(archive).map_err(|e| format!("Failed to open JRE archive: {e}"))?;
let decoder = flate2::read::GzDecoder::new(file);
tar::Archive::new(decoder).unpack(staging.path()).map_err(|e| format!("Failed to extract JRE archive: {e}"))?;
let mut roots = std::fs::read_dir(staging.path())
.map_err(|e| format!("Failed to inspect extracted JRE archive: {e}"))?
.collect::<Result<Vec<_>, _>>()
.map_err(|e| format!("Failed to inspect extracted JRE archive: {e}"))?;
if roots.len() != 1 {
return Err("Invalid JRE archive: expected a single top-level directory".to_string());
}
let root = roots.pop().expect("root count checked above");
if !root.file_type().map_err(|e| format!("Failed to inspect extracted JRE archive: {e}"))?.is_dir() {
return Err("Invalid JRE archive: expected a top-level directory".to_string());
}
std::fs::create_dir_all(dest).map_err(|e| format!("Failed to create JRE directory: {e}"))?;
for entry in std::fs::read_dir(root.path()).map_err(|e| format!("Failed to inspect extracted JRE archive: {e}"))? {
let entry = entry.map_err(|e| format!("Failed to inspect extracted JRE archive: {e}"))?;
std::fs::rename(entry.path(), dest.join(entry.file_name()))
.map_err(|e| format!("Failed to install extracted JRE: {e}"))?;
}
Ok(())
}
#[cfg(test)]
mod jre_archive_tests {
use super::*;
use std::io::Cursor;
fn append_file(
builder: &mut tar::Builder<flate2::write::GzEncoder<std::fs::File>>,
path: &str,
data: &[u8],
mode: u32,
) {
let mut header = tar::Header::new_gnu();
header.set_size(data.len() as u64);
header.set_mode(mode);
header.set_cksum();
builder.append_data(&mut header, path, Cursor::new(data)).unwrap();
}
#[test]
fn extracts_jre_archive_without_system_tools_and_strips_top_level_directory() {
let temp = tempfile::tempdir().unwrap();
let archive_path = temp.path().join("jre.tar.gz");
let encoder = flate2::write::GzEncoder::new(
std::fs::File::create(&archive_path).unwrap(),
flate2::Compression::default(),
);
let mut builder = tar::Builder::new(encoder);
append_file(&mut builder, "jdk-21/bin/java", b"java", 0o755);
append_file(&mut builder, "jdk-21/conf/release", b"JAVA_VERSION=21", 0o644);
builder.into_inner().unwrap().finish().unwrap();
let dest = temp.path().join("managed-jre");
extract_tar_gz(&archive_path, &dest).unwrap();
assert_eq!(std::fs::read(dest.join("bin/java")).unwrap(), b"java");
assert_eq!(std::fs::read(dest.join("conf/release")).unwrap(), b"JAVA_VERSION=21");
assert!(!dest.join("jdk-21").exists());
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
assert_eq!(std::fs::metadata(dest.join("bin/java")).unwrap().permissions().mode() & 0o777, 0o755);
}
}
#[test]
fn rejects_jre_archive_without_a_single_top_level_directory() {
let temp = tempfile::tempdir().unwrap();
let archive_path = temp.path().join("jre.tar.gz");
let encoder = flate2::write::GzEncoder::new(
std::fs::File::create(&archive_path).unwrap(),
flate2::Compression::default(),
);
let mut builder = tar::Builder::new(encoder);
append_file(&mut builder, "jdk-21/bin/java", b"java", 0o755);
append_file(&mut builder, "unexpected/readme.txt", b"unexpected", 0o644);
builder.into_inner().unwrap().finish().unwrap();
let error = extract_tar_gz(&archive_path, &temp.path().join("managed-jre")).unwrap_err();
assert!(error.contains("single top-level directory"), "unexpected error: {error}");
}
}
pub async fn import_agent_driver(am: &AgentManager, db_type: &str, source_path: &Path) -> Result<(), String> {
// Manual imports replace the same artifact paths as downloads. Reuse the
// install operation and per-driver locks so an import cannot race an
@ -2143,13 +2228,10 @@ mod agent_registry_install_tests {
let java_path = payload.join(relative_java_path);
std::fs::create_dir_all(java_path.parent().unwrap()).unwrap();
std::fs::write(java_path, b"java").unwrap();
let archive = archive_root.join("runtime.tar.gz");
let status = crate::process::new_std_command("tar")
.args(["czf", &archive.to_string_lossy(), "-C", &archive_root.to_string_lossy(), "payload"])
.status()
.unwrap();
assert!(status.success());
std::fs::read(archive).unwrap()
let encoder = flate2::write::GzEncoder::new(Vec::new(), flate2::Compression::default());
let mut builder = tar::Builder::new(encoder);
builder.append_dir_all("payload", &payload).unwrap();
builder.into_inner().unwrap().finish().unwrap()
}
fn write_cached_jre_download(am: &AgentManager, jre_key: &str, version: &str, url: &str, archive: &[u8]) {

View File

@ -963,22 +963,14 @@ fn test_jre_archive_bytes() -> Vec<u8> {
let root = test_path("jre-archive");
let runtime_root = root.join("dbx-jre");
let bin_dir = runtime_root.join("bin");
let archive = root.join("jre.tar.gz");
std::fs::create_dir_all(&bin_dir).unwrap();
std::fs::write(bin_dir.join("java"), b"java").unwrap();
std::fs::write(bin_dir.join("java.exe"), b"java").unwrap();
let status = std::process::Command::new("tar")
.arg("czf")
.arg(&archive)
.arg("-C")
.arg(&root)
.arg("dbx-jre")
.status()
.unwrap();
assert!(status.success());
let bytes = std::fs::read(&archive).unwrap();
let encoder = flate2::write::GzEncoder::new(Vec::new(), flate2::Compression::default());
let mut builder = tar::Builder::new(encoder);
builder.append_dir_all("dbx-jre", &runtime_root).unwrap();
let bytes = builder.into_inner().unwrap().finish().unwrap();
std::fs::remove_dir_all(root).ok();
bytes
}