From 837706eab70075b6d961c52fd137526ace0e2f20 Mon Sep 17 00:00:00 2001 From: Mykhailo Chalyi Date: Fri, 27 Mar 2026 22:48:35 +0000 Subject: [PATCH 1/5] fix(interpreter): set -e should not trigger on compound commands with && chain failure Check result.errexit_suppressed in execute_script_body() so compound commands (for/while/until) whose body ends with an AND-OR chain failure do not incorrectly trigger errexit. Fixes #873 --- crates/bashkit/src/interpreter/mod.rs | 8 ++- crates/bashkit/tests/issue_873_test.rs | 70 ++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 crates/bashkit/tests/issue_873_test.rs diff --git a/crates/bashkit/src/interpreter/mod.rs b/crates/bashkit/src/interpreter/mod.rs index aca72bdb..392a5280 100644 --- a/crates/bashkit/src/interpreter/mod.rs +++ b/crates/bashkit/src/interpreter/mod.rs @@ -1142,7 +1142,8 @@ impl Interpreter { // Run ERR trap on non-zero exit (unless in conditional chain) if exit_code != 0 { let suppressed = matches!(command, Command::List(_)) - || matches!(command, Command::Pipeline(p) if p.negated); + || matches!(command, Command::Pipeline(p) if p.negated) + || result.errexit_suppressed; if !suppressed { self.run_err_trap(&mut stdout, &mut stderr).await; } @@ -1151,9 +1152,12 @@ impl Interpreter { // errexit (set -e): stop on non-zero exit for top-level simple commands. // List commands handle errexit internally (with && / || chain awareness). // Negated pipelines (! cmd) explicitly handle the exit code. + // Compound commands (for/while/until) propagate errexit_suppressed when + // their body ends with an AND-OR chain failure. if self.is_errexit_enabled() && exit_code != 0 { let suppressed = matches!(command, Command::List(_)) - || matches!(command, Command::Pipeline(p) if p.negated); + || matches!(command, Command::Pipeline(p) if p.negated) + || result.errexit_suppressed; if !suppressed { break; } diff --git a/crates/bashkit/tests/issue_873_test.rs b/crates/bashkit/tests/issue_873_test.rs new file mode 100644 index 00000000..48579b6e --- /dev/null +++ b/crates/bashkit/tests/issue_873_test.rs @@ -0,0 +1,70 @@ +//! Test for issue #873: set -e incorrectly triggers on compound commands +//! whose body ends with && chain failure. + +use bashkit::Bash; + +#[tokio::test] +async fn set_e_for_loop_and_chain_no_exit() { + let mut bash = Bash::new(); + let result = bash + .exec( + r#" +set -euo pipefail +result="" +for src in yes no; do + [[ "${src}" == "yes" ]] && result="${src}" +done +echo "result: ${result}" +"#, + ) + .await + .unwrap(); + assert!( + result.stdout.contains("result: yes"), + "expected 'result: yes', got: {}", + result.stdout + ); +} + +#[tokio::test] +async fn set_e_while_loop_and_chain_no_exit() { + let mut bash = Bash::new(); + let result = bash + .exec( + r#" +set -e +i=0 +while [[ $i -lt 3 ]]; do + [[ $i -eq 1 ]] && echo "found one" + ((i++)) || true +done +echo "done" +"#, + ) + .await + .unwrap(); + assert!( + result.stdout.contains("found one"), + "stdout: {}", + result.stdout + ); + assert!(result.stdout.contains("done"), "stdout: {}", result.stdout); +} + +#[tokio::test] +async fn set_e_plain_failure_in_loop_still_exits() { + let mut bash = Bash::new(); + let result = bash + .exec( + r#" +set -e +for x in a b; do + false +done +echo "SHOULD NOT APPEAR" +"#, + ) + .await + .unwrap(); + assert!(!result.stdout.contains("SHOULD NOT APPEAR")); +} From b6a2dfdfb3028febfb792501a3aeac4ce04fe0bd Mon Sep 17 00:00:00 2001 From: Mykhailo Chalyi Date: Fri, 27 Mar 2026 23:03:46 +0000 Subject: [PATCH 2/5] chore: retrigger CI From deeefc751af4e18c7ea39781f08d9d79f8718954 Mon Sep 17 00:00:00 2001 From: Mykhailo Chalyi Date: Fri, 27 Mar 2026 23:09:36 +0000 Subject: [PATCH 3/5] chore: prune stale cargo-vet exemptions --- supply-chain/config.toml | 40 ---------------------------------------- 1 file changed, 40 deletions(-) diff --git a/supply-chain/config.toml b/supply-chain/config.toml index a7594112..df0c62cd 100644 --- a/supply-chain/config.toml +++ b/supply-chain/config.toml @@ -154,10 +154,6 @@ criteria = "safe-to-run" version = "0.2.4" criteria = "safe-to-deploy" -[[exemptions.cc]] -version = "1.2.57" -criteria = "safe-to-deploy" - [[exemptions.cc]] version = "1.2.58" criteria = "safe-to-deploy" @@ -206,10 +202,6 @@ criteria = "safe-to-deploy" version = "1.1.0" criteria = "safe-to-deploy" -[[exemptions.cmake]] -version = "0.1.57" -criteria = "safe-to-deploy" - [[exemptions.cmake]] version = "0.1.58" criteria = "safe-to-deploy" @@ -238,10 +230,6 @@ criteria = "safe-to-deploy" version = "0.9.0" criteria = "safe-to-deploy" -[[exemptions.console]] -version = "0.15.11" -criteria = "safe-to-run" - [[exemptions.console]] version = "0.16.3" criteria = "safe-to-run" @@ -578,10 +566,6 @@ criteria = "safe-to-deploy" version = "2.13.0" criteria = "safe-to-deploy" -[[exemptions.insta]] -version = "1.46.3" -criteria = "safe-to-run" - [[exemptions.insta]] version = "1.47.0" criteria = "safe-to-run" @@ -598,10 +582,6 @@ criteria = "safe-to-deploy" version = "2.12.0" criteria = "safe-to-deploy" -[[exemptions.iri-string]] -version = "0.7.10" -criteria = "safe-to-deploy" - [[exemptions.iri-string]] version = "0.7.11" criteria = "safe-to-deploy" @@ -642,10 +622,6 @@ criteria = "safe-to-deploy" version = "0.21.1" criteria = "safe-to-deploy" -[[exemptions.jni-sys]] -version = "0.3.0" -criteria = "safe-to-deploy" - [[exemptions.jni-sys]] version = "0.3.1" criteria = "safe-to-deploy" @@ -726,10 +702,6 @@ criteria = "safe-to-deploy" version = "0.8.9" criteria = "safe-to-deploy" -[[exemptions.mio]] -version = "1.1.1" -criteria = "safe-to-deploy" - [[exemptions.mio]] version = "1.2.0" criteria = "safe-to-deploy" @@ -1190,10 +1162,6 @@ criteria = "safe-to-deploy" version = "0.9.1" criteria = "safe-to-deploy" -[[exemptions.simd-adler32]] -version = "0.3.8" -criteria = "safe-to-deploy" - [[exemptions.simd-adler32]] version = "0.3.9" criteria = "safe-to-deploy" @@ -1390,10 +1358,6 @@ criteria = "safe-to-deploy" version = "0.1.25" criteria = "safe-to-deploy" -[[exemptions.unicode-segmentation]] -version = "1.13.1" -criteria = "safe-to-deploy" - [[exemptions.unicode-segmentation]] version = "1.13.2" criteria = "safe-to-deploy" @@ -1562,10 +1526,6 @@ criteria = "safe-to-deploy" version = "0.52.0" criteria = "safe-to-deploy" -[[exemptions.windows-sys]] -version = "0.59.0" -criteria = "safe-to-run" - [[exemptions.windows-sys]] version = "0.60.2" criteria = "safe-to-deploy" From 16576d805846f85ba8ca400c56a48177ee5bf164 Mon Sep 17 00:00:00 2001 From: Mykhailo Chalyi Date: Fri, 27 Mar 2026 23:17:09 +0000 Subject: [PATCH 4/5] chore: retrigger CI (transient audit failure) From f8b6428d3f333da2b0aa8d25198a457aa531e274 Mon Sep 17 00:00:00 2001 From: Mykhailo Chalyi Date: Fri, 27 Mar 2026 23:22:55 +0000 Subject: [PATCH 5/5] chore: update cargo-vet exemptions for wasm-bindgen 0.2.115 --- supply-chain/config.toml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/supply-chain/config.toml b/supply-chain/config.toml index df0c62cd..93052b55 100644 --- a/supply-chain/config.toml +++ b/supply-chain/config.toml @@ -639,7 +639,7 @@ version = "0.1.34" criteria = "safe-to-deploy" [[exemptions.js-sys]] -version = "0.3.91" +version = "0.3.92" criteria = "safe-to-deploy" [[exemptions.leb128fmt]] @@ -1427,23 +1427,23 @@ version = "0.4.0+wasi-0.3.0-rc-2026-01-06" criteria = "safe-to-run" [[exemptions.wasm-bindgen]] -version = "0.2.114" +version = "0.2.115" criteria = "safe-to-deploy" [[exemptions.wasm-bindgen-futures]] -version = "0.4.64" +version = "0.4.65" criteria = "safe-to-deploy" [[exemptions.wasm-bindgen-macro]] -version = "0.2.114" +version = "0.2.115" criteria = "safe-to-deploy" [[exemptions.wasm-bindgen-macro-support]] -version = "0.2.114" +version = "0.2.115" criteria = "safe-to-deploy" [[exemptions.wasm-bindgen-shared]] -version = "0.2.114" +version = "0.2.115" criteria = "safe-to-deploy" [[exemptions.wasm-encoder]] @@ -1463,7 +1463,7 @@ version = "0.244.0" criteria = "safe-to-deploy" [[exemptions.web-sys]] -version = "0.3.91" +version = "0.3.92" criteria = "safe-to-deploy" [[exemptions.web-time]]