From b82b0f0069900e9c2ec6644d75d6d14809784258 Mon Sep 17 00:00:00 2001 From: Zach Vorhies Date: Wed, 21 Oct 2026 22:34:25 -0700 Subject: [PATCH 1/6] fix(avr8js): stop launcher descendants before draining output --- crates/fbuild-core/src/platform/process.rs | 13 +++ .../src/handlers/emulator/avr8js_headless.rs | 6 +- .../src/handlers/emulator/tests_process.rs | 82 +++++++++++++++++++ 3 files changed, 98 insertions(+), 3 deletions(-) diff --git a/crates/fbuild-core/src/platform/process.rs b/crates/fbuild-core/src/platform/process.rs index 32895fb8f..64deae72a 100644 --- a/crates/fbuild-core/src/platform/process.rs +++ b/crates/fbuild-core/src/platform/process.rs @@ -174,6 +174,19 @@ fn spawn_tokio_contained_here( Ok(child) } +/// Stop a Tokio child and its descendants without waiting for pipe EOF. +/// +/// Launchers such as nodejs-wheel start another process that inherits their +/// output pipes. Kill the tree while the launcher still exists, before its +/// descendants are reparented and can no longer be found from its PID. The +/// caller remains responsible for waiting for the child and draining output. +pub async fn kill_tokio_tree(child: &tokio::process::Child) -> std::io::Result<()> { + if let Some(pid) = child.id() { + running_process::process_tree::kill_tree_async(pid, Duration::ZERO).await?; + } + Ok(()) +} + pub fn pid_is_alive(pid: u32) -> bool { pid != 0 && super::selected::process::pid_is_alive(pid) } diff --git a/crates/fbuild-daemon/src/handlers/emulator/avr8js_headless.rs b/crates/fbuild-daemon/src/handlers/emulator/avr8js_headless.rs index 65e46a49d..8342dba40 100644 --- a/crates/fbuild-daemon/src/handlers/emulator/avr8js_headless.rs +++ b/crates/fbuild-daemon/src/handlers/emulator/avr8js_headless.rs @@ -121,7 +121,7 @@ pub(crate) async fn run_avr8js_headless( loop { if monitor.timed_out() { final_outcome = Some(monitor.timeout_outcome()); - let _ = child.kill().await; + fbuild_core::platform::process::kill_tokio_tree(&child).await?; break; } @@ -147,7 +147,7 @@ pub(crate) async fn run_avr8js_headless( if let Some(outcome) = monitor.process_line(&line.line) { final_outcome = Some(outcome); - let _ = child.kill().await; + fbuild_core::platform::process::kill_tokio_tree(&child).await?; break; } } @@ -164,7 +164,7 @@ pub(crate) async fn run_avr8js_headless( } Err(_) => { final_outcome = Some(monitor.timeout_outcome()); - let _ = child.kill().await; + fbuild_core::platform::process::kill_tokio_tree(&child).await?; break; } } diff --git a/crates/fbuild-daemon/src/handlers/emulator/tests_process.rs b/crates/fbuild-daemon/src/handlers/emulator/tests_process.rs index 79ba707cf..d20b60416 100644 --- a/crates/fbuild-daemon/src/handlers/emulator/tests_process.rs +++ b/crates/fbuild-daemon/src/handlers/emulator/tests_process.rs @@ -248,6 +248,88 @@ async fn run_real_esp32s3_fixture_in_qemu() { // avr8js headless tests (use fake process, no real Node.js needed) // ----------------------------------------------------------------------- +// Exercise the actual AVR8JS runner with a launcher that leaves a descendant +// holding its pipes, like the nodejs-wheel Python entry point does. +async fn avr8js_launcher_stops_descendant( + marker: &str, + success: Option<&str>, + error: Option<&str>, +) { + use super::avr8js_headless::{RunAvr8jsHeadlessOptions, run_avr8js_headless}; + + if fbuild_core::platform::host::is_windows() { + eprintln!("skipping POSIX shell launcher fixture on Windows"); + return; + } + + let temp = tempfile::tempdir_in(fbuild_paths::temp_subdir("daemon-tests")).unwrap(); + let cache = temp.path(); + let module = cache.join("node_modules/avr8js"); + std::fs::create_dir_all(&module).unwrap(); + std::fs::write(module.join("package.json"), "{}").unwrap(); + let script = cache.join("launcher.sh"); + let pid_file = cache.join("child.pid"); + std::fs::write( + &script, + format!( + "sleep 60 &\nprintf '%s' $! > '{}'\nprintf '%s\\n' '{}'\nwait\n", + pid_file.display(), + marker + ), + ) + .unwrap(); + + let hex = cache.join("unused.hex"); + let run = run_avr8js_headless( + std::path::Path::new("/bin/sh"), + &script, + &hex, + 16_000_000, + cache, + RunAvr8jsHeadlessOptions { + timeout_secs: Some(1.0), + halt_on_error: error, + halt_on_success: success, + expect: None, + show_timestamp: false, + verbose: false, + }, + ); + let result = tokio::time::timeout(std::time::Duration::from_secs(5), run).await; + // Clean up even on RED: cancelling the runner must not leave the fixture + // process sleeping for a minute with its output pipes open. + if let Ok(pid) = std::fs::read_to_string(&pid_file) { + let _ = fbuild_core::platform::process::terminate_pid( + pid.trim().parse().unwrap(), + fbuild_core::platform::process::Termination::Force, + ); + } + let result = result + .expect("AVR8JS must stop the launcher and its descendant") + .unwrap(); + match result.outcome { + MonitorOutcome::Success(_) => assert!(success.is_some()), + MonitorOutcome::Error(_) => assert!(error.is_some()), + MonitorOutcome::Timeout { .. } => assert!(success.is_none() && error.is_none()), + other => panic!("unexpected outcome: {other:?}"), + } +} + +#[tokio::test] +async fn avr8js_launcher_success_stops_descendant() { + avr8js_launcher_stops_descendant("PASS", Some("PASS"), None).await; +} + +#[tokio::test] +async fn avr8js_launcher_error_stops_descendant() { + avr8js_launcher_stops_descendant("FAIL", None, Some("FAIL")).await; +} + +#[tokio::test] +async fn avr8js_launcher_timeout_stops_descendant() { + avr8js_launcher_stops_descendant("booting", None, None).await; +} + #[tokio::test] async fn run_avr8js_headless_captures_stdout() { let (exe, args) = test_process_command(&["Hello from AVR!"]); From 6cbd31ef2a52cfeb8ec8fa5c1ccafd3ed0dd4ac6 Mon Sep 17 00:00:00 2001 From: Zach Vorhies Date: Wed, 21 Oct 2026 22:57:07 -0700 Subject: [PATCH 2/6] chore: record local CI validation Local-Gate: v1 tree=01bd7e0cfd1846bda30a4dbc9ce888e4174b6ce1 secs=0 lanes=linux-minimal:reused@a1e91c3e72c6,dylint:reused@8c3f82d7b444 Ci-Attestation: {"at":1792648613,"gate":"general/all/ubuntu-ci-guards","host":"linux-x86_64","key":"a1e91c3e72c6e4927796ad2662ba0e0cfb07a395d4af5b243663bc9271404160","lane":"linux-minimal","parents":["b82b0f0069900e9c2ec6644d75d6d14809784258"],"secs":null,"stamp":"d0c5e79878a7e8c976bd4f0eeba1a704","tree":"01bd7e0cfd1846bda30a4dbc9ce888e4174b6ce1","v":1,"via":"reused"} Ci-Attestation: {"at":1792648613,"gate":"rust/x86_64-unknown-linux-gnu/workspace-clippy","host":"linux-x86_64","key":"a1e91c3e72c6e4927796ad2662ba0e0cfb07a395d4af5b243663bc9271404160","lane":"linux-minimal","parents":["b82b0f0069900e9c2ec6644d75d6d14809784258"],"secs":null,"stamp":"bc63fa4ce5d004c45187802e5093b2ee","tree":"01bd7e0cfd1846bda30a4dbc9ce888e4174b6ce1","v":1,"via":"reused"} Ci-Attestation: {"at":1792648613,"gate":"rust/x86_64-unknown-linux-gnu/workspace-test","host":"linux-x86_64","key":"a1e91c3e72c6e4927796ad2662ba0e0cfb07a395d4af5b243663bc9271404160","lane":"linux-minimal","parents":["b82b0f0069900e9c2ec6644d75d6d14809784258"],"secs":null,"stamp":"a23d8cb797002502724b85c31c0fc678","tree":"01bd7e0cfd1846bda30a4dbc9ce888e4174b6ce1","v":1,"via":"reused"} Ci-Attestation: {"at":1792648613,"gate":"rust/x86_64-unknown-linux-gnu/python-facade-test","host":"linux-x86_64","key":"a1e91c3e72c6e4927796ad2662ba0e0cfb07a395d4af5b243663bc9271404160","lane":"linux-minimal","parents":["b82b0f0069900e9c2ec6644d75d6d14809784258"],"secs":null,"stamp":"669fb0bc3afaae5e63ac3f557f586522","tree":"01bd7e0cfd1846bda30a4dbc9ce888e4174b6ce1","v":1,"via":"reused"} Ci-Attestation: {"at":1792648613,"gate":"general/all/dylint-policy","host":"linux-x86_64","key":"8c3f82d7b444185862f685678094b1f58888844232e502bef9e3feb92b788204","lane":"dylint","parents":["b82b0f0069900e9c2ec6644d75d6d14809784258"],"secs":null,"stamp":"48d733af56cc3696b273167ea69e2347","tree":"01bd7e0cfd1846bda30a4dbc9ce888e4174b6ce1","v":1,"via":"reused"} Ci-Attestation: {"at":1792648613,"gate":"rust/x86_64-unknown-linux-gnu/dylint-library-check","host":"linux-x86_64","key":"8c3f82d7b444185862f685678094b1f58888844232e502bef9e3feb92b788204","lane":"dylint","parents":["b82b0f0069900e9c2ec6644d75d6d14809784258"],"secs":null,"stamp":"3b52746490c0cbd2a0b971faf214ef30","tree":"01bd7e0cfd1846bda30a4dbc9ce888e4174b6ce1","v":1,"via":"reused"} Ci-Attestation: {"at":1792648613,"gate":"rust/x86_64-unknown-linux-gnu/workspace-dylint","host":"linux-x86_64","key":"8c3f82d7b444185862f685678094b1f58888844232e502bef9e3feb92b788204","lane":"dylint","parents":["b82b0f0069900e9c2ec6644d75d6d14809784258"],"secs":null,"stamp":"b9b239219ecb9e01dcf5779c46963a67","tree":"01bd7e0cfd1846bda30a4dbc9ce888e4174b6ce1","v":1,"via":"reused"} From 9dbcd2d267f64a33cb764cbf2047515f677b4125 Mon Sep 17 00:00:00 2001 From: Zach Vorhies Date: Wed, 21 Oct 2026 23:24:30 -0700 Subject: [PATCH 3/6] fix(avr8js): resolve the native Node executable before emulation --- crates/fbuild-core/src/platform/process.rs | 13 --- .../src/handlers/emulator/avr8js_headless.rs | 6 +- .../src/handlers/emulator/avr8js_npm.rs | 30 +++---- .../src/handlers/emulator/tests_npm_cache.rs | 78 ++++++++++++++++++ .../src/handlers/emulator/tests_process.rs | 82 ------------------- 5 files changed, 97 insertions(+), 112 deletions(-) diff --git a/crates/fbuild-core/src/platform/process.rs b/crates/fbuild-core/src/platform/process.rs index 64deae72a..32895fb8f 100644 --- a/crates/fbuild-core/src/platform/process.rs +++ b/crates/fbuild-core/src/platform/process.rs @@ -174,19 +174,6 @@ fn spawn_tokio_contained_here( Ok(child) } -/// Stop a Tokio child and its descendants without waiting for pipe EOF. -/// -/// Launchers such as nodejs-wheel start another process that inherits their -/// output pipes. Kill the tree while the launcher still exists, before its -/// descendants are reparented and can no longer be found from its PID. The -/// caller remains responsible for waiting for the child and draining output. -pub async fn kill_tokio_tree(child: &tokio::process::Child) -> std::io::Result<()> { - if let Some(pid) = child.id() { - running_process::process_tree::kill_tree_async(pid, Duration::ZERO).await?; - } - Ok(()) -} - pub fn pid_is_alive(pid: u32) -> bool { pid != 0 && super::selected::process::pid_is_alive(pid) } diff --git a/crates/fbuild-daemon/src/handlers/emulator/avr8js_headless.rs b/crates/fbuild-daemon/src/handlers/emulator/avr8js_headless.rs index 8342dba40..65e46a49d 100644 --- a/crates/fbuild-daemon/src/handlers/emulator/avr8js_headless.rs +++ b/crates/fbuild-daemon/src/handlers/emulator/avr8js_headless.rs @@ -121,7 +121,7 @@ pub(crate) async fn run_avr8js_headless( loop { if monitor.timed_out() { final_outcome = Some(monitor.timeout_outcome()); - fbuild_core::platform::process::kill_tokio_tree(&child).await?; + let _ = child.kill().await; break; } @@ -147,7 +147,7 @@ pub(crate) async fn run_avr8js_headless( if let Some(outcome) = monitor.process_line(&line.line) { final_outcome = Some(outcome); - fbuild_core::platform::process::kill_tokio_tree(&child).await?; + let _ = child.kill().await; break; } } @@ -164,7 +164,7 @@ pub(crate) async fn run_avr8js_headless( } Err(_) => { final_outcome = Some(monitor.timeout_outcome()); - fbuild_core::platform::process::kill_tokio_tree(&child).await?; + let _ = child.kill().await; break; } } diff --git a/crates/fbuild-daemon/src/handlers/emulator/avr8js_npm.rs b/crates/fbuild-daemon/src/handlers/emulator/avr8js_npm.rs index 0012aaf09..b5ea135c0 100644 --- a/crates/fbuild-daemon/src/handlers/emulator/avr8js_npm.rs +++ b/crates/fbuild-daemon/src/handlers/emulator/avr8js_npm.rs @@ -7,31 +7,33 @@ use std::path::{Path, PathBuf}; pub(crate) async fn find_node() -> fbuild_core::Result { let node = fbuild_core::platform::executable::name("node", "node.exe"); - // Route through fbuild-core's `run_command` so the probe spawn is - // captured by the daemon's containment group (issue #32). The probe - // is short-lived (`node --version`) but a missing binary should - // still bubble up the same way. - // - // FastLED/fbuild#844: explicit 5 s timeout. `node --version` is a - // sub-100ms operation on every supported host — anything longer is a - // wedged process node and we want to fall through to the - // "not found" error path quickly so the user sees the install - // hint instead of a stalled emulator launch. - match fbuild_core::subprocess::run_command( - &[node, "--version"], + // nodejs-wheel exposes a Python launcher on PATH. Spawning that launcher + // for emulation means Child::kill stops Python while native Node retains + // the output pipes. Ask Node for its own executable so the runner owns + // the emulator process directly (#1694). Keep discovery bounded and + // contained through the existing subprocess boundary (#32, #844). + let executable = match fbuild_core::subprocess::run_command( + &[node, "--print", "process.execPath"], None, None, Some(std::time::Duration::from_secs(5)), ) .await { - Ok(output) if output.success() => Ok(PathBuf::from(node)), - _ => Err(fbuild_core::FbuildError::DeployFailed( + Ok(output) if output.success() => PathBuf::from(output.stdout.trim()), + _ => return Err(fbuild_core::FbuildError::DeployFailed( "Node.js is required for headless avr8js emulation but 'node' was not found on PATH. \ Install Node.js 18+ from https://nodejs.org/" .to_string(), )), + }; + if !executable.is_absolute() { + return Err(fbuild_core::FbuildError::DeployFailed(format!( + "Node.js returned an invalid executable path: {}", + executable.display() + ))); } + Ok(executable) } /// Env-var that forces `ensure_avr8js_npm` to wipe and reinstall the cache diff --git a/crates/fbuild-daemon/src/handlers/emulator/tests_npm_cache.rs b/crates/fbuild-daemon/src/handlers/emulator/tests_npm_cache.rs index f34061e64..6eea361d4 100644 --- a/crates/fbuild-daemon/src/handlers/emulator/tests_npm_cache.rs +++ b/crates/fbuild-daemon/src/handlers/emulator/tests_npm_cache.rs @@ -23,6 +23,84 @@ async fn env_lock() -> tokio::sync::MutexGuard<'static, ()> { LOCK.get_or_init(|| Mutex::new(())).lock().await } +#[tokio::test] +#[ignore = "requires Node.js on PATH; run under nodejs-wheel's uv environment for launcher regression"] +async fn node_discovery_returns_native_executable() { + let _guard = env_lock().await; + let node = super::avr8js_npm::find_node().await.unwrap(); + assert!( + node.is_absolute(), + "must resolve the native executable, not a PATH launcher" + ); + let output = fbuild_core::subprocess::run_command( + &[node.to_str().unwrap(), "--print", "process.execPath"], + None, + None, + Some(std::time::Duration::from_secs(5)), + ) + .await + .unwrap(); + assert!(output.success()); + assert_eq!(node, std::path::PathBuf::from(output.stdout.trim())); +} + +#[tokio::test] +#[ignore = "requires Node.js on PATH; exercises native process shutdown without npm or firmware"] +async fn resolved_node_headless_shutdown() { + use super::avr8js_headless::{RunAvr8jsHeadlessOptions, run_avr8js_headless}; + use crate::handlers::operations::MonitorOutcome; + + let _guard = env_lock().await; + let node = super::avr8js_npm::find_node().await.unwrap(); + assert!(node.is_absolute()); + let temp = tempfile::tempdir_in(fbuild_paths::temp_subdir("daemon-tests")).unwrap(); + let cache = temp.path(); + let module = cache.join("node_modules/avr8js"); + std::fs::create_dir_all(&module).unwrap(); + std::fs::write(module.join("package.json"), "{}").unwrap(); + let script = cache.join("headless.mjs"); + let hex = cache.join("unused.hex"); + std::fs::write( + &script, + "console.log('READY'); setInterval(() => {}, 1000);", + ) + .unwrap(); + for (success, error) in [(Some("READY"), None), (None, Some("READY")), (None, None)] { + let run = run_avr8js_headless( + &node, + &script, + &hex, + 16_000_000, + cache, + RunAvr8jsHeadlessOptions { + timeout_secs: Some(if success.is_some() || error.is_some() { + 5.0 + } else { + 1.0 + }), + halt_on_error: error, + halt_on_success: success, + expect: None, + show_timestamp: false, + verbose: false, + }, + ); + let result = tokio::time::timeout(std::time::Duration::from_secs(10), run) + .await + .expect("native Node shutdown must finish") + .unwrap(); + if success.is_some() { + assert!(matches!(result.outcome, MonitorOutcome::Success(_))); + assert!(result.stdout.contains("READY")); + } else if error.is_some() { + assert!(matches!(result.outcome, MonitorOutcome::Error(_))); + assert!(result.stdout.contains("READY")); + } else { + assert!(matches!(result.outcome, MonitorOutcome::Timeout { .. })); + } + } +} + #[test] fn avr8js_cache_is_intact_detects_missing_dir() { let tmp = tempfile::TempDir::new().unwrap(); diff --git a/crates/fbuild-daemon/src/handlers/emulator/tests_process.rs b/crates/fbuild-daemon/src/handlers/emulator/tests_process.rs index d20b60416..79ba707cf 100644 --- a/crates/fbuild-daemon/src/handlers/emulator/tests_process.rs +++ b/crates/fbuild-daemon/src/handlers/emulator/tests_process.rs @@ -248,88 +248,6 @@ async fn run_real_esp32s3_fixture_in_qemu() { // avr8js headless tests (use fake process, no real Node.js needed) // ----------------------------------------------------------------------- -// Exercise the actual AVR8JS runner with a launcher that leaves a descendant -// holding its pipes, like the nodejs-wheel Python entry point does. -async fn avr8js_launcher_stops_descendant( - marker: &str, - success: Option<&str>, - error: Option<&str>, -) { - use super::avr8js_headless::{RunAvr8jsHeadlessOptions, run_avr8js_headless}; - - if fbuild_core::platform::host::is_windows() { - eprintln!("skipping POSIX shell launcher fixture on Windows"); - return; - } - - let temp = tempfile::tempdir_in(fbuild_paths::temp_subdir("daemon-tests")).unwrap(); - let cache = temp.path(); - let module = cache.join("node_modules/avr8js"); - std::fs::create_dir_all(&module).unwrap(); - std::fs::write(module.join("package.json"), "{}").unwrap(); - let script = cache.join("launcher.sh"); - let pid_file = cache.join("child.pid"); - std::fs::write( - &script, - format!( - "sleep 60 &\nprintf '%s' $! > '{}'\nprintf '%s\\n' '{}'\nwait\n", - pid_file.display(), - marker - ), - ) - .unwrap(); - - let hex = cache.join("unused.hex"); - let run = run_avr8js_headless( - std::path::Path::new("/bin/sh"), - &script, - &hex, - 16_000_000, - cache, - RunAvr8jsHeadlessOptions { - timeout_secs: Some(1.0), - halt_on_error: error, - halt_on_success: success, - expect: None, - show_timestamp: false, - verbose: false, - }, - ); - let result = tokio::time::timeout(std::time::Duration::from_secs(5), run).await; - // Clean up even on RED: cancelling the runner must not leave the fixture - // process sleeping for a minute with its output pipes open. - if let Ok(pid) = std::fs::read_to_string(&pid_file) { - let _ = fbuild_core::platform::process::terminate_pid( - pid.trim().parse().unwrap(), - fbuild_core::platform::process::Termination::Force, - ); - } - let result = result - .expect("AVR8JS must stop the launcher and its descendant") - .unwrap(); - match result.outcome { - MonitorOutcome::Success(_) => assert!(success.is_some()), - MonitorOutcome::Error(_) => assert!(error.is_some()), - MonitorOutcome::Timeout { .. } => assert!(success.is_none() && error.is_none()), - other => panic!("unexpected outcome: {other:?}"), - } -} - -#[tokio::test] -async fn avr8js_launcher_success_stops_descendant() { - avr8js_launcher_stops_descendant("PASS", Some("PASS"), None).await; -} - -#[tokio::test] -async fn avr8js_launcher_error_stops_descendant() { - avr8js_launcher_stops_descendant("FAIL", None, Some("FAIL")).await; -} - -#[tokio::test] -async fn avr8js_launcher_timeout_stops_descendant() { - avr8js_launcher_stops_descendant("booting", None, None).await; -} - #[tokio::test] async fn run_avr8js_headless_captures_stdout() { let (exe, args) = test_process_command(&["Hello from AVR!"]); From 0d716cb24f76111732dd8d6f9f0cdcc3d9bef3e0 Mon Sep 17 00:00:00 2001 From: Zach Vorhies Date: Wed, 21 Oct 2026 23:43:09 -0700 Subject: [PATCH 4/6] test(avr8js): compare executable paths without allocating Local-Gate: v1 tree=e79bccdf4174852a7ebd3e9505b17691e4950f62 secs=876 lanes=linux-minimal:run,dylint:run Ci-Attestation: {"at":1792652268,"gate":"general/all/ubuntu-ci-guards","host":"linux-x86_64","key":"5d04b4e8ad8509a589d46ca718d103cd25f25849f01b7851d702daeb7b395c6a","lane":"linux-minimal","parents":["9dbcd2d267f64a33cb764cbf2047515f677b4125"],"secs":616,"stamp":"a4f0b0a735dba17fbd3413a29c3bb1e2","tree":"e79bccdf4174852a7ebd3e9505b17691e4950f62","v":1,"via":"run"} Ci-Attestation: {"at":1792652268,"gate":"rust/x86_64-unknown-linux-gnu/workspace-clippy","host":"linux-x86_64","key":"5d04b4e8ad8509a589d46ca718d103cd25f25849f01b7851d702daeb7b395c6a","lane":"linux-minimal","parents":["9dbcd2d267f64a33cb764cbf2047515f677b4125"],"secs":616,"stamp":"77a5e1a706bf5f62c480a415818bb773","tree":"e79bccdf4174852a7ebd3e9505b17691e4950f62","v":1,"via":"run"} Ci-Attestation: {"at":1792652268,"gate":"rust/x86_64-unknown-linux-gnu/workspace-test","host":"linux-x86_64","key":"5d04b4e8ad8509a589d46ca718d103cd25f25849f01b7851d702daeb7b395c6a","lane":"linux-minimal","parents":["9dbcd2d267f64a33cb764cbf2047515f677b4125"],"secs":616,"stamp":"f3369f8d705bcaa57516fa50d3bcdf88","tree":"e79bccdf4174852a7ebd3e9505b17691e4950f62","v":1,"via":"run"} Ci-Attestation: {"at":1792652268,"gate":"rust/x86_64-unknown-linux-gnu/python-facade-test","host":"linux-x86_64","key":"5d04b4e8ad8509a589d46ca718d103cd25f25849f01b7851d702daeb7b395c6a","lane":"linux-minimal","parents":["9dbcd2d267f64a33cb764cbf2047515f677b4125"],"secs":616,"stamp":"4019d1697a551d138e02bc6f09badceb","tree":"e79bccdf4174852a7ebd3e9505b17691e4950f62","v":1,"via":"run"} Ci-Attestation: {"at":1792652268,"gate":"general/all/dylint-policy","host":"linux-x86_64","key":"43637a90a0857299924d31cc9806a907e1f69a4a69ec83be1f2ae43c3f751ffd","lane":"dylint","parents":["9dbcd2d267f64a33cb764cbf2047515f677b4125"],"secs":260,"stamp":"eb19e68a6289509c569af4744f3af54a","tree":"e79bccdf4174852a7ebd3e9505b17691e4950f62","v":1,"via":"run"} Ci-Attestation: {"at":1792652268,"gate":"rust/x86_64-unknown-linux-gnu/dylint-library-check","host":"linux-x86_64","key":"43637a90a0857299924d31cc9806a907e1f69a4a69ec83be1f2ae43c3f751ffd","lane":"dylint","parents":["9dbcd2d267f64a33cb764cbf2047515f677b4125"],"secs":260,"stamp":"377e65b77bdbbc7770fc0d294461663c","tree":"e79bccdf4174852a7ebd3e9505b17691e4950f62","v":1,"via":"run"} Ci-Attestation: {"at":1792652268,"gate":"rust/x86_64-unknown-linux-gnu/workspace-dylint","host":"linux-x86_64","key":"43637a90a0857299924d31cc9806a907e1f69a4a69ec83be1f2ae43c3f751ffd","lane":"dylint","parents":["9dbcd2d267f64a33cb764cbf2047515f677b4125"],"secs":260,"stamp":"bcf386b80e9966b94ba78e036888d716","tree":"e79bccdf4174852a7ebd3e9505b17691e4950f62","v":1,"via":"run"} --- crates/fbuild-daemon/src/handlers/emulator/tests_npm_cache.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/fbuild-daemon/src/handlers/emulator/tests_npm_cache.rs b/crates/fbuild-daemon/src/handlers/emulator/tests_npm_cache.rs index 6eea361d4..bc71322b2 100644 --- a/crates/fbuild-daemon/src/handlers/emulator/tests_npm_cache.rs +++ b/crates/fbuild-daemon/src/handlers/emulator/tests_npm_cache.rs @@ -41,7 +41,7 @@ async fn node_discovery_returns_native_executable() { .await .unwrap(); assert!(output.success()); - assert_eq!(node, std::path::PathBuf::from(output.stdout.trim())); + assert_eq!(node.as_path(), std::path::Path::new(output.stdout.trim())); } #[tokio::test] From 33feea77cb71c2ccf59e6ea6bd2f94d91a4dd9f2 Mon Sep 17 00:00:00 2001 From: Zach Vorhies Date: Wed, 21 Oct 2026 23:58:58 -0700 Subject: [PATCH 5/6] chore: bump version to 2.5.43 --- Cargo.lock | 42 +++++++++++++++++++++--------------------- Cargo.toml | 2 +- pyproject.toml | 2 +- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d0f05fd19..2fe9ccbf8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -968,7 +968,7 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "fbuild-bench-fastled-examples" -version = "2.5.42" +version = "2.5.43" dependencies = [ "fbuild-core", "fbuild-library-select", @@ -982,7 +982,7 @@ dependencies = [ [[package]] name = "fbuild-build" -version = "2.5.42" +version = "2.5.43" dependencies = [ "async-trait", "blake3", @@ -1019,7 +1019,7 @@ dependencies = [ [[package]] name = "fbuild-build-arm" -version = "2.5.42" +version = "2.5.43" dependencies = [ "async-trait", "blake3", @@ -1053,7 +1053,7 @@ dependencies = [ [[package]] name = "fbuild-build-engine" -version = "2.5.42" +version = "2.5.43" dependencies = [ "async-trait", "blake3", @@ -1087,7 +1087,7 @@ dependencies = [ [[package]] name = "fbuild-build-esp" -version = "2.5.42" +version = "2.5.43" dependencies = [ "async-trait", "blake3", @@ -1122,7 +1122,7 @@ dependencies = [ [[package]] name = "fbuild-build-mcu" -version = "2.5.42" +version = "2.5.43" dependencies = [ "async-trait", "blake3", @@ -1156,7 +1156,7 @@ dependencies = [ [[package]] name = "fbuild-cli" -version = "2.5.42" +version = "2.5.43" dependencies = [ "blake3", "clap", @@ -1188,7 +1188,7 @@ dependencies = [ [[package]] name = "fbuild-config" -version = "2.5.42" +version = "2.5.43" dependencies = [ "fbuild-core", "fbuild-paths", @@ -1203,7 +1203,7 @@ dependencies = [ [[package]] name = "fbuild-core" -version = "2.5.42" +version = "2.5.43" dependencies = [ "async-trait", "fs2", @@ -1232,7 +1232,7 @@ dependencies = [ [[package]] name = "fbuild-daemon" -version = "2.5.42" +version = "2.5.43" dependencies = [ "async-trait", "axum 0.7.9", @@ -1271,7 +1271,7 @@ dependencies = [ [[package]] name = "fbuild-deploy" -version = "2.5.42" +version = "2.5.43" dependencies = [ "async-trait", "espflash", @@ -1299,7 +1299,7 @@ dependencies = [ [[package]] name = "fbuild-header-scan" -version = "2.5.42" +version = "2.5.43" dependencies = [ "criterion", "fbuild-paths", @@ -1310,7 +1310,7 @@ dependencies = [ [[package]] name = "fbuild-library" -version = "2.5.42" +version = "2.5.43" dependencies = [ "async-trait", "axum 0.7.9", @@ -1342,7 +1342,7 @@ dependencies = [ [[package]] name = "fbuild-library-select" -version = "2.5.42" +version = "2.5.43" dependencies = [ "bincode", "blake3", @@ -1363,7 +1363,7 @@ dependencies = [ [[package]] name = "fbuild-packages" -version = "2.5.42" +version = "2.5.43" dependencies = [ "async-trait", "axum 0.7.9", @@ -1395,7 +1395,7 @@ dependencies = [ [[package]] name = "fbuild-packages-fetch" -version = "2.5.42" +version = "2.5.43" dependencies = [ "async-trait", "axum 0.7.9", @@ -1424,7 +1424,7 @@ dependencies = [ [[package]] name = "fbuild-paths" -version = "2.5.42" +version = "2.5.43" dependencies = [ "blake3", "fbuild-core", @@ -1436,7 +1436,7 @@ dependencies = [ [[package]] name = "fbuild-python" -version = "2.5.42" +version = "2.5.43" dependencies = [ "base64", "fbuild-core", @@ -1460,7 +1460,7 @@ dependencies = [ [[package]] name = "fbuild-serial" -version = "2.5.42" +version = "2.5.43" dependencies = [ "async-trait", "base64", @@ -1483,7 +1483,7 @@ dependencies = [ [[package]] name = "fbuild-test-support" -version = "2.5.42" +version = "2.5.43" dependencies = [ "fbuild-config", "fbuild-core", @@ -1503,7 +1503,7 @@ dependencies = [ [[package]] name = "fbuild-toolchain" -version = "2.5.42" +version = "2.5.43" dependencies = [ "async-trait", "axum 0.7.9", diff --git a/Cargo.toml b/Cargo.toml index 98558afa9..687090ca3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -93,7 +93,7 @@ targets = [ ] [workspace.package] -version = "2.5.42" +version = "2.5.43" edition = "2021" rust-version = "1.95.0" license = "AGPL-3.0-only" diff --git a/pyproject.toml b/pyproject.toml index c7a4b5e8d..81d51e520 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "fbuild" -version = "2.5.42" +version = "2.5.43" description = "PlatformIO-compatible embedded build tool (Rust implementation)" readme = "README.md" license = "AGPL-3.0-only" From e6a46de5b5c31c1e56634f43ecaff6c65aeec8ba Mon Sep 17 00:00:00 2001 From: Zach Vorhies Date: Thu, 22 Oct 2026 00:01:05 -0700 Subject: [PATCH 6/6] ci: refresh platform inventory after AVR8JS regressions Local-Gate: v1 tree=f251321b26939504eb51e03529c912fc434a6c24 secs=882 lanes=linux-minimal:run,dylint:run Ci-Attestation: {"at":1792653348,"gate":"general/all/ubuntu-ci-guards","host":"linux-x86_64","key":"f8d0308d338ee6cc4a309c038bb795b65390796937ca0bf9b4acd12336f77b9f","lane":"linux-minimal","parents":["33feea77cb71c2ccf59e6ea6bd2f94d91a4dd9f2"],"secs":561,"stamp":"088b4918b40f325337e0a71c17664d04","tree":"f251321b26939504eb51e03529c912fc434a6c24","v":1,"via":"run"} Ci-Attestation: {"at":1792653348,"gate":"rust/x86_64-unknown-linux-gnu/workspace-clippy","host":"linux-x86_64","key":"f8d0308d338ee6cc4a309c038bb795b65390796937ca0bf9b4acd12336f77b9f","lane":"linux-minimal","parents":["33feea77cb71c2ccf59e6ea6bd2f94d91a4dd9f2"],"secs":561,"stamp":"22369317f08ab028e46bf894c79ce5fe","tree":"f251321b26939504eb51e03529c912fc434a6c24","v":1,"via":"run"} Ci-Attestation: {"at":1792653348,"gate":"rust/x86_64-unknown-linux-gnu/workspace-test","host":"linux-x86_64","key":"f8d0308d338ee6cc4a309c038bb795b65390796937ca0bf9b4acd12336f77b9f","lane":"linux-minimal","parents":["33feea77cb71c2ccf59e6ea6bd2f94d91a4dd9f2"],"secs":561,"stamp":"79f9866c2bc1682d4e438986b686dfb8","tree":"f251321b26939504eb51e03529c912fc434a6c24","v":1,"via":"run"} Ci-Attestation: {"at":1792653348,"gate":"rust/x86_64-unknown-linux-gnu/python-facade-test","host":"linux-x86_64","key":"f8d0308d338ee6cc4a309c038bb795b65390796937ca0bf9b4acd12336f77b9f","lane":"linux-minimal","parents":["33feea77cb71c2ccf59e6ea6bd2f94d91a4dd9f2"],"secs":561,"stamp":"7691bbaff87486fb166c64c39c56e599","tree":"f251321b26939504eb51e03529c912fc434a6c24","v":1,"via":"run"} Ci-Attestation: {"at":1792653348,"gate":"general/all/dylint-policy","host":"linux-x86_64","key":"09b1ca1da07d7f28f4314945d6f98d86fc573692d20612c237875f4e6787f800","lane":"dylint","parents":["33feea77cb71c2ccf59e6ea6bd2f94d91a4dd9f2"],"secs":321,"stamp":"d674f99fb321a6ce757169e707dfe9dd","tree":"f251321b26939504eb51e03529c912fc434a6c24","v":1,"via":"run"} Ci-Attestation: {"at":1792653348,"gate":"rust/x86_64-unknown-linux-gnu/dylint-library-check","host":"linux-x86_64","key":"09b1ca1da07d7f28f4314945d6f98d86fc573692d20612c237875f4e6787f800","lane":"dylint","parents":["33feea77cb71c2ccf59e6ea6bd2f94d91a4dd9f2"],"secs":321,"stamp":"59ab9ce869c5f51ac0a0701646dfc9b1","tree":"f251321b26939504eb51e03529c912fc434a6c24","v":1,"via":"run"} Ci-Attestation: {"at":1792653348,"gate":"rust/x86_64-unknown-linux-gnu/workspace-dylint","host":"linux-x86_64","key":"09b1ca1da07d7f28f4314945d6f98d86fc573692d20612c237875f4e6787f800","lane":"dylint","parents":["33feea77cb71c2ccf59e6ea6bd2f94d91a4dd9f2"],"secs":321,"stamp":"bacd37c59324c40d1bd29963a7b707fa","tree":"f251321b26939504eb51e03529c912fc434a6c24","v":1,"via":"run"} --- ci/platform_boundary_research.tsv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/platform_boundary_research.tsv b/ci/platform_boundary_research.tsv index bbf7143cf..d27207355 100644 --- a/ci/platform_boundary_research.tsv +++ b/ci/platform_boundary_research.tsv @@ -74,7 +74,7 @@ crates/fbuild-core/src/platform/windows/usb_pnp.rs 40 native_path windows_sys:: crates/fbuild-core/tests/it/platformio_package_resolution.rs 5 attr_cfg #[cfg(windows)] host host_mechanic crates/fbuild-core/tests/it/platformio_package_resolution.rs 107 attr_cfg #[cfg(windows)] host host_mechanic crates/fbuild-daemon/Cargo.toml 74 native_dependency libc process host_mechanic -crates/fbuild-daemon/src/handlers/emulator/tests_npm_cache.rs 146 attr_cfg #[cfg(windows)] host_executable host_artifact_policy +crates/fbuild-daemon/src/handlers/emulator/tests_npm_cache.rs 224 attr_cfg #[cfg(windows)] host_executable host_artifact_policy crates/fbuild-daemon/src/handlers/emulator/tests_process.rs 9 attr_cfg #[cfg(windows)] host_executable host_artifact_policy crates/fbuild-daemon/src/handlers/emulator/tests_process.rs 21 attr_cfg #[cfg(not(windows))] host_executable host_artifact_policy crates/fbuild-daemon/src/handlers/health.rs 51 attr_cfg #[cfg(target_os=)] host host_mechanic