diff --git a/src/compile/common.rs b/src/compile/common.rs index 9aed8350..66979708 100644 --- a/src/compile/common.rs +++ b/src/compile/common.rs @@ -1113,6 +1113,7 @@ pub fn generate_integrity_check(skip: bool) -> String { // Indentation is handled by replace_with_indent at the call site. r#"- bash: | + set -eo pipefail AGENTIC_PIPELINES_PATH="$(Pipeline.Workspace)/agentic-pipeline-compiler/ado-aw" chmod +x "$AGENTIC_PIPELINES_PATH" $AGENTIC_PIPELINES_PATH check "{{ pipeline_path }}" @@ -1147,6 +1148,7 @@ pub fn generate_debug_pipeline_replacements(debug: bool) -> Vec<(String, String) # step, a broken backend (e.g., npx timeout) only surfaces as a silent # missing-tool error during the agent run. - bash: | + set -eo pipefail echo "=== Probing MCP backends ===" PROBE_FAILED=false for server in $(jq -r '.mcpServers | keys[]' /tmp/awf-tools/mcp-config.json); do @@ -2302,6 +2304,7 @@ pub fn generate_awf_path_step(awf_paths: &[String]) -> String { format!( "\ - bash: | + set -eo pipefail AWF_PATH_FILE=\"/tmp/awf-tools/ado-path-entries\" cat > \"$AWF_PATH_FILE\" << AWF_PATH_EOF {path_lines} diff --git a/src/compile/extensions/mod.rs b/src/compile/extensions/mod.rs index 7853fa87..ba4a9902 100644 --- a/src/compile/extensions/mod.rs +++ b/src/compile/extensions/mod.rs @@ -732,6 +732,7 @@ pub fn wrap_prompt_append(content: &str, display_name: &str) -> Result { Ok(format!( r#"- bash: | + set -eo pipefail cat >> "/tmp/awf-tools/agent-prompt.md" << '{delimiter}' {indented_content} {delimiter} diff --git a/src/compile/extensions/trigger_filters.rs b/src/compile/extensions/trigger_filters.rs index a0d84a6d..1b0be9cc 100644 --- a/src/compile/extensions/trigger_filters.rs +++ b/src/compile/extensions/trigger_filters.rs @@ -101,6 +101,7 @@ impl CompilerExtension for TriggerFiltersExtension { let mut steps = Vec::new(); steps.push(format!( r#"- bash: | + set -eo pipefail mkdir -p /tmp/ado-aw-scripts curl -fsSL "{RELEASE_BASE_URL}/v{version}/checksums.txt" -o /tmp/ado-aw-scripts/checksums.txt curl -fsSL "{RELEASE_BASE_URL}/v{version}/scripts.zip" -o /tmp/ado-aw-scripts/scripts.zip diff --git a/src/data/1es-base.yml b/src/data/1es-base.yml index 920da5eb..9cc28e81 100644 --- a/src/data/1es-base.yml +++ b/src/data/1es-base.yml @@ -59,6 +59,7 @@ extends: {{ engine_install_steps }} - bash: | + set -eo pipefail COMPILER_VERSION="{{ compiler_version }}" DOWNLOAD_DIR="$(Pipeline.Workspace)/agentic-pipeline-compiler" DOWNLOAD_URL="https://github.com/githubnext/ado-aw/releases/download/v${COMPILER_VERSION}/ado-aw-linux-x64" @@ -79,6 +80,7 @@ extends: {{ integrity_check }} - bash: | + set -eo pipefail mkdir -p "$(Agent.TempDirectory)/staging" # Generate MCPG API key early so it's available as an ADO secret variable @@ -106,6 +108,7 @@ extends: displayName: "Prepare MCPG config" - bash: | + set -eo pipefail mkdir -p /tmp/awf-tools/staging echo "HOME: $HOME" @@ -128,6 +131,7 @@ extends: displayName: "Prepare tooling" - bash: | + set -eo pipefail # Write agent instructions to /tmp so it's accessible inside AWF container cat > "/tmp/awf-tools/agent-prompt.md" << 'AGENT_PROMPT_EOF' {{ agent_content }} @@ -180,6 +184,7 @@ extends: # Start SafeOutputs HTTP server on host (MCPG proxies to it) - bash: | + set -eo pipefail SAFE_OUTPUTS_PORT=8100 SAFE_OUTPUTS_API_KEY=$(openssl rand -base64 45 | tr -d '/+=') echo "##vso[task.setvariable variable=SAFE_OUTPUTS_PORT]$SAFE_OUTPUTS_PORT" @@ -220,6 +225,7 @@ extends: # Start MCP Gateway (MCPG) on host - bash: | + set -eo pipefail # Substitute runtime values into MCPG config MCPG_CONFIG=$(cat /tmp/awf-tools/staging/mcpg-config.json \ | sed "s|\${SAFE_OUTPUTS_PORT}|$(SAFE_OUTPUTS_PORT)|g" \ @@ -323,7 +329,7 @@ extends: # Network isolation via AWF (Agentic Workflow Firewall) - bash: | - set -o pipefail + set -eo pipefail AGENT_OUTPUT_FILE="$(Agent.TempDirectory)/staging/logs/agent-output.txt" mkdir -p "$(Agent.TempDirectory)/staging/logs" @@ -368,6 +374,7 @@ extends: {{ engine_env }} - bash: | + set -eo pipefail # Copy safe outputs from /tmp back to staging for artifact publish mkdir -p "$(Agent.TempDirectory)/staging" cp -r /tmp/awf-tools/staging/* "$(Agent.TempDirectory)/staging/" 2>/dev/null || true @@ -377,6 +384,7 @@ extends: condition: always() - bash: | + set -eo pipefail # Stop MCPG container echo "Stopping MCPG..." docker stop mcpg 2>/dev/null || true @@ -394,6 +402,7 @@ extends: {{ finalize_steps }} - bash: | + set -eo pipefail # Copy all logs to output directory for artifact upload mkdir -p "$(Agent.TempDirectory)/staging/logs" if [ -d "{{ engine_log_dir }}" ]; then @@ -433,6 +442,7 @@ extends: {{ engine_install_steps }} - bash: | + set -eo pipefail COMPILER_VERSION="{{ compiler_version }}" DOWNLOAD_DIR="$(Pipeline.Workspace)/agentic-pipeline-compiler" DOWNLOAD_URL="https://github.com/githubnext/ado-aw/releases/download/v${COMPILER_VERSION}/ado-aw-linux-x64" @@ -487,11 +497,13 @@ extends: displayName: "Pre-pull AWF container images (v{{ firewall_version }})" - bash: | + set -eo pipefail mkdir -p {{ working_directory }}/safe_outputs cp -a "$(Pipeline.Workspace)/agent_outputs_$(Build.BuildId)/." {{ working_directory }}/safe_outputs displayName: "Prepare safe outputs for analysis" - bash: | + set -eo pipefail # Write threat analysis prompt to /tmp (accessible inside AWF container) cat > "/tmp/awf-tools/threat-analysis-prompt.md" << 'THREAT_ANALYSIS_EOF' {{ threat_analysis_prompt }} @@ -502,12 +514,13 @@ extends: displayName: "Prepare threat analysis prompt" - bash: | + set -eo pipefail AGENTIC_PIPELINES_PATH="$(Pipeline.Workspace)/agentic-pipeline-compiler/ado-aw" chmod +x "$AGENTIC_PIPELINES_PATH" displayName: "Setup agentic pipeline compiler" - bash: | - set -o pipefail + set -eo pipefail # Run threat analysis with AWF network isolation THREAT_OUTPUT_FILE="$(Agent.TempDirectory)/threat-analysis-output.txt" @@ -534,6 +547,7 @@ extends: GITHUB_READ_ONLY: 1 - bash: | + set -eo pipefail # Create analyzed outputs directory with original safe outputs and analysis mkdir -p "$(Agent.TempDirectory)/analyzed_outputs" @@ -547,7 +561,7 @@ extends: # Extract JSON from THREAT_DETECTION_RESULT line in threat analysis output if [ -f "$(Agent.TempDirectory)/threat-analysis-output.txt" ]; then - RESULT_LINE=$(grep "THREAT_DETECTION_RESULT:" "$(Agent.TempDirectory)/threat-analysis-output.txt" | tail -1) + RESULT_LINE=$(grep "THREAT_DETECTION_RESULT:" "$(Agent.TempDirectory)/threat-analysis-output.txt" | tail -1 || true) if [ -n "$RESULT_LINE" ]; then # Extract JSON after the prefix JSON_CONTENT=$(echo "$RESULT_LINE" | sed 's/.*THREAT_DETECTION_RESULT://') @@ -567,6 +581,7 @@ extends: condition: always() - bash: | + set -eo pipefail SAFE_TO_PROCESS="false" JSON_FILE="$(Agent.TempDirectory)/analyzed_outputs/threat-analysis.json" @@ -596,6 +611,7 @@ extends: condition: always() - bash: | + set -eo pipefail # Copy all logs to analyzed outputs for artifact upload mkdir -p "$(Agent.TempDirectory)/analyzed_outputs/logs" if [ -d "{{ engine_log_dir }}" ]; then @@ -635,6 +651,7 @@ extends: artifact: analyzed_outputs_$(Build.BuildId) - bash: | + set -eo pipefail COMPILER_VERSION="{{ compiler_version }}" DOWNLOAD_DIR="$(Pipeline.Workspace)/agentic-pipeline-compiler" DOWNLOAD_URL="https://github.com/githubnext/ado-aw/releases/download/v${COMPILER_VERSION}/ado-aw-linux-x64" @@ -653,18 +670,21 @@ extends: displayName: "Download agentic pipeline compiler (v{{ compiler_version }})" - bash: | + set -eo pipefail ls -la "$(Pipeline.Workspace)/agentic-pipeline-compiler" chmod +x "$(Pipeline.Workspace)/agentic-pipeline-compiler/ado-aw" echo "##vso[task.prependpath]$(Pipeline.Workspace)/agentic-pipeline-compiler" displayName: Add agentic compiler to path - bash: | + set -eo pipefail mkdir -p "$(Agent.TempDirectory)/staging" 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" - EXIT_CODE=$? + set -eo pipefail + ado-aw execute --source "{{ source_path }}" --safe-output-dir "$(Pipeline.Workspace)/analyzed_outputs_$(Build.BuildId)" --output-dir "$(Agent.TempDirectory)/staging" \ + && EXIT_CODE=0 || EXIT_CODE=$? if [ $EXIT_CODE -eq 2 ]; then echo "##vso[task.complete result=SucceededWithIssues;]Executor completed with warnings" exit 0 @@ -675,6 +695,7 @@ extends: {{ executor_ado_env }} - bash: | + set -eo pipefail # Copy all logs to output directory for artifact upload mkdir -p "$(Agent.TempDirectory)/staging/logs" # Copy agent output log from analyzed_outputs for optimisation use diff --git a/src/data/base.yml b/src/data/base.yml index d5ca1500..3931200b 100644 --- a/src/data/base.yml +++ b/src/data/base.yml @@ -30,6 +30,7 @@ jobs: {{ engine_install_steps }} - bash: | + set -eo pipefail COMPILER_VERSION="{{ compiler_version }}" DOWNLOAD_DIR="$(Pipeline.Workspace)/agentic-pipeline-compiler" DOWNLOAD_URL="https://github.com/githubnext/ado-aw/releases/download/v${COMPILER_VERSION}/ado-aw-linux-x64" @@ -50,6 +51,7 @@ jobs: {{ integrity_check }} - bash: | + set -eo pipefail mkdir -p "$(Agent.TempDirectory)/staging" # Generate MCPG API key early so it's available as an ADO secret variable @@ -77,6 +79,7 @@ jobs: displayName: "Prepare MCPG config" - bash: | + set -eo pipefail mkdir -p /tmp/awf-tools/staging echo "HOME: $HOME" @@ -99,6 +102,7 @@ jobs: displayName: "Prepare tooling" - bash: | + set -eo pipefail # Write agent instructions to /tmp so it's accessible inside AWF container cat > "/tmp/awf-tools/agent-prompt.md" << 'AGENT_PROMPT_EOF' {{ agent_content }} @@ -151,6 +155,7 @@ jobs: # Start SafeOutputs HTTP server on host (MCPG proxies to it) - bash: | + set -eo pipefail SAFE_OUTPUTS_PORT=8100 SAFE_OUTPUTS_API_KEY=$(openssl rand -base64 45 | tr -d '/+=') echo "##vso[task.setvariable variable=SAFE_OUTPUTS_PORT]$SAFE_OUTPUTS_PORT" @@ -191,6 +196,7 @@ jobs: # Start MCP Gateway (MCPG) on host - bash: | + set -eo pipefail # Substitute runtime values into MCPG config MCPG_CONFIG=$(cat /tmp/awf-tools/staging/mcpg-config.json \ | sed "s|\${SAFE_OUTPUTS_PORT}|$(SAFE_OUTPUTS_PORT)|g" \ @@ -294,7 +300,7 @@ jobs: # Network isolation via AWF (Agentic Workflow Firewall) - bash: | - set -o pipefail + set -eo pipefail AGENT_OUTPUT_FILE="$(Agent.TempDirectory)/staging/logs/agent-output.txt" mkdir -p "$(Agent.TempDirectory)/staging/logs" @@ -339,6 +345,7 @@ jobs: {{ engine_env }} - bash: | + set -eo pipefail # Copy safe outputs from /tmp back to staging for artifact publish mkdir -p "$(Agent.TempDirectory)/staging" cp -r /tmp/awf-tools/staging/* "$(Agent.TempDirectory)/staging/" 2>/dev/null || true @@ -348,6 +355,7 @@ jobs: condition: always() - bash: | + set -eo pipefail # Stop MCPG container echo "Stopping MCPG..." docker stop mcpg 2>/dev/null || true @@ -365,6 +373,7 @@ jobs: {{ finalize_steps }} - bash: | + set -eo pipefail # Copy all logs to output directory for artifact upload mkdir -p "$(Agent.TempDirectory)/staging/logs" if [ -d "{{ engine_log_dir }}" ]; then @@ -402,6 +411,7 @@ jobs: {{ engine_install_steps }} - bash: | + set -eo pipefail COMPILER_VERSION="{{ compiler_version }}" DOWNLOAD_DIR="$(Pipeline.Workspace)/agentic-pipeline-compiler" DOWNLOAD_URL="https://github.com/githubnext/ado-aw/releases/download/v${COMPILER_VERSION}/ado-aw-linux-x64" @@ -456,11 +466,13 @@ jobs: displayName: "Pre-pull AWF container images (v{{ firewall_version }})" - bash: | + set -eo pipefail mkdir -p {{ working_directory }}/safe_outputs cp -a "$(Pipeline.Workspace)/agent_outputs_$(Build.BuildId)/." {{ working_directory }}/safe_outputs displayName: "Prepare safe outputs for analysis" - bash: | + set -eo pipefail # Write threat analysis prompt to /tmp (accessible inside AWF container) cat > "/tmp/awf-tools/threat-analysis-prompt.md" << 'THREAT_ANALYSIS_EOF' {{ threat_analysis_prompt }} @@ -471,12 +483,13 @@ jobs: displayName: "Prepare threat analysis prompt" - bash: | + set -eo pipefail AGENTIC_PIPELINES_PATH="$(Pipeline.Workspace)/agentic-pipeline-compiler/ado-aw" chmod +x "$AGENTIC_PIPELINES_PATH" displayName: "Setup agentic pipeline compiler" - bash: | - set -o pipefail + set -eo pipefail # Run threat analysis with AWF network isolation THREAT_OUTPUT_FILE="$(Agent.TempDirectory)/threat-analysis-output.txt" @@ -503,6 +516,7 @@ jobs: GITHUB_READ_ONLY: 1 - bash: | + set -eo pipefail # Create analyzed outputs directory with original safe outputs and analysis mkdir -p "$(Agent.TempDirectory)/analyzed_outputs" @@ -516,7 +530,7 @@ jobs: # Extract JSON from THREAT_DETECTION_RESULT line in threat analysis output if [ -f "$(Agent.TempDirectory)/threat-analysis-output.txt" ]; then - RESULT_LINE=$(grep "THREAT_DETECTION_RESULT:" "$(Agent.TempDirectory)/threat-analysis-output.txt" | tail -1) + RESULT_LINE=$(grep "THREAT_DETECTION_RESULT:" "$(Agent.TempDirectory)/threat-analysis-output.txt" | tail -1 || true) if [ -n "$RESULT_LINE" ]; then # Extract JSON after the prefix JSON_CONTENT=$(echo "$RESULT_LINE" | sed 's/.*THREAT_DETECTION_RESULT://') @@ -536,6 +550,7 @@ jobs: condition: always() - bash: | + set -eo pipefail SAFE_TO_PROCESS="false" JSON_FILE="$(Agent.TempDirectory)/analyzed_outputs/threat-analysis.json" @@ -565,6 +580,7 @@ jobs: condition: always() - bash: | + set -eo pipefail # Copy all logs to analyzed outputs for artifact upload mkdir -p "$(Agent.TempDirectory)/analyzed_outputs/logs" if [ -d "{{ engine_log_dir }}" ]; then @@ -603,6 +619,7 @@ jobs: artifact: analyzed_outputs_$(Build.BuildId) - bash: | + set -eo pipefail COMPILER_VERSION="{{ compiler_version }}" DOWNLOAD_DIR="$(Pipeline.Workspace)/agentic-pipeline-compiler" DOWNLOAD_URL="https://github.com/githubnext/ado-aw/releases/download/v${COMPILER_VERSION}/ado-aw-linux-x64" @@ -621,18 +638,21 @@ jobs: displayName: "Download agentic pipeline compiler (v{{ compiler_version }})" - bash: | + set -eo pipefail ls -la "$(Pipeline.Workspace)/agentic-pipeline-compiler" chmod +x "$(Pipeline.Workspace)/agentic-pipeline-compiler/ado-aw" echo "##vso[task.prependpath]$(Pipeline.Workspace)/agentic-pipeline-compiler" displayName: Add agentic compiler to path - bash: | + set -eo pipefail mkdir -p "$(Agent.TempDirectory)/staging" 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" - EXIT_CODE=$? + set -eo pipefail + ado-aw execute --source "{{ source_path }}" --safe-output-dir "$(Pipeline.Workspace)/analyzed_outputs_$(Build.BuildId)" --output-dir "$(Agent.TempDirectory)/staging" \ + && EXIT_CODE=0 || EXIT_CODE=$? if [ $EXIT_CODE -eq 2 ]; then echo "##vso[task.complete result=SucceededWithIssues;]Executor completed with warnings" exit 0 @@ -643,6 +663,7 @@ jobs: {{ executor_ado_env }} - bash: | + set -eo pipefail # Copy all logs to output directory for artifact upload mkdir -p "$(Agent.TempDirectory)/staging/logs" # Copy agent output log from analyzed_outputs for optimisation use diff --git a/src/runtimes/dotnet/mod.rs b/src/runtimes/dotnet/mod.rs index d91e705d..e92b64e8 100644 --- a/src/runtimes/dotnet/mod.rs +++ b/src/runtimes/dotnet/mod.rs @@ -209,6 +209,7 @@ pub fn generate_ensure_nuget_config(config: &DotnetRuntimeConfig) -> String { format!( "\ - bash: |\n\ + set -eo pipefail\n\ if [ ! -f nuget.config ] && [ ! -f NuGet.config ] && [ ! -f NuGet.Config ]; then\n\ cat > nuget.config <<'EOF'\n\ \n\ diff --git a/src/runtimes/lean/mod.rs b/src/runtimes/lean/mod.rs index fb070195..40291c2f 100644 --- a/src/runtimes/lean/mod.rs +++ b/src/runtimes/lean/mod.rs @@ -91,6 +91,7 @@ pub fn generate_lean_install(config: &LeanRuntimeConfig) -> String { let toolchain = config.toolchain().unwrap_or("stable"); let script = format!( "\ +set -eo pipefail curl https://elan.lean-lang.org/elan-init.sh -sSf | sh -s -- -y --default-toolchain {toolchain} echo \"##vso[task.prependpath]$HOME/.elan/bin\" export PATH=\"$HOME/.elan/bin:$PATH\" @@ -109,6 +110,7 @@ lake --version || echo \"Lake installed via elan\"" /// Generate the prompt append step to inform the agent that Lean 4 is available. pub fn generate_lean_prompt() -> String { r#"- bash: | + set -eo pipefail cat >> "/tmp/awf-tools/agent-prompt.md" << 'LEAN_PROMPT_EOF' --- diff --git a/src/runtimes/node/mod.rs b/src/runtimes/node/mod.rs index 8933b81d..56114f30 100644 --- a/src/runtimes/node/mod.rs +++ b/src/runtimes/node/mod.rs @@ -157,6 +157,7 @@ pub fn generate_ensure_npmrc(config: &NodeRuntimeConfig) -> String { format!( "\ - bash: |\n\ + set -eo pipefail\n\ if [ ! -f .npmrc ]; then\n\ echo 'registry={registry}' > .npmrc\n\ echo 'Created .npmrc with registry={registry}'\n\