use std::{env, fs}; use std::path::{PathBuf}; use std::process::Command; use std::io::{Error, ErrorKind}; fn main() { match env::var("MINDWORK_START_DEV_ENV") { Ok(val) => { let is_started_manually = match val.parse::() { Ok(b) => b, Err(_) => { println!("cargo: warning= Invalid value for MINDWORK_START_DEV_ENV: expected 'true' or 'false'"); return; } }; if is_started_manually { if let Err(e) = kill_zombie_qdrant_process(){ println!("cargo:warning=Error: {e}"); return; }; if let Err(e) = delete_old_certificates() { println!("cargo: warning= Failed to delete old certificates: {e}"); } } }, Err(_) => { println!("cargo: warning= The environment variable 'MINDWORK_START_DEV_ENV' was not found."); } } tauri_build::build(); let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap()); println!("cargo:rustc-env=OUT_DIR={}", out_dir.to_str().unwrap()); // // When we are in debug mode, we want to set the current RID // to the current architecture. This is necessary, so that // the developers get the right behavior. // // We read the current OS and architecture and overwrite the // current RID in the metadata file (line #10). // // We have to take care of different naming conventions. The // following RIDs are supported: win-x64, win-arm64, linux-x64, // linux-arm64, osx-x64, osx-arm64. // let current_os = std::env::consts::OS; let current_arch = std::env::consts::ARCH; let rid = match (current_os, current_arch) { ("windows", "x86_64") => "win-x64", ("windows", "aarch64") => "win-arm64", ("linux", "x86_64") => "linux-x64", ("linux", "aarch64") => "linux-arm64", ("macos", "x86_64") => "osx-x64", ("macos", "aarch64") => "osx-arm64", _ => panic!("Unsupported OS or architecture: {current_os} {current_arch}"), }; let metadata = include_str!("../metadata.txt"); let mut metadata_lines = metadata.lines().collect::>(); metadata_lines[9] = rid; let new_metadata = metadata_lines.join("\n"); std::fs::write("../metadata.txt", new_metadata).unwrap(); // // Read the current version and update the // Rust and Tauri configuration files: // let version = metadata_lines[0]; update_cargo_toml("Cargo.toml", version); update_tauri_conf("tauri.conf.json", version); } fn update_cargo_toml(cargo_path: &str, version: &str) { let cargo_toml = std::fs::read_to_string(cargo_path).unwrap(); let cargo_toml_lines = cargo_toml.lines(); let mut new_cargo_toml = String::new(); for line in cargo_toml_lines { if line.starts_with("version = ") { new_cargo_toml.push_str(&format!("version = \"{version}\"")); } else { new_cargo_toml.push_str(line); } new_cargo_toml.push('\n'); } std::fs::write(cargo_path, new_cargo_toml).unwrap(); } fn update_tauri_conf(tauri_conf_path: &str, version: &str) { let tauri_conf = std::fs::read_to_string(tauri_conf_path).unwrap(); let tauri_conf_lines = tauri_conf.lines(); let mut new_tauri_conf = String::new(); for line in tauri_conf_lines { // The version in Tauri's config is formatted like this: // "version": "0.1.0-alpha.0" // Please notice, that the version number line might have a leading tab, etc. if line.contains("\"version\": ") { new_tauri_conf.push_str(&format!("\t\"version\": \"{version}\"")); } else { new_tauri_conf.push_str(line); } new_tauri_conf.push('\n'); } std::fs::write(tauri_conf_path, new_tauri_conf).unwrap(); } #[cfg(unix)] pub fn ensure_process_killed(pid: u32, expected_name: &str) -> Result<(), Error> { // // Check if PID exists and name matches // let ps_output = Command::new("ps") .arg("-p") .arg(pid.to_string()) .arg("-o") .arg("comm=") .output()?; let output = String::from_utf8_lossy(&ps_output.stdout).trim().to_string(); if output.is_empty() { // Process doesn't exist return Ok(()); } let name = output; if name != expected_name { return Err(Error::new(ErrorKind::InvalidInput, "Process name does not match")); } // // Kill the process // let kill_output = Command::new("kill") .arg("-9") .arg(pid.to_string()) .output()?; if !kill_output.status.success() { return Err(Error::new(ErrorKind::Other, "Failed to kill process")); } // // Verify process is killed // let ps_check = Command::new("ps") .arg("-p") .arg(pid.to_string()) .output()?; let output = String::from_utf8_lossy(&ps_check.stdout).trim().to_string(); if output.is_empty() { Ok(()) } else { Err(Error::new(ErrorKind::Other, "Process still running after kill attempt")) } } #[cfg(windows)] pub fn ensure_process_killed(pid: u32, expected_name: &str) -> Result<(), Error> { // // Check if PID exists and name matches // let tasklist_output = Command::new("tasklist") .arg("/FI") .arg(format!("PID eq {}", pid)) .arg("/FO") .arg("CSV") .arg("/NH") .output()?; let output = String::from_utf8_lossy(&tasklist_output.stdout).trim().to_string(); if output.is_empty() || !output.starts_with('"') { println!("cargo:warning= Pid file was found, but process was not."); return Ok(()) } let name = output.split(',').next().unwrap_or("").trim_matches('"'); if name != expected_name { return Err(Error::new(ErrorKind::InvalidInput, format!("Process name does not match. Expected:{}, got:{}",expected_name,name))); } // // Kill the process // let kill_output = Command::new("taskkill") .arg("/PID") .arg(pid.to_string()) .arg("/F") .arg("/T") .output()?; if !kill_output.status.success() { return Err(Error::new(ErrorKind::Other, "Failed to kill process")); } // // Verify process is killed // let tasklist_check = Command::new("tasklist") .arg("/FI") .arg(format!("PID eq {}", pid)) .output()?; let output = String::from_utf8_lossy(&tasklist_check.stdout).trim().to_string(); if output.is_empty() || !output.starts_with('"') { Ok(()) } else { Err(Error::new(ErrorKind::Other, "Process still running after kill attempt")) } } pub fn kill_zombie_qdrant_process() -> Result<(), Error> { let pid_file = dirs::data_local_dir() .expect("Local appdata was not found") .join("com.github.mindwork-ai.ai-studio") .join("data") .join("databases") .join("qdrant") .join("qdrant.pid"); if !pid_file.exists() { return Ok(()); } let pid_str = fs::read_to_string(&pid_file)?; let pid: u32 = pid_str.trim().parse().map_err(|_| {Error::new(ErrorKind::InvalidData, "Invalid PID in file")})?; if let Err(e) = ensure_process_killed(pid, "qdrant.exe".as_ref()){ return Err(e); } fs::remove_file(&pid_file)?; println!("cargo:warning= Killed qdrant process and deleted redundant Pid file: {}", pid_file.display()); Ok(()) } pub fn delete_old_certificates() -> Result<(), Box> { let dir_path = dirs::data_local_dir() .expect("Local appdata was not found") .join("com.github.mindwork-ai.ai-studio") .join("data") .join("databases") .join("qdrant"); if !dir_path.exists() { return Ok(()); } for entry in fs::read_dir(dir_path)? { let entry = entry?; let path = entry.path(); if path.is_dir() { let file_name = entry.file_name(); let folder_name = file_name.to_string_lossy(); if folder_name.starts_with("cert-") { fs::remove_dir_all(&path)?; println!("cargo: warning= Removed old certificates in: {}", path.display()); } } } Ok(()) }