From 2f839b332dc4fc92617fac2639bcfe24473e74fa Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Thu, 10 Sep 2026 11:47:03 -0600 Subject: [PATCH] fix(function): build bundled bash tools without writing back to the source tree PR #24 follow-up 1. The bundled-tools test called the writing generate_bash_declarations on the repo's assets/functions/tools/*.sh, so cargo test could rewrite tracked files. Split the build+export step into a pure build_bash_tool so the test never touches the source tree. --- src/function/mod.rs | 3 ++- src/parsers/bash.rs | 21 +++++++++++++++------ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/function/mod.rs b/src/function/mod.rs index 7708b1f..39f399c 100644 --- a/src/function/mod.rs +++ b/src/function/mod.rs @@ -3389,7 +3389,8 @@ mod tests { continue; } let name = path.file_stem().unwrap().to_string_lossy().to_string(); - let declarations = Functions::generate_declarations(&path) + let src = std::fs::read_to_string(&path).unwrap(); + let (_, declarations) = bash::build_bash_tool(&src, &name) .unwrap_or_else(|e| panic!("bundled tool '{name}' failed to parse: {e}")); assert!( !declarations.is_empty(), diff --git a/src/parsers/bash.rs b/src/parsers/bash.rs index 1547e11..019eaec 100644 --- a/src/parsers/bash.rs +++ b/src/parsers/bash.rs @@ -18,23 +18,32 @@ pub fn generate_bash_declarations( .with_context(|| format!("Failed to load script at '{tool_file:?}'"))?; debug!("Building script at '{tool_file:?}'"); + let (build_script, declarations) = build_bash_tool(&src, file_name) + .with_context(|| format!("Failed to build script at '{tools_file_path:?}'"))?; + function::write_file_atomic(tools_file_path, &build_script, Some(0o755)) + .with_context(|| format!("Failed to write built script to '{tools_file_path:?}'"))?; + + Ok(declarations) +} + +/// Builds the argc script and derives its declarations without touching the +/// filesystem, so callers can inspect a tool source without rewriting it. +pub fn build_bash_tool(src: &str, file_name: &str) -> Result<(String, Vec)> { let build_script = argc::build( - &src, + src, "", env::var("TERM_WIDTH").ok().and_then(|v| v.parse().ok()), )?; let build_script = allow_empty_required_values(&build_script); - function::write_file_atomic(tools_file_path, &build_script, Some(0o755)) - .with_context(|| format!("Failed to write built script to '{tools_file_path:?}'"))?; let command_value = argc::export(&build_script, file_name) - .with_context(|| format!("Failed to parse script at '{tool_file:?}'"))?; + .with_context(|| format!("Failed to parse script '{file_name}'"))?; if command_value.subcommands.is_empty() { let function_declaration = command_to_function_declaration(&command_value).ok_or_else(|| { anyhow::format_err!("Tool definition missing or empty description: {file_name}") })?; - Ok(vec![function_declaration]) + Ok((build_script, vec![function_declaration])) } else { let mut declarations = vec![]; for subcommand in &command_value.subcommands { @@ -54,7 +63,7 @@ pub fn generate_bash_declarations( } } - Ok(declarations) + Ok((build_script, declarations)) } }