From 00c34aa1af1bac5cf98631cefcb0e707764609c7 Mon Sep 17 00:00:00 2001 From: Jay Gowdy Date: Fri, 22 May 2026 21:23:30 -0700 Subject: [PATCH 1/2] Remove default 60s command timeout The default timeout of 60s silently kills long-running commands like daemon processes. Commands that want a timeout can still pass --timeout explicitly; the framework should not impose one by default. --- docs/concepts.md | 2 +- docs/design.md | 2 +- src/flags.rs | 8 ++++---- tests/foundation.rs | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/concepts.md b/docs/concepts.md index 3c3e819..ae55760 100644 --- a/docs/concepts.md +++ b/docs/concepts.md @@ -259,7 +259,7 @@ populate middleware: | `--offset` | `offset` | `0` | Client-side starting offset for list output. | | `--schema` | `schema` | `false` | Renders command schema instead of running business logic. | | `--reason` | `reason` | empty | Reason passed to authorization. | -| `--timeout` | `timeout` | `60s` | Command deadline; `0s` disables it. | +| `--timeout` | `timeout` | `0s` | Command deadline (e.g. `60s`, `5m`); default `0s` = no timeout. | | `--debug` | `debug` | empty | Enables debug behavior for integrations that use it. | | `--search` | `search` | empty | Searches command and guide documentation before command execution. | diff --git a/docs/design.md b/docs/design.md index 392250b..48e8f4d 100644 --- a/docs/design.md +++ b/docs/design.md @@ -246,7 +246,7 @@ Framework global flags populate middleware and apply consistently to every comma | `--offset` | Client-side starting offset for list output. | | `--schema` | Renders command schema instead of running business logic. | | `--reason` | Reason passed to authorization, audit, and activity. | -| `--timeout` | Command deadline; `0s` disables the deadline. | +| `--timeout` | Command deadline (e.g. `60s`, `5m`); default is no timeout (`0s`). | | `--debug` | Debug selector for integrations that use it. | | `--search` | Searches command and guide documentation before command execution. | | `--version`, `-v` | Prints version/build metadata. | diff --git a/src/flags.rs b/src/flags.rs index 697eca5..716ca2b 100644 --- a/src/flags.rs +++ b/src/flags.rs @@ -49,7 +49,7 @@ impl Default for GlobalFlags { offset: 0, schema: false, reason: String::new(), - timeout: "60s".to_owned(), + timeout: "0s".to_owned(), debug: String::new(), search: String::new(), } @@ -150,9 +150,9 @@ pub fn register_global_flags(command: Command) -> Command { .long("timeout") .global(true) .allow_hyphen_values(true) - .default_value("60s") + .default_value("0s") .value_name("DURATION") - .help("Overall command timeout (e.g. 60s, 5m); use 0s to disable"), + .help("Overall command timeout (e.g. 60s, 5m); default 0s = no timeout"), ) .arg( Arg::new("debug") @@ -236,7 +236,7 @@ pub fn global_flags_from_matches(matches: &ArgMatches) -> GlobalFlags { timeout: matches .get_one::("timeout") .cloned() - .unwrap_or_else(|| "60s".to_owned()), + .unwrap_or_else(|| "0s".to_owned()), debug: matches .get_one::("debug") .cloned() diff --git a/tests/foundation.rs b/tests/foundation.rs index 2ffd525..a458131 100644 --- a/tests/foundation.rs +++ b/tests/foundation.rs @@ -2753,7 +2753,7 @@ async fn runtime_command_context_exposes_args_user_args_path_and_middleware() { "user_region_present": false, "output": "json", "debug": "*", - "timeout_present": true, + "timeout_present": false, "app": "my-cli", "credential_present": false }) @@ -3483,7 +3483,7 @@ fn global_flag_defaults_and_derived_flag_classes_cover_common_clap_actions() { offset: 0, schema: false, reason: String::new(), - timeout: "60s".to_owned(), + timeout: "0s".to_owned(), debug: String::new(), search: String::new(), } From 05240d6a95e25e9240f697c268cc362c4abc2ffb Mon Sep 17 00:00:00 2001 From: Jay Gowdy Date: Tue, 26 May 2026 19:11:44 -0700 Subject: [PATCH 2/2] Fix flaky ETXTBSY race in exec provider tests Sync the script file to disk before setting permissions and executing. On Linux, std::fs::write can return before the kernel finishes flushing the inode, causing ETXTBSY when the test immediately tries to exec the script. --- tests/foundation.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/foundation.rs b/tests/foundation.rs index a458131..6cf935f 100644 --- a/tests/foundation.rs +++ b/tests/foundation.rs @@ -8854,6 +8854,13 @@ impl AuthProvider for EmptyThenFilledProvider { fn make_executable(path: &std::path::Path) { use std::os::unix::fs::PermissionsExt; + // Sync the file to disk before changing permissions and executing. + // Without this, Linux can return ETXTBSY when the exec races with + // the kernel flushing the write from std::fs::write. + let file = std::fs::File::open(path).expect("script should be openable for sync"); + file.sync_all().expect("script sync should succeed"); + drop(file); + let mut permissions = std::fs::metadata(path) .expect("script metadata should be readable") .permissions();