From 38c9a59cc92e8f5d2204e38e9946a44284d05a34 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 7 May 2026 09:14:58 +0000 Subject: [PATCH 1/4] feat(logging): add configurable log output dir and debug pipeline ado-aw flags Agent-Logs-Url: https://github.com/githubnext/ado-aw/sessions/78e8e09d-bd5a-43f7-83e6-6adf4097b88c Co-authored-by: jamesadevine <4742697+jamesadevine@users.noreply.github.com> --- docs/cli.md | 2 +- docs/template-markers.md | 6 ++++ src/compile/common.rs | 13 +++++++-- src/data/1es-base.yml | 19 +++++++----- src/data/base.yml | 19 +++++++----- src/logging.rs | 63 +++++++++++++++++++++++++++++++++------- src/main.rs | 12 ++++++-- 7 files changed, 103 insertions(+), 31 deletions(-) diff --git a/docs/cli.md b/docs/cli.md index c4e430fe..bb272c69 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -4,7 +4,7 @@ _Part of the [ado-aw documentation](../AGENTS.md)._ ## CLI Commands -Global flags (apply to all subcommands): `--verbose, -v` (enable info-level logging), `--debug, -d` (enable debug-level logging, implies verbose) +Global flags (apply to all subcommands): `--verbose, -v` (enable info-level logging), `--debug, -d` (enable debug-level logging, implies verbose), `--log-output-dir ` (write ado-aw logs to a specific directory; overrides `ADO_AW_LOG_DIR`) - `init` - Initialize a repository for AI-first agentic pipeline authoring - `--path ` - Target directory (defaults to current directory) diff --git a/docs/template-markers.md b/docs/template-markers.md index a5fe6dae..f5c749d0 100644 --- a/docs/template-markers.md +++ b/docs/template-markers.md @@ -279,6 +279,12 @@ Generates a pipeline step that probes each configured MCPG backend with an MCP i When `--debug-pipeline` is not passed (the default), this placeholder is replaced with an empty string. +## {{ ado_aw_debug_flags }} + +Generates ado-aw CLI debug flags in runtime script invocations. When `--debug-pipeline` is passed (debug builds only), this inserts `--debug ` so ado-aw writes detailed debug-level logs. + +When `--debug-pipeline` is not passed, this placeholder is replaced with an empty string. + ## {{ pr_trigger }} Generates PR trigger configuration. When a schedule or pipeline trigger is configured, this generates `pr: none` to disable PR triggers. Otherwise, it generates an empty string, allowing the default PR trigger behavior. diff --git a/src/compile/common.rs b/src/compile/common.rs index 6e2767e8..c6f07ace 100644 --- a/src/compile/common.rs +++ b/src/compile/common.rs @@ -708,6 +708,7 @@ pub fn generate_integrity_check(skip: bool) -> String { /// stderr dump on health-check failure /// - `{{ verify_mcp_backends }}`: full pipeline step that probes each MCPG /// backend with MCP initialize + tools/list +/// - `{{ ado_aw_debug_flags }}`: `--debug ` for ado-aw CLI invocations /// /// When `debug` is `false`, both markers resolve to empty strings. pub fn generate_debug_pipeline_replacements(debug: bool) -> Vec<(String, String)> { @@ -717,6 +718,7 @@ pub fn generate_debug_pipeline_replacements(debug: bool) -> Vec<(String, String) // generate_mcpg_docker_env when no env flags are needed). ("{{ mcpg_debug_flags }}".into(), "\\".into()), ("{{ verify_mcp_backends }}".into(), String::new()), + ("{{ ado_aw_debug_flags }}".into(), String::new()), ]; } @@ -787,6 +789,7 @@ pub fn generate_debug_pipeline_replacements(debug: bool) -> Vec<(String, String) vec![ ("{{ mcpg_debug_flags }}".into(), mcpg_debug_flags), ("{{ verify_mcp_backends }}".into(), verify_mcp_backends), + ("{{ ado_aw_debug_flags }}".into(), "--debug ".into()), ] } @@ -3226,19 +3229,21 @@ mod tests { #[test] fn test_debug_pipeline_replacements_disabled() { let replacements = generate_debug_pipeline_replacements(false); - assert_eq!(replacements.len(), 2); + assert_eq!(replacements.len(), 3); // mcpg_debug_flags returns `\` for bash line continuation let flags = replacements.iter().find(|(m, _)| m == "{{ mcpg_debug_flags }}").unwrap(); assert_eq!(flags.1, "\\", "mcpg_debug_flags should be a bare backslash when disabled"); // verify_mcp_backends should be empty let probe = replacements.iter().find(|(m, _)| m == "{{ verify_mcp_backends }}").unwrap(); assert!(probe.1.is_empty(), "verify_mcp_backends should be empty when disabled"); + let ado_aw_flags = replacements.iter().find(|(m, _)| m == "{{ ado_aw_debug_flags }}").unwrap(); + assert!(ado_aw_flags.1.is_empty(), "ado_aw_debug_flags should be empty when disabled"); } #[test] fn test_debug_pipeline_replacements_enabled() { let replacements = generate_debug_pipeline_replacements(true); - assert_eq!(replacements.len(), 2); + assert_eq!(replacements.len(), 3); let flags = replacements.iter().find(|(m, _)| m == "{{ mcpg_debug_flags }}"); assert!(flags.is_some(), "Should have mcpg_debug_flags marker"); @@ -3252,6 +3257,10 @@ mod tests { assert!(probe_value.contains("tools/list"), "Should contain tools/list probe"); assert!(probe_value.contains("initialize"), "Should contain initialize handshake"); assert!(probe_value.contains("MCPG_API_KEY"), "Should contain API key env mapping"); + + let ado_aw_flags = replacements.iter().find(|(m, _)| m == "{{ ado_aw_debug_flags }}"); + assert!(ado_aw_flags.is_some(), "Should have ado_aw_debug_flags marker"); + assert_eq!(&ado_aw_flags.unwrap().1, "--debug "); } // ─── validate_submit_pr_review_events ──────────────────────────────────── diff --git a/src/data/1es-base.yml b/src/data/1es-base.yml index e0407036..f9e7cc49 100644 --- a/src/data/1es-base.yml +++ b/src/data/1es-base.yml @@ -188,7 +188,7 @@ extends: # (with trailing space). The value MUST be newline-free; is_safe_tool_name enforces this. # Positional args (output_directory, bounding_directory) MUST come after all named # options — clap parses them positionally and reordering would break the command. - nohup /tmp/awf-tools/ado-aw mcp-http \ + nohup /tmp/awf-tools/ado-aw {{ ado_aw_debug_flags }}mcp-http \ --port "$SAFE_OUTPUTS_PORT" \ --api-key "$SAFE_OUTPUTS_API_KEY" \ {{ enabled_tools_args }}"/tmp/awf-tools/staging" \ @@ -395,8 +395,9 @@ extends: if [ -d "{{ engine_log_dir }}" ]; then cp -r "{{ engine_log_dir }}"/* "$(Agent.TempDirectory)/staging/logs/" 2>/dev/null || true fi - if [ -d ~/.ado-aw/logs ]; then - cp -r ~/.ado-aw/logs/* "$(Agent.TempDirectory)/staging/logs/" 2>/dev/null || true + ADO_AW_LOG_DIR="${ADO_AW_LOG_DIR:-$HOME/.ado-aw/logs}" + if [ -d "$ADO_AW_LOG_DIR" ]; then + cp -r "$ADO_AW_LOG_DIR"/* "$(Agent.TempDirectory)/staging/logs/" 2>/dev/null || true fi if [ -d /tmp/gh-aw/mcp-logs ]; then mkdir -p "$(Agent.TempDirectory)/staging/logs/mcpg" @@ -593,9 +594,10 @@ extends: mkdir -p "$(Agent.TempDirectory)/analyzed_outputs/logs/copilot" cp -r "{{ engine_log_dir }}"/* "$(Agent.TempDirectory)/analyzed_outputs/logs/copilot/" 2>/dev/null || true fi - if [ -d ~/.ado-aw/logs ]; then + ADO_AW_LOG_DIR="${ADO_AW_LOG_DIR:-$HOME/.ado-aw/logs}" + if [ -d "$ADO_AW_LOG_DIR" ]; then mkdir -p "$(Agent.TempDirectory)/analyzed_outputs/logs/ado-aw" - cp -r ~/.ado-aw/logs/* "$(Agent.TempDirectory)/analyzed_outputs/logs/ado-aw/" 2>/dev/null || true + cp -r "$ADO_AW_LOG_DIR"/* "$(Agent.TempDirectory)/analyzed_outputs/logs/ado-aw/" 2>/dev/null || true fi echo "Logs copied to $(Agent.TempDirectory)/analyzed_outputs/logs" ls -laR "$(Agent.TempDirectory)/analyzed_outputs/logs" 2>/dev/null || echo "No logs found" @@ -653,7 +655,7 @@ extends: displayName: "Prepare output directory" - bash: | - ado-aw execute --source "{{ source_path }}" --safe-output-dir "$(Pipeline.Workspace)/analyzed_outputs_$(Build.BuildId)" --output-dir "$(Agent.TempDirectory)/staging" + ado-aw {{ ado_aw_debug_flags }}execute --source "{{ source_path }}" --safe-output-dir "$(Pipeline.Workspace)/analyzed_outputs_$(Build.BuildId)" --output-dir "$(Agent.TempDirectory)/staging" EXIT_CODE=$? if [ $EXIT_CODE -eq 2 ]; then echo "##vso[task.complete result=SucceededWithIssues;]Executor completed with warnings" @@ -674,9 +676,10 @@ extends: mkdir -p "$(Agent.TempDirectory)/staging/logs/copilot" cp -r "{{ engine_log_dir }}"/* "$(Agent.TempDirectory)/staging/logs/copilot/" 2>/dev/null || true fi - if [ -d ~/.ado-aw/logs ]; then + ADO_AW_LOG_DIR="${ADO_AW_LOG_DIR:-$HOME/.ado-aw/logs}" + if [ -d "$ADO_AW_LOG_DIR" ]; then mkdir -p "$(Agent.TempDirectory)/staging/logs/ado-aw" - cp -r ~/.ado-aw/logs/* "$(Agent.TempDirectory)/staging/logs/ado-aw/" 2>/dev/null || true + cp -r "$ADO_AW_LOG_DIR"/* "$(Agent.TempDirectory)/staging/logs/ado-aw/" 2>/dev/null || true fi echo "Logs copied to $(Agent.TempDirectory)/staging/logs" ls -laR "$(Agent.TempDirectory)/staging/logs" 2>/dev/null || echo "No logs found" diff --git a/src/data/base.yml b/src/data/base.yml index 24a9ba80..d2e8c878 100644 --- a/src/data/base.yml +++ b/src/data/base.yml @@ -159,7 +159,7 @@ jobs: # (with trailing space). The value MUST be newline-free; is_safe_tool_name enforces this. # Positional args (output_directory, bounding_directory) MUST come after all named # options — clap parses them positionally and reordering would break the command. - nohup /tmp/awf-tools/ado-aw mcp-http \ + nohup /tmp/awf-tools/ado-aw {{ ado_aw_debug_flags }}mcp-http \ --port "$SAFE_OUTPUTS_PORT" \ --api-key "$SAFE_OUTPUTS_API_KEY" \ {{ enabled_tools_args }}"/tmp/awf-tools/staging" \ @@ -366,8 +366,9 @@ jobs: if [ -d "{{ engine_log_dir }}" ]; then cp -r "{{ engine_log_dir }}"/* "$(Agent.TempDirectory)/staging/logs/" 2>/dev/null || true fi - if [ -d ~/.ado-aw/logs ]; then - cp -r ~/.ado-aw/logs/* "$(Agent.TempDirectory)/staging/logs/" 2>/dev/null || true + ADO_AW_LOG_DIR="${ADO_AW_LOG_DIR:-$HOME/.ado-aw/logs}" + if [ -d "$ADO_AW_LOG_DIR" ]; then + cp -r "$ADO_AW_LOG_DIR"/* "$(Agent.TempDirectory)/staging/logs/" 2>/dev/null || true fi if [ -d /tmp/gh-aw/mcp-logs ]; then mkdir -p "$(Agent.TempDirectory)/staging/logs/mcpg" @@ -562,9 +563,10 @@ jobs: mkdir -p "$(Agent.TempDirectory)/analyzed_outputs/logs/copilot" cp -r "{{ engine_log_dir }}"/* "$(Agent.TempDirectory)/analyzed_outputs/logs/copilot/" 2>/dev/null || true fi - if [ -d ~/.ado-aw/logs ]; then + ADO_AW_LOG_DIR="${ADO_AW_LOG_DIR:-$HOME/.ado-aw/logs}" + if [ -d "$ADO_AW_LOG_DIR" ]; then mkdir -p "$(Agent.TempDirectory)/analyzed_outputs/logs/ado-aw" - cp -r ~/.ado-aw/logs/* "$(Agent.TempDirectory)/analyzed_outputs/logs/ado-aw/" 2>/dev/null || true + cp -r "$ADO_AW_LOG_DIR"/* "$(Agent.TempDirectory)/analyzed_outputs/logs/ado-aw/" 2>/dev/null || true fi echo "Logs copied to $(Agent.TempDirectory)/analyzed_outputs/logs" ls -laR "$(Agent.TempDirectory)/analyzed_outputs/logs" 2>/dev/null || echo "No logs found" @@ -621,7 +623,7 @@ jobs: displayName: "Prepare output directory" - bash: | - ado-aw execute --source "{{ source_path }}" --safe-output-dir "$(Pipeline.Workspace)/analyzed_outputs_$(Build.BuildId)" --output-dir "$(Agent.TempDirectory)/staging" + ado-aw {{ ado_aw_debug_flags }}execute --source "{{ source_path }}" --safe-output-dir "$(Pipeline.Workspace)/analyzed_outputs_$(Build.BuildId)" --output-dir "$(Agent.TempDirectory)/staging" EXIT_CODE=$? if [ $EXIT_CODE -eq 2 ]; then echo "##vso[task.complete result=SucceededWithIssues;]Executor completed with warnings" @@ -642,9 +644,10 @@ jobs: mkdir -p "$(Agent.TempDirectory)/staging/logs/copilot" cp -r "{{ engine_log_dir }}"/* "$(Agent.TempDirectory)/staging/logs/copilot/" 2>/dev/null || true fi - if [ -d ~/.ado-aw/logs ]; then + ADO_AW_LOG_DIR="${ADO_AW_LOG_DIR:-$HOME/.ado-aw/logs}" + if [ -d "$ADO_AW_LOG_DIR" ]; then mkdir -p "$(Agent.TempDirectory)/staging/logs/ado-aw" - cp -r ~/.ado-aw/logs/* "$(Agent.TempDirectory)/staging/logs/ado-aw/" 2>/dev/null || true + cp -r "$ADO_AW_LOG_DIR"/* "$(Agent.TempDirectory)/staging/logs/ado-aw/" 2>/dev/null || true fi echo "Logs copied to $(Agent.TempDirectory)/staging/logs" ls -laR "$(Agent.TempDirectory)/staging/logs" 2>/dev/null || echo "No logs found" diff --git a/src/logging.rs b/src/logging.rs index 149855fb..cfd4f13b 100644 --- a/src/logging.rs +++ b/src/logging.rs @@ -9,28 +9,52 @@ use chrono::{Local, Utc}; use log::LevelFilter; use std::fs::{self, File}; use std::io::Write; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::sync::Mutex; /// Get the standard log directory path /// -/// Returns `$HOME/.ado-aw/logs/` on Unix/macOS -/// Returns `%USERPROFILE%\.ado-aw\logs\` on Windows +/// Resolution order: +/// 1. CLI override (`--log-output-dir`) +/// 2. `ADO_AW_LOG_DIR` env var +/// 3. Default (`$HOME/.ado-aw/logs` or `%USERPROFILE%\.ado-aw\logs`) pub fn log_directory() -> Result { + log_directory_with_override(None) +} + +/// Resolve log directory, optionally overriding with a CLI-provided path. +pub fn log_directory_with_override(output_dir_override: Option<&Path>) -> Result { + if let Some(path) = output_dir_override { + return Ok(path.to_path_buf()); + } + if let Ok(from_env) = std::env::var("ADO_AW_LOG_DIR") { + let trimmed = from_env.trim(); + if !trimmed.is_empty() { + return Ok(PathBuf::from(trimmed)); + } + } let home = dirs::home_dir().context("Could not determine home directory")?; Ok(home.join(".ado-aw").join("logs")) } /// Get the path for today's log file pub fn daily_log_path() -> Result { - let log_dir = log_directory()?; + daily_log_path_with_override(None) +} + +fn daily_log_path_with_override(output_dir_override: Option<&Path>) -> Result { + let log_dir = log_directory_with_override(output_dir_override)?; let date = Local::now().format("%Y-%m-%d"); Ok(log_dir.join(format!("{}.log", date))) } /// Ensure the log directory exists pub fn ensure_log_directory() -> Result { - let log_dir = log_directory()?; + ensure_log_directory_with_override(None) +} + +fn ensure_log_directory_with_override(output_dir_override: Option<&Path>) -> Result { + let log_dir = log_directory_with_override(output_dir_override)?; fs::create_dir_all(&log_dir).context("Failed to create log directory")?; Ok(log_dir) } @@ -111,12 +135,17 @@ impl log::Log for FileLogger { /// # Arguments /// * `command_name` - Name of the command (included in session marker) /// * `level` - Minimum log level to capture +/// * `output_dir_override` - Optional directory override for log output /// /// # Returns /// Path to the log file, or error if initialization failed -pub fn init_file_logging(command_name: &str, level: LevelFilter) -> Result { - ensure_log_directory()?; - let log_path = daily_log_path()?; +pub fn init_file_logging( + command_name: &str, + level: LevelFilter, + output_dir_override: Option<&Path>, +) -> Result { + ensure_log_directory_with_override(output_dir_override)?; + let log_path = daily_log_path_with_override(output_dir_override)?; // Open log file in append mode let file = fs::OpenOptions::new() @@ -152,10 +181,16 @@ pub fn init_file_logging(command_name: &str, level: LevelFilter) -> Result Option { +pub fn init_logging( + command_name: &str, + debug: bool, + verbose: bool, + output_dir_override: Option<&Path>, +) -> Option { let level = if debug { LevelFilter::Debug } else if verbose { @@ -168,7 +203,7 @@ pub fn init_logging(command_name: &str, debug: bool, verbose: bool) -> Option { log::debug!("Logging to: {}", path.display()); Some(path) @@ -196,6 +231,7 @@ pub fn init_logging(command_name: &str, debug: bool, verbose: bool) -> Option, #[command(subcommand)] command: Option, } @@ -337,8 +340,13 @@ async fn main() -> Result<()> { None => "ado-aw", }; - // Initialize file-based logging to $HOME/.ado-aw/logs/{command}.log - let _log_path = logging::init_logging(command_name, args.debug, args.verbose); + // Initialize file-based logging to a daily log file. + let _log_path = logging::init_logging( + command_name, + args.debug, + args.verbose, + args.log_output_dir.as_deref(), + ); let Some(command) = args.command else { println!("No subcommand was used. Try `compile `"); From dc24b5e255cc7064a899b4ff751e8f1dd886d06f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 7 May 2026 09:17:40 +0000 Subject: [PATCH 2/4] fix(logging): remove unused wrapper fns and keep override-based path resolution Agent-Logs-Url: https://github.com/githubnext/ado-aw/sessions/78e8e09d-bd5a-43f7-83e6-6adf4097b88c Co-authored-by: jamesadevine <4742697+jamesadevine@users.noreply.github.com> --- src/logging.rs | 31 ++++++++----------------------- 1 file changed, 8 insertions(+), 23 deletions(-) diff --git a/src/logging.rs b/src/logging.rs index cfd4f13b..f56eb7a9 100644 --- a/src/logging.rs +++ b/src/logging.rs @@ -12,18 +12,13 @@ use std::io::Write; use std::path::{Path, PathBuf}; use std::sync::Mutex; -/// Get the standard log directory path +/// Resolve log directory, optionally overriding with a CLI-provided path. /// /// Resolution order: /// 1. CLI override (`--log-output-dir`) /// 2. `ADO_AW_LOG_DIR` env var /// 3. Default (`$HOME/.ado-aw/logs` or `%USERPROFILE%\.ado-aw\logs`) -pub fn log_directory() -> Result { - log_directory_with_override(None) -} - -/// Resolve log directory, optionally overriding with a CLI-provided path. -pub fn log_directory_with_override(output_dir_override: Option<&Path>) -> Result { +fn log_directory(output_dir_override: Option<&Path>) -> Result { if let Some(path) = output_dir_override { return Ok(path.to_path_buf()); } @@ -37,24 +32,14 @@ pub fn log_directory_with_override(output_dir_override: Option<&Path>) -> Result Ok(home.join(".ado-aw").join("logs")) } -/// Get the path for today's log file -pub fn daily_log_path() -> Result { - daily_log_path_with_override(None) -} - fn daily_log_path_with_override(output_dir_override: Option<&Path>) -> Result { - let log_dir = log_directory_with_override(output_dir_override)?; + let log_dir = log_directory(output_dir_override)?; let date = Local::now().format("%Y-%m-%d"); Ok(log_dir.join(format!("{}.log", date))) } -/// Ensure the log directory exists -pub fn ensure_log_directory() -> Result { - ensure_log_directory_with_override(None) -} - fn ensure_log_directory_with_override(output_dir_override: Option<&Path>) -> Result { - let log_dir = log_directory_with_override(output_dir_override)?; + let log_dir = log_directory(output_dir_override)?; fs::create_dir_all(&log_dir).context("Failed to create log directory")?; Ok(log_dir) } @@ -235,7 +220,7 @@ mod tests { #[test] fn test_log_directory() { - let dir = log_directory().unwrap(); + let dir = log_directory(None).unwrap(); assert!( dir.ends_with(".ado-aw/logs") || dir.ends_with(".ado-aw\\logs") ); @@ -243,7 +228,7 @@ mod tests { #[test] fn test_daily_log_path() { - let path = daily_log_path().unwrap(); + let path = daily_log_path_with_override(None).unwrap(); let filename = path.file_name().unwrap().to_string_lossy(); // Should be YYYY-MM-DD.log format assert!(filename.ends_with(".log")); @@ -252,14 +237,14 @@ mod tests { #[test] fn test_ensure_log_directory() { - let dir = ensure_log_directory().unwrap(); + let dir = ensure_log_directory_with_override(None).unwrap(); assert!(dir.exists()); } #[test] fn test_log_directory_override() { let temp = tempdir().unwrap(); - let dir = log_directory_with_override(Some(temp.path())).unwrap(); + let dir = log_directory(Some(temp.path())).unwrap(); assert_eq!(dir, temp.path()); } From 29e77a613b5d82f9b7ad5ab4447069ab7724e3e1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 7 May 2026 09:44:31 +0000 Subject: [PATCH 3/4] fix(logging): always write debug-level logs to file while keeping stderr verbosity Agent-Logs-Url: https://github.com/githubnext/ado-aw/sessions/fc1c4059-6c5b-40a3-b88d-c0a5baad0498 Co-authored-by: jamesadevine <4742697+jamesadevine@users.noreply.github.com> --- src/logging.rs | 75 +++++++++++++++++++++++++++++++------------------- 1 file changed, 47 insertions(+), 28 deletions(-) diff --git a/src/logging.rs b/src/logging.rs index f56eb7a9..710c2a87 100644 --- a/src/logging.rs +++ b/src/logging.rs @@ -12,6 +12,8 @@ use std::io::Write; use std::path::{Path, PathBuf}; use std::sync::Mutex; +const FILE_LOG_LEVEL: LevelFilter = LevelFilter::Debug; + /// Resolve log directory, optionally overriding with a CLI-provided path. /// /// Resolution order: @@ -75,12 +77,13 @@ fn build_session_marker(command_name: &str) -> String { /// A simple file logger that implements log::Log struct FileLogger { file: Mutex, - level: LevelFilter, + file_level: LevelFilter, + stderr_level: LevelFilter, } impl log::Log for FileLogger { fn enabled(&self, metadata: &log::Metadata) -> bool { - metadata.level() <= self.level + metadata.level() <= self.file_level || metadata.level() <= self.stderr_level } fn log(&self, record: &log::Record) { @@ -94,14 +97,18 @@ impl log::Log for FileLogger { record.args() ); - // Write to file - if let Ok(mut file) = self.file.lock() { - let _ = file.write_all(message.as_bytes()); - let _ = file.flush(); + // Write to file (always capture debug+) + if record.level() <= self.file_level { + if let Ok(mut file) = self.file.lock() { + let _ = file.write_all(message.as_bytes()); + let _ = file.flush(); + } } - // Also write to stderr for immediate visibility - eprint!("{}", message); + // Write to stderr according to selected runtime verbosity + if record.level() <= self.stderr_level { + eprint!("{}", message); + } } } @@ -119,14 +126,14 @@ impl log::Log for FileLogger { /// /// # Arguments /// * `command_name` - Name of the command (included in session marker) -/// * `level` - Minimum log level to capture +/// * `stderr_level` - Runtime verbosity for stderr output /// * `output_dir_override` - Optional directory override for log output /// /// # Returns /// Path to the log file, or error if initialization failed pub fn init_file_logging( command_name: &str, - level: LevelFilter, + stderr_level: LevelFilter, output_dir_override: Option<&Path>, ) -> Result { ensure_log_directory_with_override(output_dir_override)?; @@ -148,11 +155,12 @@ pub fn init_file_logging( let logger = FileLogger { file: Mutex::new(file), - level, + file_level: FILE_LOG_LEVEL, + stderr_level, }; log::set_boxed_logger(Box::new(logger)) - .map(|()| log::set_max_level(level)) + .map(|()| log::set_max_level(FILE_LOG_LEVEL.max(stderr_level))) .context("Failed to set logger")?; Ok(log_path) @@ -170,25 +178,25 @@ pub fn init_file_logging( /// /// # Returns /// Path to the log file if file logging was initialized +fn selected_stderr_level(debug: bool, verbose: bool, rust_log_set: bool) -> LevelFilter { + if debug { + LevelFilter::Debug + } else if verbose || rust_log_set { + LevelFilter::Info + } else { + LevelFilter::Warn + } +} + pub fn init_logging( command_name: &str, debug: bool, verbose: bool, output_dir_override: Option<&Path>, ) -> Option { - let level = if debug { - LevelFilter::Debug - } else if verbose { - LevelFilter::Info - } else if std::env::var("RUST_LOG").is_ok() { - // If RUST_LOG is set, use Info as minimum for file logging - LevelFilter::Info - } else { - // Default: only warnings and errors - LevelFilter::Warn - }; + let stderr_level = selected_stderr_level(debug, verbose, std::env::var("RUST_LOG").is_ok()); - match init_file_logging(command_name, level, output_dir_override) { + match init_file_logging(command_name, stderr_level, output_dir_override) { Ok(path) => { log::debug!("Logging to: {}", path.display()); Some(path) @@ -199,12 +207,12 @@ pub fn init_logging( // Use env_logger as fallback let mut builder = env_logger::Builder::new(); - if debug { - builder.filter_level(LevelFilter::Debug); - } else if verbose { - builder.filter_level(LevelFilter::Info); + if debug || verbose { + builder.filter_level(stderr_level); } else if let Ok(rust_log) = std::env::var("RUST_LOG") { builder.parse_filters(&rust_log); + } else { + builder.filter_level(stderr_level); } let _ = builder.try_init(); @@ -255,4 +263,15 @@ mod tests { assert!(marker.contains("COMMAND=test-command")); assert!(marker.ends_with(" ===")); } + + #[test] + fn test_selected_stderr_level() { + assert_eq!( + selected_stderr_level(true, false, false), + LevelFilter::Debug + ); + assert_eq!(selected_stderr_level(false, true, false), LevelFilter::Info); + assert_eq!(selected_stderr_level(false, false, true), LevelFilter::Info); + assert_eq!(selected_stderr_level(false, false, false), LevelFilter::Warn); + } } From 8473a3870b5bca870fd394b1e4652e55679a1772 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 7 May 2026 10:00:46 +0000 Subject: [PATCH 4/4] fix(compile): stop injecting ado-aw debug flags for --debug-pipeline Agent-Logs-Url: https://github.com/githubnext/ado-aw/sessions/b9291c25-ff23-4838-a2d9-e2a050dd7aa4 Co-authored-by: jamesadevine <4742697+jamesadevine@users.noreply.github.com> --- docs/template-markers.md | 6 ------ src/compile/common.rs | 15 +++------------ src/data/1es-base.yml | 4 ++-- src/data/base.yml | 4 ++-- 4 files changed, 7 insertions(+), 22 deletions(-) diff --git a/docs/template-markers.md b/docs/template-markers.md index f5c749d0..a5fe6dae 100644 --- a/docs/template-markers.md +++ b/docs/template-markers.md @@ -279,12 +279,6 @@ Generates a pipeline step that probes each configured MCPG backend with an MCP i When `--debug-pipeline` is not passed (the default), this placeholder is replaced with an empty string. -## {{ ado_aw_debug_flags }} - -Generates ado-aw CLI debug flags in runtime script invocations. When `--debug-pipeline` is passed (debug builds only), this inserts `--debug ` so ado-aw writes detailed debug-level logs. - -When `--debug-pipeline` is not passed, this placeholder is replaced with an empty string. - ## {{ pr_trigger }} Generates PR trigger configuration. When a schedule or pipeline trigger is configured, this generates `pr: none` to disable PR triggers. Otherwise, it generates an empty string, allowing the default PR trigger behavior. diff --git a/src/compile/common.rs b/src/compile/common.rs index c6f07ace..207c5d5f 100644 --- a/src/compile/common.rs +++ b/src/compile/common.rs @@ -708,9 +708,8 @@ pub fn generate_integrity_check(skip: bool) -> String { /// stderr dump on health-check failure /// - `{{ verify_mcp_backends }}`: full pipeline step that probes each MCPG /// backend with MCP initialize + tools/list -/// - `{{ ado_aw_debug_flags }}`: `--debug ` for ado-aw CLI invocations /// -/// When `debug` is `false`, both markers resolve to empty strings. +/// When `debug` is `false`, debug markers resolve to empty strings. pub fn generate_debug_pipeline_replacements(debug: bool) -> Vec<(String, String)> { if !debug { return vec![ @@ -718,7 +717,6 @@ pub fn generate_debug_pipeline_replacements(debug: bool) -> Vec<(String, String) // generate_mcpg_docker_env when no env flags are needed). ("{{ mcpg_debug_flags }}".into(), "\\".into()), ("{{ verify_mcp_backends }}".into(), String::new()), - ("{{ ado_aw_debug_flags }}".into(), String::new()), ]; } @@ -789,7 +787,6 @@ pub fn generate_debug_pipeline_replacements(debug: bool) -> Vec<(String, String) vec![ ("{{ mcpg_debug_flags }}".into(), mcpg_debug_flags), ("{{ verify_mcp_backends }}".into(), verify_mcp_backends), - ("{{ ado_aw_debug_flags }}".into(), "--debug ".into()), ] } @@ -3229,21 +3226,19 @@ mod tests { #[test] fn test_debug_pipeline_replacements_disabled() { let replacements = generate_debug_pipeline_replacements(false); - assert_eq!(replacements.len(), 3); + assert_eq!(replacements.len(), 2); // mcpg_debug_flags returns `\` for bash line continuation let flags = replacements.iter().find(|(m, _)| m == "{{ mcpg_debug_flags }}").unwrap(); assert_eq!(flags.1, "\\", "mcpg_debug_flags should be a bare backslash when disabled"); // verify_mcp_backends should be empty let probe = replacements.iter().find(|(m, _)| m == "{{ verify_mcp_backends }}").unwrap(); assert!(probe.1.is_empty(), "verify_mcp_backends should be empty when disabled"); - let ado_aw_flags = replacements.iter().find(|(m, _)| m == "{{ ado_aw_debug_flags }}").unwrap(); - assert!(ado_aw_flags.1.is_empty(), "ado_aw_debug_flags should be empty when disabled"); } #[test] fn test_debug_pipeline_replacements_enabled() { let replacements = generate_debug_pipeline_replacements(true); - assert_eq!(replacements.len(), 3); + assert_eq!(replacements.len(), 2); let flags = replacements.iter().find(|(m, _)| m == "{{ mcpg_debug_flags }}"); assert!(flags.is_some(), "Should have mcpg_debug_flags marker"); @@ -3257,10 +3252,6 @@ mod tests { assert!(probe_value.contains("tools/list"), "Should contain tools/list probe"); assert!(probe_value.contains("initialize"), "Should contain initialize handshake"); assert!(probe_value.contains("MCPG_API_KEY"), "Should contain API key env mapping"); - - let ado_aw_flags = replacements.iter().find(|(m, _)| m == "{{ ado_aw_debug_flags }}"); - assert!(ado_aw_flags.is_some(), "Should have ado_aw_debug_flags marker"); - assert_eq!(&ado_aw_flags.unwrap().1, "--debug "); } // ─── validate_submit_pr_review_events ──────────────────────────────────── diff --git a/src/data/1es-base.yml b/src/data/1es-base.yml index f9e7cc49..6cc2069e 100644 --- a/src/data/1es-base.yml +++ b/src/data/1es-base.yml @@ -188,7 +188,7 @@ extends: # (with trailing space). The value MUST be newline-free; is_safe_tool_name enforces this. # Positional args (output_directory, bounding_directory) MUST come after all named # options — clap parses them positionally and reordering would break the command. - nohup /tmp/awf-tools/ado-aw {{ ado_aw_debug_flags }}mcp-http \ + nohup /tmp/awf-tools/ado-aw mcp-http \ --port "$SAFE_OUTPUTS_PORT" \ --api-key "$SAFE_OUTPUTS_API_KEY" \ {{ enabled_tools_args }}"/tmp/awf-tools/staging" \ @@ -655,7 +655,7 @@ extends: displayName: "Prepare output directory" - bash: | - ado-aw {{ ado_aw_debug_flags }}execute --source "{{ source_path }}" --safe-output-dir "$(Pipeline.Workspace)/analyzed_outputs_$(Build.BuildId)" --output-dir "$(Agent.TempDirectory)/staging" + ado-aw execute --source "{{ source_path }}" --safe-output-dir "$(Pipeline.Workspace)/analyzed_outputs_$(Build.BuildId)" --output-dir "$(Agent.TempDirectory)/staging" EXIT_CODE=$? if [ $EXIT_CODE -eq 2 ]; then echo "##vso[task.complete result=SucceededWithIssues;]Executor completed with warnings" diff --git a/src/data/base.yml b/src/data/base.yml index d2e8c878..a423a1ea 100644 --- a/src/data/base.yml +++ b/src/data/base.yml @@ -159,7 +159,7 @@ jobs: # (with trailing space). The value MUST be newline-free; is_safe_tool_name enforces this. # Positional args (output_directory, bounding_directory) MUST come after all named # options — clap parses them positionally and reordering would break the command. - nohup /tmp/awf-tools/ado-aw {{ ado_aw_debug_flags }}mcp-http \ + nohup /tmp/awf-tools/ado-aw mcp-http \ --port "$SAFE_OUTPUTS_PORT" \ --api-key "$SAFE_OUTPUTS_API_KEY" \ {{ enabled_tools_args }}"/tmp/awf-tools/staging" \ @@ -623,7 +623,7 @@ jobs: displayName: "Prepare output directory" - bash: | - ado-aw {{ ado_aw_debug_flags }}execute --source "{{ source_path }}" --safe-output-dir "$(Pipeline.Workspace)/analyzed_outputs_$(Build.BuildId)" --output-dir "$(Agent.TempDirectory)/staging" + ado-aw execute --source "{{ source_path }}" --safe-output-dir "$(Pipeline.Workspace)/analyzed_outputs_$(Build.BuildId)" --output-dir "$(Agent.TempDirectory)/staging" EXIT_CODE=$? if [ $EXIT_CODE -eq 2 ]; then echo "##vso[task.complete result=SucceededWithIssues;]Executor completed with warnings"