diff --git a/adk-rust/crates/detmir-portal/src/command_runner.rs b/adk-rust/crates/detmir-portal/src/command_runner.rs new file mode 100644 index 0000000..1e86e95 --- /dev/null +++ b/adk-rust/crates/detmir-portal/src/command_runner.rs @@ -0,0 +1,26 @@ +//! External command execution helpers for the portal. +//! +//! CONTRACT: these helpers are intentionally small and side-effect explicit. +//! They preserve stdout/stderr error text because readiness verification APIs +//! expose command failure diagnostics to operators. + +use std::path::Path; +use std::process::Command; + +pub(crate) fn run_in_dir(dir: &Path, command: &mut Command) -> std::result::Result<(), String> { + let output = command + .current_dir(dir) + .output() + .map_err(|err| format!("run command in {}: {err}", dir.display()))?; + if output.status.success() { + Ok(()) + } else { + Err(format!( + "{}{}", + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + ) + .trim() + .to_string()) + } +} diff --git a/adk-rust/crates/detmir-portal/src/main.rs b/adk-rust/crates/detmir-portal/src/main.rs index ac58323..eb5dfd0 100644 --- a/adk-rust/crates/detmir-portal/src/main.rs +++ b/adk-rust/crates/detmir-portal/src/main.rs @@ -23,12 +23,14 @@ use serde_json::{Value, json}; use sha2::{Digest, Sha256}; use tiny_http::{Header, Method, Request, Response, Server, StatusCode}; +mod command_runner; mod executive_actions; mod portal_roles; mod production; mod risk_narrative; mod workforce_kpi_explain; +use command_runner::run_in_dir; use executive_actions::{ actions_from_center, build_action_center_from_report, filter_actions_for_role, }; @@ -1810,24 +1812,6 @@ fn read_json_file(path: &Path) -> Result { serde_json::from_str(&text).with_context(|| format!("parse {}", path.display())) } -fn run_in_dir(dir: &Path, command: &mut Command) -> std::result::Result<(), String> { - let output = command - .current_dir(dir) - .output() - .map_err(|err| format!("run command in {}: {err}", dir.display()))?; - if output.status.success() { - Ok(()) - } else { - Err(format!( - "{}{}", - String::from_utf8_lossy(&output.stdout), - String::from_utf8_lossy(&output.stderr) - ) - .trim() - .to_string()) - } -} - fn query_flag(url: &str, key: &str) -> bool { let Some(query) = url.split_once('?').map(|(_, query)| query) else { return false;