From c02b3967dfe3d879d1a210b09448b604c7a2e0eb Mon Sep 17 00:00:00 2001 From: zipg Date: Mon, 13 Jul 2026 22:20:58 +0800 Subject: [PATCH] fix(agent): import JRE from offline bundle --- crates/dbx-core/src/agent_service.rs | 9 ++- crates/dbx-core/tests/agent_service.rs | 78 ++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/crates/dbx-core/src/agent_service.rs b/crates/dbx-core/src/agent_service.rs index 1e99f67bc3..7e19717d46 100644 --- a/crates/dbx-core/src/agent_service.rs +++ b/crates/dbx-core/src/agent_service.rs @@ -1239,6 +1239,7 @@ pub fn import_offline_zip( let registry = read_registry_from_zip(&mut archive)?; let platform = AgentManager::current_platform(); + std::fs::create_dir_all(am.base_dir()).map_err(|e| format!("Failed to create agent directory: {e}"))?; let mut local_state = am.load_state(); let mut result = OfflineImportResult { jre_installed: Vec::new(), drivers_installed: Vec::new(), drivers_skipped: Vec::new() }; @@ -1375,7 +1376,7 @@ fn read_registry_from_zip(archive: &mut zip::ZipArchive) -> Resul fn extract_jre_key_from_filename(name: &str) -> Option { let filename = name.rsplit('/').next()?; - let rest = filename.strip_prefix("jre-")?; + let rest = filename.strip_prefix("dbx-jre-").or_else(|| filename.strip_prefix("jre-"))?; let key = rest.split('-').next()?; if key.is_empty() { return None; @@ -1442,6 +1443,12 @@ mod agent_download_url_tests { "agents/drivers/dbx-agent-h2.jar?mirror=r2&v=0.5.33" ); } + + #[test] + fn offline_jre_filename_parser_accepts_release_and_legacy_names() { + assert_eq!(extract_jre_key_from_filename("jre/dbx-jre-21-macos-aarch64.tar.gz").as_deref(), Some("21")); + assert_eq!(extract_jre_key_from_filename("jre/jre-21-macos-aarch64.tar.gz").as_deref(), Some("21")); + } } #[cfg(test)] diff --git a/crates/dbx-core/tests/agent_service.rs b/crates/dbx-core/tests/agent_service.rs index dbec2fd1df..953f9bd062 100644 --- a/crates/dbx-core/tests/agent_service.rs +++ b/crates/dbx-core/tests/agent_service.rs @@ -491,6 +491,26 @@ fn offline_zip_import_emits_progress_and_updates_state() { assert!(events.iter().any(|event| event.step == "driver" && event.db_type.as_deref() == Some("H2"))); } +#[test] +fn offline_zip_import_installs_release_named_jre() { + let manager = test_manager("offline-release-jre"); + let zip_path = test_path("offline-release-jre-zip").join("agents.zip"); + std::fs::create_dir_all(zip_path.parent().unwrap()).unwrap(); + write_offline_driver_zip_with_jre(&zip_path, "h2", "0.2.0", "21.0.12"); + let events = std::sync::Mutex::new(Vec::new()); + + let result = import_agents_from_zip(&manager, &zip_path, |event| { + events.lock().unwrap().push(event); + }) + .unwrap(); + + assert_eq!(result.jre_installed, vec![DEFAULT_JRE_KEY]); + assert_eq!(result.drivers_installed, vec!["h2"]); + assert!(manager.is_jre_installed(DEFAULT_JRE_KEY)); + assert_eq!(manager.load_state().jre_versions.get(DEFAULT_JRE_KEY).map(String::as_str), Some("21.0.12")); + assert!(events.lock().unwrap().iter().any(|event| event.step == "jre-extract")); +} + #[test] fn offline_zip_import_rejects_corrupt_jar() { let manager = test_manager("offline-corrupt-driver"); @@ -509,6 +529,64 @@ fn write_offline_driver_zip(path: &std::path::Path, db_type: &str, version: &str write_offline_driver_zip_with_jar(path, db_type, version, test_agent_jar_bytes()); } +fn write_offline_driver_zip_with_jre(path: &std::path::Path, db_type: &str, version: &str, jre_version: &str) { + let file = std::fs::File::create(path).unwrap(); + let mut zip = zip::ZipWriter::new(file); + let options = zip::write::SimpleFileOptions::default().compression_method(zip::CompressionMethod::Stored); + let jar = test_agent_jar_bytes(); + let jre_archive = test_jre_archive_bytes(); + let registry = serde_json::json!({ + "jres": { + DEFAULT_JRE_KEY: { + "version": jre_version, + "platforms": {} + } + }, + "drivers": { + db_type: { + "version": version, + "label": db_type, + "min_app_version": "0.1.0", + "jre": DEFAULT_JRE_KEY, + "jar": { "url": format!("https://example.com/dbx-agent-{db_type}.jar"), "size": jar.len() } + } + } + }); + + zip.start_file("agent-registry.json", options).unwrap(); + std::io::Write::write_all(&mut zip, registry.to_string().as_bytes()).unwrap(); + zip.start_file(format!("jre/dbx-jre-{DEFAULT_JRE_KEY}-{}.tar.gz", AgentManager::current_platform()), options) + .unwrap(); + std::io::Write::write_all(&mut zip, &jre_archive).unwrap(); + zip.start_file(format!("drivers/dbx-agent-{db_type}.jar"), options).unwrap(); + std::io::Write::write_all(&mut zip, &jar).unwrap(); + zip.finish().unwrap(); +} + +fn test_jre_archive_bytes() -> Vec { + 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(); + std::fs::remove_dir_all(root).ok(); + bytes +} + fn write_offline_driver_zip_with_jar(path: &std::path::Path, db_type: &str, version: &str, jar: Vec) { let file = std::fs::File::create(path).unwrap(); let mut zip = zip::ZipWriter::new(file);