From 7046da02f2fb5aacda73ae256df7f45017a5c7ff Mon Sep 17 00:00:00 2001 From: krVatsal Date: Sun, 8 Mar 2026 20:58:30 +0530 Subject: [PATCH 1/2] add conditional generation of temp file for powershell --- tools/third-party-licenses/src/cargo.rs | 33 +++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tools/third-party-licenses/src/cargo.rs b/tools/third-party-licenses/src/cargo.rs index 7b10a4fa43..82c05fe9d5 100644 --- a/tools/third-party-licenses/src/cargo.rs +++ b/tools/third-party-licenses/src/cargo.rs @@ -85,12 +85,45 @@ fn parse(parsed: Output) -> Vec { } fn run() -> Result { + // Try normal stdout capture first let output = Command::new("cargo") .args(["about", "generate", "--format", "json", "--frozen"]) .current_dir(env!("CARGO_WORKSPACE_DIR")) .output() .map_err(|e| Error::Io(e, "Failed to run cargo about generate".into()))?; + // On Windows, if cargo-about fails (often due to PowerShell detection in process ancestry), + // fall back to using a temporary file to work around the issue. + // TODO: Add an option to cargo-about to disable the PowerShell check (see issue: https://discord.com/channels/731730685944922173/731738914812854303/1479960786871779459) + #[cfg(target_os = "windows")] + if !output.status.success() { + eprintln!("cargo-about failed with stdout capture, retrying with temporary file..."); + + let temp_file = PathBuf::from(env!("CARGO_WORKSPACE_DIR")).join(".cargo-about-temp.json"); + + let status = Command::new("cargo") + .args(["about", "generate", "--format", "json", "--frozen", "--output-file"]) + .arg(&temp_file) + .current_dir(env!("CARGO_WORKSPACE_DIR")) + .status() + .map_err(|e| Error::Io(e, "Failed to run cargo about generate with temp file".into()))?; + + if !status.success() { + return Err(Error::Command(format!( + "cargo about generate failed:\nOriginal error: {}\nTemp file error: command exited with non-zero status", + String::from_utf8_lossy(&output.stderr) + ))); + } + + let json_content = fs::read_to_string(&temp_file).map_err(|e| Error::Io(e, format!("Failed to read cargo about output from {}", temp_file.display())))?; + + // Clean up temp file + let _ = fs::remove_file(&temp_file); + + return serde_json::from_str(&json_content).map_err(|e| Error::Json(e, "Failed to parse cargo about generate JSON from temp file".into())); + } + + // Handle other error cases if !output.status.success() { return Err(Error::Command(format!("cargo about generate failed:\n{}", String::from_utf8_lossy(&output.stderr)))); } From 1106ad43bb6460e79aeb2cc0a076d101895755ee Mon Sep 17 00:00:00 2001 From: krVatsal Date: Sun, 8 Mar 2026 22:47:27 +0530 Subject: [PATCH 2/2] Fix cargo-about PowerShell compatibility with unique temp files and automatic cleanup --- tools/third-party-licenses/src/cargo.rs | 30 +++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/tools/third-party-licenses/src/cargo.rs b/tools/third-party-licenses/src/cargo.rs index 82c05fe9d5..084b5d837a 100644 --- a/tools/third-party-licenses/src/cargo.rs +++ b/tools/third-party-licenses/src/cargo.rs @@ -4,6 +4,7 @@ use std::fs; use std::hash::{Hash, Hasher}; use std::path::PathBuf; use std::process::Command; +use std::time::{SystemTime, UNIX_EPOCH}; pub struct CargoLicenseSource {} @@ -84,6 +85,23 @@ fn parse(parsed: Output) -> Vec { .collect() } +// Guard to ensure temp file cleanup even on early returns or panics +struct TempFileGuard { + path: PathBuf, +} + +impl TempFileGuard { + fn new(path: PathBuf) -> Self { + Self { path } + } +} + +impl Drop for TempFileGuard { + fn drop(&mut self) { + let _ = fs::remove_file(&self.path); + } +} + fn run() -> Result { // Try normal stdout capture first let output = Command::new("cargo") @@ -99,7 +117,14 @@ fn run() -> Result { if !output.status.success() { eprintln!("cargo-about failed with stdout capture, retrying with temporary file..."); - let temp_file = PathBuf::from(env!("CARGO_WORKSPACE_DIR")).join(".cargo-about-temp.json"); + // Generate unique temp filename to avoid race conditions + let timestamp = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_nanos(); + let pid = std::process::id(); + let temp_filename = format!(".cargo-about-temp-{}-{}.json", pid, timestamp); + let temp_file = PathBuf::from(env!("CARGO_WORKSPACE_DIR")).join(&temp_filename); + + // Ensure cleanup even if we return early or panic + let _guard = TempFileGuard::new(temp_file.clone()); let status = Command::new("cargo") .args(["about", "generate", "--format", "json", "--frozen", "--output-file"]) @@ -117,9 +142,6 @@ fn run() -> Result { let json_content = fs::read_to_string(&temp_file).map_err(|e| Error::Io(e, format!("Failed to read cargo about output from {}", temp_file.display())))?; - // Clean up temp file - let _ = fs::remove_file(&temp_file); - return serde_json::from_str(&json_content).map_err(|e| Error::Json(e, "Failed to parse cargo about generate JSON from temp file".into())); }