diff --git a/.github/aw/actions-lock.json b/.github/aw/actions-lock.json index 921c423791..139f2b18c6 100644 --- a/.github/aw/actions-lock.json +++ b/.github/aw/actions-lock.json @@ -25,6 +25,11 @@ "version": "v9.0.0", "sha": "3a2844b7e9c422d3c10d287c895573f7108da1b3" }, + "actions/setup-dotnet@v5": { + "repo": "actions/setup-dotnet", + "version": "v5", + "sha": "9a946fdbd5fb07b82b2f5a4466058b876ab72bb2" + }, "actions/setup-python@v6.2.0": { "repo": "actions/setup-python", "version": "v6.2.0", @@ -40,10 +45,10 @@ "version": "v4.36.0", "sha": "7211b7c8077ea37d8641b6271f6a365a22a5fbfa" }, - "github/gh-aw-actions/setup@v0.76.1": { + "github/gh-aw-actions/setup@v0.75.4": { "repo": "github/gh-aw-actions/setup", - "version": "v0.76.1", - "sha": "46d564922b082d0db93244972e8005ea6904ee5f" + "version": "v0.75.4", + "sha": "9f050961da586148d135e113d8bb025185cdf2b8" }, "super-linter/super-linter@v8.6.0": { "repo": "super-linter/super-linter", diff --git a/.github/workflows/build-failure-analysis-command.lock.yml b/.github/workflows/build-failure-analysis-command.lock.yml index 8249a9d099..dad484f449 100644 --- a/.github/workflows/build-failure-analysis-command.lock.yml +++ b/.github/workflows/build-failure-analysis-command.lock.yml @@ -1,5 +1,5 @@ -# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"cdf7e6dd50799a6020e37115183a80c698040971ea601872f5f10f6fc7484afc","compiler_version":"v0.75.4","strict":true,"agent_id":"copilot"} -# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-dotnet","sha":"c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7","version":"v5.2.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"9f050961da586148d135e113d8bb025185cdf2b8","version":"v0.75.4"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.53"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.53"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.53"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.18"},{"image":"ghcr.io/github/github-mcp-server:v1.0.4","digest":"sha256:e3816a476a977cfb836e7d221510011436c654d11861db66ecfd826601aba6a4","pinned_image":"ghcr.io/github/github-mcp-server:v1.0.4@sha256:e3816a476a977cfb836e7d221510011436c654d11861db66ecfd826601aba6a4"},{"image":"node:lts-alpine","digest":"sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f","pinned_image":"node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"}]} +# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"8c43d00c2a30f216209a4a2279555645a8844600bafa122cef9d19bf7356798e","compiler_version":"v0.75.4","strict":true,"agent_id":"copilot"} +# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-dotnet","sha":"9a946fdbd5fb07b82b2f5a4466058b876ab72bb2","version":"v5"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"9f050961da586148d135e113d8bb025185cdf2b8","version":"v0.75.4"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.53"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.53"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.53"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.18"},{"image":"ghcr.io/github/github-mcp-server:v1.0.4","digest":"sha256:e3816a476a977cfb836e7d221510011436c654d11861db66ecfd826601aba6a4","pinned_image":"ghcr.io/github/github-mcp-server:v1.0.4@sha256:e3816a476a977cfb836e7d221510011436c654d11861db66ecfd826601aba6a4"},{"image":"node:lts-alpine","digest":"sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f","pinned_image":"node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"}]} # ___ _ _ # / _ \ | | (_) # | |_| | __ _ ___ _ __ | |_ _ ___ @@ -40,12 +40,15 @@ # # Custom actions used: # - actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 +# - actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 (source v6) # - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 +# - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 (source v8) # - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 # - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 (source v9) -# - actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5.2.0 +# - actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5 # - actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 # - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 +# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 (source v7) # - github/gh-aw-actions/setup@9f050961da586148d135e113d8bb025185cdf2b8 # v0.75.4 # # Container images used: @@ -84,8 +87,10 @@ env: jobs: activation: - needs: pre_activation - if: needs.pre_activation.outputs.activated == 'true' + needs: + - build + - pre_activation + if: needs.pre_activation.outputs.activated == 'true' && (needs.build.outputs.outcome == 'failure') runs-on: ubuntu-slim permissions: actions: read @@ -250,20 +255,20 @@ jobs: run: | bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh" { - cat << 'GH_AW_PROMPT_d129ae11e5fa59d9_EOF' + cat << 'GH_AW_PROMPT_dd24a643e7187558_EOF' - GH_AW_PROMPT_d129ae11e5fa59d9_EOF + GH_AW_PROMPT_dd24a643e7187558_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md" cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md" cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md" cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md" - cat << 'GH_AW_PROMPT_d129ae11e5fa59d9_EOF' + cat << 'GH_AW_PROMPT_dd24a643e7187558_EOF' - Tools: add_comment, create_pull_request_review_comment(max:10), missing_tool, missing_data, noop + Tools: add_comment(max:5), create_pull_request_review_comment(max:25), missing_tool, missing_data, noop(max:5) - GH_AW_PROMPT_d129ae11e5fa59d9_EOF + GH_AW_PROMPT_dd24a643e7187558_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md" - cat << 'GH_AW_PROMPT_d129ae11e5fa59d9_EOF' + cat << 'GH_AW_PROMPT_dd24a643e7187558_EOF' The following GitHub context information is available for this workflow: {{#if github.actor}} @@ -292,16 +297,16 @@ jobs: {{/if}} - GH_AW_PROMPT_d129ae11e5fa59d9_EOF + GH_AW_PROMPT_dd24a643e7187558_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md" if [ "$GITHUB_EVENT_NAME" = "issue_comment" ] && [ -n "$GH_AW_IS_PR_COMMENT" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review_comment" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review" ]; then cat "${RUNNER_TEMP}/gh-aw/prompts/pr_context_prompt.md" fi - cat << 'GH_AW_PROMPT_d129ae11e5fa59d9_EOF' + cat << 'GH_AW_PROMPT_dd24a643e7187558_EOF' {{#runtime-import .github/workflows/shared/build-failure-analysis-shared.md}} {{#runtime-import .github/workflows/build-failure-analysis-command.md}} - GH_AW_PROMPT_d129ae11e5fa59d9_EOF + GH_AW_PROMPT_dd24a643e7187558_EOF } > "$GH_AW_PROMPT" - name: Interpolate variables and render templates uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 @@ -383,7 +388,10 @@ jobs: retention-days: 1 agent: - needs: activation + needs: + - activation + - build + if: needs.build.outputs.outcome == 'failure' runs-on: ubuntu-latest permissions: contents: read @@ -437,41 +445,24 @@ jobs: uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - - name: Setup .NET - uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5.2.0 - with: - dotnet-version: '8.0' - name: Create gh-aw temp directory run: bash "${RUNNER_TEMP}/gh-aw/actions/create_gh_aw_tmp_dir.sh" - name: Configure gh CLI for GitHub Enterprise run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_gh_for_ghe.sh" env: GH_TOKEN: ${{ github.token }} + - name: Download analysis artifact + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 (source v8) + with: + name: build-failure-analysis-data + path: /tmp/ + - name: Setup .NET (for NuGet MCP Server) + uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5 + with: + dotnet-version: 9.0.x - continue-on-error: true - id: build - name: Build with binary log - run: | - set -o pipefail - ./build.sh --binaryLog 2>&1 | tee /tmp/build-output.log - - if: always() - name: Put dotnet on the path - run: echo "$PWD/.dotnet" >> $GITHUB_PATH - - id: find-binlog - name: Locate binlog - run: "BINLOG=$(find artifacts/log -name '*.binlog' -type f -printf '%T@ %p\\n' 2>/dev/null \\\n | sort -rn | head -1 | cut -d' ' -f2-)\nif [ -n \"$BINLOG\" ] && [ -f \"$BINLOG\" ]; then\n BINLOG=$(realpath \"$BINLOG\")\n echo \"found=true\" >> \"$GITHUB_OUTPUT\"\n echo \"path=$BINLOG\" >> \"$GITHUB_OUTPUT\"\nelse\n echo \"found=false\" >> \"$GITHUB_OUTPUT\"\nfi\n" - - if: steps.build.outcome == 'failure' && steps.find-binlog.outputs.found == 'true' - name: Install binlog-mcp - run: "mkdir -p /tmp/binlog-tool\ncat > /tmp/binlog-tool/nuget.config <<'EOF'\n\n\n \n \n \n \n\nEOF\ndotnet tool install --global Microsoft.AITools.BinlogMcp \\\n --configfile /tmp/binlog-tool/nuget.config \\\n --version \"$BINLOG_MCP_VERSION\"\necho \"$HOME/.dotnet/tools\" >> \"$GITHUB_PATH\"\n" - - continue-on-error: true - if: steps.build.outcome == 'failure' && steps.find-binlog.outputs.found == 'true' name: Install NuGet MCP Server run: dotnet tool install --global NuGet.Mcp.Server --version "$NUGET_MCP_VERSION" - - continue-on-error: true - env: - BINLOG_PATH: ${{ steps.find-binlog.outputs.path }} - if: steps.build.outcome == 'failure' && steps.find-binlog.outputs.found == 'true' - name: Dump binlog as JSON - run: "mkdir -p /tmp/binlog-data\ntimeout 180 dotnet run --project .github/workflows/scripts/DumpBinlog -- \\\n \"$BINLOG_PATH\" \\\n /tmp/binlog-data\n" - env: GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} @@ -482,13 +473,24 @@ jobs: SHA=$(gh api "repos/${GH_AW_GITHUB_REPOSITORY}/pulls/${GH_AW_GITHUB_EVENT_ISSUE_NUMBER}" --jq .head.sha) echo "sha=$SHA" >> "$GITHUB_OUTPUT" - env: - GH_AW_BINLOG_PATH_VALUE: ${{ steps.find-binlog.outputs.path }} + GH_AW_BINLOG_REL_VALUE: ${{ needs.build.outputs.binlog-relative-path }} + GH_AW_BUILD_OUTCOME_VALUE: ${{ needs.build.outputs.outcome }} GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} GH_AW_PR_HEAD_SHA_VALUE: ${{ steps.resolve-pr-sha.outputs.sha || github.sha }} - GH_AW_STEPS_BUILD_OUTCOME: ${{ steps.build.outcome }} name: Export agent context - run: "{\n echo \"GH_AW_BUILD_OUTCOME=${GH_AW_STEPS_BUILD_OUTCOME}\"\n echo \"GH_AW_BINLOG_PATH=${GH_AW_BINLOG_PATH_VALUE}\"\n echo \"GH_AW_PR_NUMBER=${GH_AW_GITHUB_EVENT_ISSUE_NUMBER}\"\n echo \"GH_AW_PR_HEAD_SHA=${GH_AW_PR_HEAD_SHA_VALUE}\"\n echo \"GH_AW_WORKSPACE=${GH_AW_GITHUB_WORKSPACE}\"\n} >> \"$GITHUB_ENV\"\n" + run: |- + BINLOG_PATH="" + if [ -n "${GH_AW_BINLOG_REL_VALUE:-}" ]; then + BINLOG_PATH="${GH_AW_GITHUB_WORKSPACE}/${GH_AW_BINLOG_REL_VALUE}" + fi + { + echo "GH_AW_BUILD_OUTCOME=${GH_AW_BUILD_OUTCOME_VALUE}" + echo "GH_AW_BINLOG_PATH=${BINLOG_PATH}" + echo "GH_AW_PR_NUMBER=${GH_AW_GITHUB_EVENT_ISSUE_NUMBER}" + echo "GH_AW_PR_HEAD_SHA=${GH_AW_PR_HEAD_SHA_VALUE}" + echo "GH_AW_WORKSPACE=${GH_AW_GITHUB_WORKSPACE}" + } >> "$GITHUB_ENV" - name: Configure Git credentials env: @@ -556,16 +558,16 @@ jobs: mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs" mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs - cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_5068ae042e1283c8_EOF' - {"add_comment":{"hide_older_comments":true,"max":1},"create_pull_request_review_comment":{"max":10,"side":"RIGHT"},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"false"},"report_incomplete":{}} - GH_AW_SAFE_OUTPUTS_CONFIG_5068ae042e1283c8_EOF + cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_5d45d569ae4d0c5b_EOF' + {"add_comment":{"hide_older_comments":true,"max":5},"create_pull_request_review_comment":{"max":25,"side":"RIGHT"},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":5,"report-as-issue":"false"},"report_incomplete":{}} + GH_AW_SAFE_OUTPUTS_CONFIG_5d45d569ae4d0c5b_EOF - name: Generate Safe Outputs Tools env: GH_AW_TOOLS_META_JSON: | { "description_suffixes": { - "add_comment": " CONSTRAINTS: Maximum 1 comment(s) can be added. Supports reply_to_id for discussion threading.", - "create_pull_request_review_comment": " CONSTRAINTS: Maximum 10 review comment(s) can be created. Comments will be on the RIGHT side of the diff." + "add_comment": " CONSTRAINTS: Maximum 5 comment(s) can be added. Supports reply_to_id for discussion threading.", + "create_pull_request_review_comment": " CONSTRAINTS: Maximum 25 review comment(s) can be created. Comments will be on the RIGHT side of the diff." }, "repo_params": {}, "dynamic_tools": [] @@ -790,7 +792,7 @@ jobs: mkdir -p /home/runner/.copilot GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) - cat << GH_AW_MCP_CONFIG_46d2e953909d7ce5_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" + cat << GH_AW_MCP_CONFIG_626cbfb26bdaa019_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" { "mcpServers": { "github": { @@ -831,7 +833,7 @@ jobs: "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}" } } - GH_AW_MCP_CONFIG_46d2e953909d7ce5_EOF + GH_AW_MCP_CONFIG_626cbfb26bdaa019_EOF - name: Mount MCP servers as CLIs id: mount-mcp-clis continue-on-error: true @@ -1079,10 +1081,105 @@ jobs: /tmp/gh-aw/sandbox/firewall/awf-reflect.json if-no-files-found: ignore + build: + name: Build (for analysis) + runs-on: ubuntu-latest + permissions: + contents: read + + timeout-minutes: 30 + env: + BINLOG_MCP_VERSION: 1.0.0-preview.26272.1 + outputs: + binlog-found: ${{ steps.find-binlog.outputs.found }} + binlog-relative-path: ${{ steps.find-binlog.outputs.relative-path }} + outcome: ${{ steps.build.outcome }} + steps: + - name: Configure GH_HOST for enterprise compatibility + id: ghes-host-config + shell: bash + run: | + # Derive GH_HOST from GITHUB_SERVER_URL so the gh CLI targets the correct + # GitHub instance (GHES/GHEC). On github.com this is a harmless no-op. + GH_HOST="${GITHUB_SERVER_URL#https://}" + GH_HOST="${GH_HOST#http://}" + echo "GH_HOST=${GH_HOST}" >> "$GITHUB_ENV" + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 (source v6) + with: + ref: refs/pull/${{ github.event.issue.number }}/merge + - name: Build with binary log + id: build + run: | + set -uo pipefail + ./build.sh --binaryLog 2>&1 | tee /tmp/build-output.log + # `tee` is best-effort: rely on the build's own exit code so a + # logging-pipeline glitch never misclassifies a green build as + # failed (which would otherwise trigger the AI agent and + # re-expose us to the Copilot-flake red-X bug). + exit "${PIPESTATUS[0]}" + continue-on-error: true + - name: Put dotnet on the path + if: always() + run: echo "$PWD/.dotnet" >> $GITHUB_PATH + - name: Locate binlog + id: find-binlog + if: always() + run: | + BINLOG=$(find artifacts/log -name '*.binlog' -type f -printf '%T@ %p\n' 2>/dev/null \ + | sort -rn | head -1 | cut -d' ' -f2-) + if [ -n "$BINLOG" ] && [ -f "$BINLOG" ]; then + REL=$(realpath --relative-to="$PWD" "$BINLOG") + echo "found=true" >> "$GITHUB_OUTPUT" + echo "relative-path=$REL" >> "$GITHUB_OUTPUT" + else + echo "found=false" >> "$GITHUB_OUTPUT" + fi + - name: Install binlog-mcp + if: steps.build.outcome == 'failure' && steps.find-binlog.outputs.found == 'true' + run: | + mkdir -p /tmp/binlog-tool + cat > /tmp/binlog-tool/nuget.config <<'EOF' + + + + + + + + EOF + dotnet tool install --global Microsoft.AITools.BinlogMcp \ + --configfile /tmp/binlog-tool/nuget.config \ + --version "$BINLOG_MCP_VERSION" + echo "$HOME/.dotnet/tools" >> "$GITHUB_PATH" + continue-on-error: true + - name: Dump binlog as JSON + if: steps.build.outcome == 'failure' && steps.find-binlog.outputs.found == 'true' + run: | + mkdir -p /tmp/binlog-data + timeout 180 dotnet run --project .github/workflows/scripts/DumpBinlog -- \ + "$BINLOG_REL_PATH" \ + /tmp/binlog-data + env: + BINLOG_REL_PATH: ${{ steps.find-binlog.outputs.relative-path }} + continue-on-error: true + - name: Upload analysis artifact + if: always() && steps.build.outcome == 'failure' + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 (source v7) + with: + if-no-files-found: warn + name: build-failure-analysis-data + path: | + /tmp/binlog-data/ + /tmp/build-output.log + retention-days: 1 + continue-on-error: true + conclusion: needs: - activation - agent + - build - detection - safe_outputs if: > @@ -1137,7 +1234,7 @@ jobs: uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} - GH_AW_NOOP_MAX: "1" + GH_AW_NOOP_MAX: "5" GH_AW_WORKFLOW_NAME: "Build Failure Analysis (command)" GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/build-failure-analysis-command.md" GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} @@ -1458,6 +1555,7 @@ jobs: } pre_activation: + needs: build runs-on: ubuntu-slim outputs: activated: ${{ steps.check_membership.outputs.is_team_member == 'true' && steps.check_command_position.outputs.command_position_ok == 'true' }} @@ -1582,7 +1680,7 @@ jobs: GH_AW_ALLOWED_DOMAINS: "*.vsblob.vsassets.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.nuget.org,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,azuresearch-usnc.nuget.org,azuresearch-ussc.nuget.org,builds.dotnet.microsoft.com,ci.dot.net,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,dc.services.visualstudio.com,dist.nuget.org,dot.net,dotnet.microsoft.com,dotnetcli.blob.core.windows.net,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,nuget.org,nuget.pkg.github.com,nugetregistryv2prod.blob.core.windows.net,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,oneocsp.microsoft.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkgs.dev.azure.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com,www.microsoft.com" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} - GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"hide_older_comments\":true,\"max\":1},\"create_pull_request_review_comment\":{\"max\":10,\"side\":\"RIGHT\"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"report_incomplete\":{}}" + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"hide_older_comments\":true,\"max\":5},\"create_pull_request_review_comment\":{\"max\":25,\"side\":\"RIGHT\"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":5,\"report-as-issue\":\"false\"},\"report_incomplete\":{}}" with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | diff --git a/.github/workflows/build-failure-analysis-command.md b/.github/workflows/build-failure-analysis-command.md index 2d7b6dee03..7e1c512902 100644 --- a/.github/workflows/build-failure-analysis-command.md +++ b/.github/workflows/build-failure-analysis-command.md @@ -15,6 +15,16 @@ on: strategy: centralized roles: [admin, maintainer, write] reaction: "eyes" + # Make `pre_activation` and `activation` wait for the custom `build` job + # defined below so the agent only runs when there is actually something + # to analyse, mirroring the auto-trigger workflow. + needs: [build] + +# Skip activation (and therefore the agent job) when the build job reported +# success — even when invoked explicitly via `/analyze-build-failure`, there +# is nothing to analyse on a green build and running the agent would just +# expose us to transient Copilot AI flakes (see issue #8685). +if: needs.build.outputs.outcome == 'failure' permissions: contents: read @@ -38,69 +48,125 @@ network: imports: - shared/build-failure-analysis-shared.md -# Same deterministic setup as build-failure-analysis.md. The slash-command -# trigger fires on a `pull_request_comment` event; gh-aw handles the PR -# checkout when the comment originates on a PR. +# Custom build job that runs on every slash-command invocation. Mirrors the +# `build` job in build-failure-analysis.md so the slash-command variant +# benefits from the same skip-on-success gating. +jobs: + build: + name: Build (for analysis) + runs-on: ubuntu-latest + timeout-minutes: 30 + permissions: + contents: read + outputs: + outcome: ${{ steps.build.outcome }} + binlog-found: ${{ steps.find-binlog.outputs.found }} + binlog-relative-path: ${{ steps.find-binlog.outputs.relative-path }} + env: + BINLOG_MCP_VERSION: '1.0.0-preview.26272.1' + steps: + # `pull_request_comment` events have the `issues` event payload, so + # the default checkout would build the default branch — NOT the PR + # the maintainer ran `/analyze-build-failure` on. Check out the PR's + # merge ref explicitly so we analyse the same code that the auto + # `pull_request` workflow would build. + - uses: actions/checkout@v6 + with: + ref: refs/pull/${{ github.event.issue.number }}/merge + + - name: Build with binary log + id: build + continue-on-error: true + run: | + set -uo pipefail + ./build.sh --binaryLog 2>&1 | tee /tmp/build-output.log + # `tee` is best-effort: rely on the build's own exit code so a + # logging-pipeline glitch never misclassifies a green build as + # failed (which would otherwise trigger the AI agent and + # re-expose us to the Copilot-flake red-X bug). + exit "${PIPESTATUS[0]}" + + - name: Put dotnet on the path + if: always() + run: echo "$PWD/.dotnet" >> $GITHUB_PATH + + - name: Locate binlog + id: find-binlog + if: always() + run: | + BINLOG=$(find artifacts/log -name '*.binlog' -type f -printf '%T@ %p\n' 2>/dev/null \ + | sort -rn | head -1 | cut -d' ' -f2-) + if [ -n "$BINLOG" ] && [ -f "$BINLOG" ]; then + REL=$(realpath --relative-to="$PWD" "$BINLOG") + echo "found=true" >> "$GITHUB_OUTPUT" + echo "relative-path=$REL" >> "$GITHUB_OUTPUT" + else + echo "found=false" >> "$GITHUB_OUTPUT" + fi + + - name: Install binlog-mcp + if: steps.build.outcome == 'failure' && steps.find-binlog.outputs.found == 'true' + continue-on-error: true + run: | + mkdir -p /tmp/binlog-tool + cat > /tmp/binlog-tool/nuget.config <<'EOF' + + + + + + + + EOF + dotnet tool install --global Microsoft.AITools.BinlogMcp \ + --configfile /tmp/binlog-tool/nuget.config \ + --version "$BINLOG_MCP_VERSION" + echo "$HOME/.dotnet/tools" >> "$GITHUB_PATH" + + - name: Dump binlog as JSON + if: steps.build.outcome == 'failure' && steps.find-binlog.outputs.found == 'true' + continue-on-error: true + env: + BINLOG_REL_PATH: ${{ steps.find-binlog.outputs.relative-path }} + run: | + mkdir -p /tmp/binlog-data + timeout 180 dotnet run --project .github/workflows/scripts/DumpBinlog -- \ + "$BINLOG_REL_PATH" \ + /tmp/binlog-data + + - name: Upload analysis artifact + if: always() && steps.build.outcome == 'failure' + continue-on-error: true + uses: actions/upload-artifact@v7 + with: + name: build-failure-analysis-data + path: | + /tmp/binlog-data/ + /tmp/build-output.log + if-no-files-found: warn + retention-days: 1 + +# Steps that run in the agent job. The top-level `if:` gates these on a +# failed build, so the slash-command never invokes the AI agent on a green +# build (and thus cannot surface as a red workflow check from a transient +# Copilot AI flake). steps: - - name: Build with binary log - id: build - continue-on-error: true - run: | - set -o pipefail - ./build.sh --binaryLog 2>&1 | tee /tmp/build-output.log - - - name: Put dotnet on the path - if: always() - run: echo "$PWD/.dotnet" >> $GITHUB_PATH - - - name: Locate binlog - id: find-binlog - run: | - BINLOG=$(find artifacts/log -name '*.binlog' -type f -printf '%T@ %p\n' 2>/dev/null \ - | sort -rn | head -1 | cut -d' ' -f2-) - if [ -n "$BINLOG" ] && [ -f "$BINLOG" ]; then - BINLOG=$(realpath "$BINLOG") - echo "found=true" >> "$GITHUB_OUTPUT" - echo "path=$BINLOG" >> "$GITHUB_OUTPUT" - else - echo "found=false" >> "$GITHUB_OUTPUT" - fi + - name: Download analysis artifact + uses: actions/download-artifact@v8 + with: + name: build-failure-analysis-data + path: /tmp/ - - name: Install binlog-mcp - if: steps.build.outcome == 'failure' && steps.find-binlog.outputs.found == 'true' - run: | - mkdir -p /tmp/binlog-tool - cat > /tmp/binlog-tool/nuget.config <<'EOF' - - - - - - - - EOF - dotnet tool install --global Microsoft.AITools.BinlogMcp \ - --configfile /tmp/binlog-tool/nuget.config \ - --version "$BINLOG_MCP_VERSION" - echo "$HOME/.dotnet/tools" >> "$GITHUB_PATH" + - name: Setup .NET (for NuGet MCP Server) + uses: actions/setup-dotnet@v5 + with: + dotnet-version: '9.0.x' - name: Install NuGet MCP Server - if: steps.build.outcome == 'failure' && steps.find-binlog.outputs.found == 'true' continue-on-error: true run: dotnet tool install --global NuGet.Mcp.Server --version "$NUGET_MCP_VERSION" - - name: Dump binlog as JSON - if: steps.build.outcome == 'failure' && steps.find-binlog.outputs.found == 'true' - continue-on-error: true - env: - BINLOG_PATH: ${{ steps.find-binlog.outputs.path }} - run: | - mkdir -p /tmp/binlog-data - timeout 180 dotnet run --project .github/workflows/scripts/DumpBinlog -- \ - "$BINLOG_PATH" \ - /tmp/binlog-data - # `pull_request_comment` events use the `issues` event payload, so # `github.sha` is the default branch tip — NOT the PR head. Always resolve # the real PR head SHA via the API so permalinks and inline comment @@ -117,15 +183,19 @@ steps: - name: Export agent context env: - GH_AW_STEPS_BUILD_OUTCOME: ${{ steps.build.outcome }} - GH_AW_BINLOG_PATH_VALUE: ${{ steps.find-binlog.outputs.path }} + GH_AW_BUILD_OUTCOME_VALUE: ${{ needs.build.outputs.outcome }} + GH_AW_BINLOG_REL_VALUE: ${{ needs.build.outputs.binlog-relative-path }} GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} GH_AW_PR_HEAD_SHA_VALUE: ${{ steps.resolve-pr-sha.outputs.sha || github.sha }} GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} run: | + BINLOG_PATH="" + if [ -n "${GH_AW_BINLOG_REL_VALUE:-}" ]; then + BINLOG_PATH="${GH_AW_GITHUB_WORKSPACE}/${GH_AW_BINLOG_REL_VALUE}" + fi { - echo "GH_AW_BUILD_OUTCOME=${GH_AW_STEPS_BUILD_OUTCOME}" - echo "GH_AW_BINLOG_PATH=${GH_AW_BINLOG_PATH_VALUE}" + echo "GH_AW_BUILD_OUTCOME=${GH_AW_BUILD_OUTCOME_VALUE}" + echo "GH_AW_BINLOG_PATH=${BINLOG_PATH}" echo "GH_AW_PR_NUMBER=${GH_AW_GITHUB_EVENT_ISSUE_NUMBER}" echo "GH_AW_PR_HEAD_SHA=${GH_AW_PR_HEAD_SHA_VALUE}" echo "GH_AW_WORKSPACE=${GH_AW_GITHUB_WORKSPACE}" @@ -148,15 +218,29 @@ tools: - "NuGet.Mcp.Server" safe-outputs: - # Match build-failure-analysis.md: suppress tracking-issue spam from - # transient AI service flakes (see issue #8685 and comments there). + # The agent runs only when the build job reports failure (see top-level + # `if:` above). On a failed build the agent normally emits at most one + # `noop`, one summary comment, and a small set of inline review comments, + # but the Copilot CLI harness retries with `--continue` on + # mid-conversation AI flakes (up to 3 retries) and each retry re-emits + # every safe-output call it has issued so far. The caps below absorb that + # retry budget without spurious safe-output validation warnings: + # - noop max=5: covers 1 happy-path + 4 retry-amplified noops. + # - add-comment max=5: covers 1 summary + 4 retries (hide-older-comments + # auto-collapses the duplicates anyway). + # - create-pull-request-review-comment max=25: shared body asks the + # agent for "top 5 highest-priority issues" per run, so 5 × (1 + 3 + # retries) = 20 is the worst case under flake amplification. + # We also disable `report-as-issue` / `report-failure-as-issue` so + # transient flakes never spam tracking issues (see issue #8685). report-failure-as-issue: false add-comment: - max: 1 + max: 5 hide-older-comments: true create-pull-request-review-comment: - max: 10 + max: 25 noop: + max: 5 report-as-issue: false --- diff --git a/.github/workflows/build-failure-analysis.lock.yml b/.github/workflows/build-failure-analysis.lock.yml index 5a4a993e8c..be3a4c3d1b 100644 --- a/.github/workflows/build-failure-analysis.lock.yml +++ b/.github/workflows/build-failure-analysis.lock.yml @@ -1,5 +1,5 @@ -# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"455b908fb91e2873c51b4690c39505ae86cc92158666b7e14a6083907c1cd383","compiler_version":"v0.75.4","strict":true,"agent_id":"copilot"} -# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-dotnet","sha":"c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7","version":"v5.2.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"9f050961da586148d135e113d8bb025185cdf2b8","version":"v0.75.4"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.53"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.53"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.53"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.18"},{"image":"ghcr.io/github/github-mcp-server:v1.0.4","digest":"sha256:e3816a476a977cfb836e7d221510011436c654d11861db66ecfd826601aba6a4","pinned_image":"ghcr.io/github/github-mcp-server:v1.0.4@sha256:e3816a476a977cfb836e7d221510011436c654d11861db66ecfd826601aba6a4"},{"image":"node:lts-alpine","digest":"sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f","pinned_image":"node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"}]} +# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"36668e910355f18bfec1b7fe7525746a9f4bbdf7661589c31b5b1afc318004d8","compiler_version":"v0.75.4","strict":true,"agent_id":"copilot"} +# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-dotnet","sha":"9a946fdbd5fb07b82b2f5a4466058b876ab72bb2","version":"v5"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"9f050961da586148d135e113d8bb025185cdf2b8","version":"v0.75.4"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.53"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.53"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.53"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.18"},{"image":"ghcr.io/github/github-mcp-server:v1.0.4","digest":"sha256:e3816a476a977cfb836e7d221510011436c654d11861db66ecfd826601aba6a4","pinned_image":"ghcr.io/github/github-mcp-server:v1.0.4@sha256:e3816a476a977cfb836e7d221510011436c654d11861db66ecfd826601aba6a4"},{"image":"node:lts-alpine","digest":"sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f","pinned_image":"node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"}]} # ___ _ _ # / _ \ | | (_) # | |_| | __ _ ___ _ __ | |_ _ ___ @@ -40,12 +40,15 @@ # # Custom actions used: # - actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 +# - actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 (source v6) # - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 +# - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 (source v8) # - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 # - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 (source v9) -# - actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5.2.0 +# - actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5 # - actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 # - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 +# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 (source v7) # - github/gh-aw-actions/setup@9f050961da586148d135e113d8bb025185cdf2b8 # v0.75.4 # # Container images used: @@ -97,9 +100,12 @@ env: jobs: activation: - needs: pre_activation + needs: + - build + - pre_activation if: > - needs.pre_activation.outputs.activated == 'true' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.id == github.repository_id) + needs.pre_activation.outputs.activated == 'true' && ((needs.build.outputs.outcome == 'failure') && (github.event_name != 'pull_request' || + github.event.pull_request.head.repo.id == github.repository_id)) runs-on: ubuntu-slim permissions: actions: read @@ -249,20 +255,20 @@ jobs: run: | bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh" { - cat << 'GH_AW_PROMPT_d01b03d1807f525c_EOF' + cat << 'GH_AW_PROMPT_ba091b964d18ff2d_EOF' - GH_AW_PROMPT_d01b03d1807f525c_EOF + GH_AW_PROMPT_ba091b964d18ff2d_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md" cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md" cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md" cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md" - cat << 'GH_AW_PROMPT_d01b03d1807f525c_EOF' + cat << 'GH_AW_PROMPT_ba091b964d18ff2d_EOF' - Tools: add_comment, create_pull_request_review_comment(max:10), missing_tool, missing_data, noop + Tools: add_comment(max:5), create_pull_request_review_comment(max:25), missing_tool, missing_data, noop(max:5) - GH_AW_PROMPT_d01b03d1807f525c_EOF + GH_AW_PROMPT_ba091b964d18ff2d_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md" - cat << 'GH_AW_PROMPT_d01b03d1807f525c_EOF' + cat << 'GH_AW_PROMPT_ba091b964d18ff2d_EOF' The following GitHub context information is available for this workflow: {{#if github.actor}} @@ -291,13 +297,13 @@ jobs: {{/if}} - GH_AW_PROMPT_d01b03d1807f525c_EOF + GH_AW_PROMPT_ba091b964d18ff2d_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md" - cat << 'GH_AW_PROMPT_d01b03d1807f525c_EOF' + cat << 'GH_AW_PROMPT_ba091b964d18ff2d_EOF' {{#runtime-import .github/workflows/shared/build-failure-analysis-shared.md}} {{#runtime-import .github/workflows/build-failure-analysis.md}} - GH_AW_PROMPT_d01b03d1807f525c_EOF + GH_AW_PROMPT_ba091b964d18ff2d_EOF } > "$GH_AW_PROMPT" - name: Interpolate variables and render templates uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 @@ -375,7 +381,11 @@ jobs: retention-days: 1 agent: - needs: activation + needs: + - activation + - build + if: > + (needs.build.outputs.outcome == 'failure') && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.id == github.repository_id) runs-on: ubuntu-latest permissions: contents: read @@ -429,41 +439,24 @@ jobs: uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - - name: Setup .NET - uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5.2.0 - with: - dotnet-version: '8.0' - name: Create gh-aw temp directory run: bash "${RUNNER_TEMP}/gh-aw/actions/create_gh_aw_tmp_dir.sh" - name: Configure gh CLI for GitHub Enterprise run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_gh_for_ghe.sh" env: GH_TOKEN: ${{ github.token }} + - name: Download analysis artifact + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 (source v8) + with: + name: build-failure-analysis-data + path: /tmp/ + - name: Setup .NET (for NuGet MCP Server) + uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5 + with: + dotnet-version: 9.0.x - continue-on-error: true - id: build - name: Build with binary log - run: | - set -o pipefail - ./build.sh --binaryLog 2>&1 | tee /tmp/build-output.log - - if: always() - name: Put dotnet on the path - run: echo "$PWD/.dotnet" >> $GITHUB_PATH - - id: find-binlog - name: Locate binlog - run: "BINLOG=$(find artifacts/log -name '*.binlog' -type f -printf '%T@ %p\\n' 2>/dev/null \\\n | sort -rn | head -1 | cut -d' ' -f2-)\nif [ -n \"$BINLOG\" ] && [ -f \"$BINLOG\" ]; then\n BINLOG=$(realpath \"$BINLOG\")\n echo \"found=true\" >> \"$GITHUB_OUTPUT\"\n echo \"path=$BINLOG\" >> \"$GITHUB_OUTPUT\"\nelse\n echo \"found=false\" >> \"$GITHUB_OUTPUT\"\nfi\n" - - if: steps.build.outcome == 'failure' && steps.find-binlog.outputs.found == 'true' - name: Install binlog-mcp - run: "mkdir -p /tmp/binlog-tool\ncat > /tmp/binlog-tool/nuget.config <<'EOF'\n\n\n \n \n \n \n\nEOF\ndotnet tool install --global Microsoft.AITools.BinlogMcp \\\n --configfile /tmp/binlog-tool/nuget.config \\\n --version \"$BINLOG_MCP_VERSION\"\necho \"$HOME/.dotnet/tools\" >> \"$GITHUB_PATH\"\n" - - continue-on-error: true - if: steps.build.outcome == 'failure' && steps.find-binlog.outputs.found == 'true' name: Install NuGet MCP Server run: dotnet tool install --global NuGet.Mcp.Server --version "$NUGET_MCP_VERSION" - - continue-on-error: true - env: - BINLOG_PATH: ${{ steps.find-binlog.outputs.path }} - if: steps.build.outcome == 'failure' && steps.find-binlog.outputs.found == 'true' - name: Dump binlog as JSON - run: "mkdir -p /tmp/binlog-data\ntimeout 180 dotnet run --project .github/workflows/scripts/DumpBinlog -- \\\n \"$BINLOG_PATH\" \\\n /tmp/binlog-data\n" - env: GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} GH_AW_INPUTS_PR_NUMBER: ${{ inputs.pr-number }} @@ -475,13 +468,13 @@ jobs: SHA=$(gh api "repos/${GH_AW_GITHUB_REPOSITORY}/pulls/${GH_AW_INPUTS_PR_NUMBER}" --jq .head.sha) echo "sha=$SHA" >> "$GITHUB_OUTPUT" - env: - GH_AW_BINLOG_PATH_VALUE: ${{ steps.find-binlog.outputs.path }} + GH_AW_BINLOG_REL_VALUE: ${{ needs.build.outputs.binlog-relative-path }} + GH_AW_BUILD_OUTCOME_VALUE: ${{ needs.build.outputs.outcome }} GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} GH_AW_PR_HEAD_SHA_VALUE: ${{ steps.resolve-pr-sha.outputs.sha || github.event.pull_request.head.sha || github.sha }} GH_AW_PR_NUMBER_VALUE: ${{ github.event.pull_request.number || github.event.issue.number || inputs.pr-number }} - GH_AW_STEPS_BUILD_OUTCOME: ${{ steps.build.outcome }} name: Export agent context - run: "{\n echo \"GH_AW_BUILD_OUTCOME=${GH_AW_STEPS_BUILD_OUTCOME}\"\n echo \"GH_AW_BINLOG_PATH=${GH_AW_BINLOG_PATH_VALUE}\"\n echo \"GH_AW_PR_NUMBER=${GH_AW_PR_NUMBER_VALUE}\"\n echo \"GH_AW_PR_HEAD_SHA=${GH_AW_PR_HEAD_SHA_VALUE}\"\n echo \"GH_AW_WORKSPACE=${GH_AW_GITHUB_WORKSPACE}\"\n} >> \"$GITHUB_ENV\"\n" + run: "# The binlog file itself is not transported between jobs (it is large\n# and the agent only needs the pre-dumped JSON files). Set\n# GH_AW_BINLOG_PATH to a synthetic workspace-relative path purely for\n# display / permalink purposes; the agent must rely on\n# /tmp/binlog-data/*.json for actual data (see shared body).\nBINLOG_PATH=\"\"\nif [ -n \"${GH_AW_BINLOG_REL_VALUE:-}\" ]; then\n BINLOG_PATH=\"${GH_AW_GITHUB_WORKSPACE}/${GH_AW_BINLOG_REL_VALUE}\"\nfi\n{\n echo \"GH_AW_BUILD_OUTCOME=${GH_AW_BUILD_OUTCOME_VALUE}\"\n echo \"GH_AW_BINLOG_PATH=${BINLOG_PATH}\"\n echo \"GH_AW_PR_NUMBER=${GH_AW_PR_NUMBER_VALUE}\"\n echo \"GH_AW_PR_HEAD_SHA=${GH_AW_PR_HEAD_SHA_VALUE}\"\n echo \"GH_AW_WORKSPACE=${GH_AW_GITHUB_WORKSPACE}\"\n} >> \"$GITHUB_ENV\"\n" - name: Configure Git credentials env: @@ -549,16 +542,16 @@ jobs: mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs" mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs - cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_d63b713a200ed2d5_EOF' - {"add_comment":{"hide_older_comments":true,"max":1},"create_pull_request_review_comment":{"max":10,"side":"RIGHT"},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"false"},"report_incomplete":{}} - GH_AW_SAFE_OUTPUTS_CONFIG_d63b713a200ed2d5_EOF + cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_3599610bf1d695d8_EOF' + {"add_comment":{"hide_older_comments":true,"max":5},"create_pull_request_review_comment":{"max":25,"side":"RIGHT"},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":5,"report-as-issue":"false"},"report_incomplete":{}} + GH_AW_SAFE_OUTPUTS_CONFIG_3599610bf1d695d8_EOF - name: Generate Safe Outputs Tools env: GH_AW_TOOLS_META_JSON: | { "description_suffixes": { - "add_comment": " CONSTRAINTS: Maximum 1 comment(s) can be added. Supports reply_to_id for discussion threading.", - "create_pull_request_review_comment": " CONSTRAINTS: Maximum 10 review comment(s) can be created. Comments will be on the RIGHT side of the diff." + "add_comment": " CONSTRAINTS: Maximum 5 comment(s) can be added. Supports reply_to_id for discussion threading.", + "create_pull_request_review_comment": " CONSTRAINTS: Maximum 25 review comment(s) can be created. Comments will be on the RIGHT side of the diff." }, "repo_params": {}, "dynamic_tools": [] @@ -783,7 +776,7 @@ jobs: mkdir -p /home/runner/.copilot GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) - cat << GH_AW_MCP_CONFIG_18f7fc77e234b36e_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" + cat << GH_AW_MCP_CONFIG_d9ac6c610fd39d46_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" { "mcpServers": { "github": { @@ -824,7 +817,7 @@ jobs: "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}" } } - GH_AW_MCP_CONFIG_18f7fc77e234b36e_EOF + GH_AW_MCP_CONFIG_d9ac6c610fd39d46_EOF - name: Mount MCP servers as CLIs id: mount-mcp-clis continue-on-error: true @@ -1071,10 +1064,104 @@ jobs: /tmp/gh-aw/sandbox/firewall/awf-reflect.json if-no-files-found: ignore + build: + name: Build (for analysis) + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository + runs-on: ubuntu-latest + permissions: + contents: read + + timeout-minutes: 30 + env: + BINLOG_MCP_VERSION: 1.0.0-preview.26272.1 + outputs: + binlog-found: ${{ steps.find-binlog.outputs.found }} + binlog-relative-path: ${{ steps.find-binlog.outputs.relative-path }} + outcome: ${{ steps.build.outcome }} + steps: + - name: Configure GH_HOST for enterprise compatibility + id: ghes-host-config + shell: bash + run: | + # Derive GH_HOST from GITHUB_SERVER_URL so the gh CLI targets the correct + # GitHub instance (GHES/GHEC). On github.com this is a harmless no-op. + GH_HOST="${GITHUB_SERVER_URL#https://}" + GH_HOST="${GH_HOST#http://}" + echo "GH_HOST=${GH_HOST}" >> "$GITHUB_ENV" + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 (source v6) + - name: Build with binary log + id: build + run: | + set -uo pipefail + ./build.sh --binaryLog 2>&1 | tee /tmp/build-output.log + # `tee` is best-effort: rely on the build's own exit code so a + # logging-pipeline glitch never misclassifies a green build as + # failed (which would otherwise trigger the AI agent and + # re-expose us to the Copilot-flake red-X bug). + exit "${PIPESTATUS[0]}" + continue-on-error: true + - name: Put dotnet on the path + if: always() + run: echo "$PWD/.dotnet" >> $GITHUB_PATH + - name: Locate binlog + id: find-binlog + if: always() + run: | + BINLOG=$(find artifacts/log -name '*.binlog' -type f -printf '%T@ %p\n' 2>/dev/null \ + | sort -rn | head -1 | cut -d' ' -f2-) + if [ -n "$BINLOG" ] && [ -f "$BINLOG" ]; then + REL=$(realpath --relative-to="$PWD" "$BINLOG") + echo "found=true" >> "$GITHUB_OUTPUT" + echo "relative-path=$REL" >> "$GITHUB_OUTPUT" + else + echo "found=false" >> "$GITHUB_OUTPUT" + fi + - name: Install binlog-mcp + if: steps.build.outcome == 'failure' && steps.find-binlog.outputs.found == 'true' + run: | + mkdir -p /tmp/binlog-tool + cat > /tmp/binlog-tool/nuget.config <<'EOF' + + + + + + + + EOF + dotnet tool install --global Microsoft.AITools.BinlogMcp \ + --configfile /tmp/binlog-tool/nuget.config \ + --version "$BINLOG_MCP_VERSION" + echo "$HOME/.dotnet/tools" >> "$GITHUB_PATH" + continue-on-error: true + - name: Dump binlog as JSON + if: steps.build.outcome == 'failure' && steps.find-binlog.outputs.found == 'true' + run: | + mkdir -p /tmp/binlog-data + timeout 180 dotnet run --project .github/workflows/scripts/DumpBinlog -- \ + "$BINLOG_REL_PATH" \ + /tmp/binlog-data + env: + BINLOG_REL_PATH: ${{ steps.find-binlog.outputs.relative-path }} + continue-on-error: true + - name: Upload analysis artifact + if: always() && steps.build.outcome == 'failure' + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 (source v7) + with: + if-no-files-found: warn + name: build-failure-analysis-data + path: | + /tmp/binlog-data/ + /tmp/build-output.log + retention-days: 1 + continue-on-error: true + conclusion: needs: - activation - agent + - build - detection - safe_outputs if: > @@ -1129,7 +1216,7 @@ jobs: uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} - GH_AW_NOOP_MAX: "1" + GH_AW_NOOP_MAX: "5" GH_AW_WORKFLOW_NAME: "Build Failure Analysis" GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/build-failure-analysis.md" GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} @@ -1430,7 +1517,7 @@ jobs: } pre_activation: - if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.id == github.repository_id + needs: build runs-on: ubuntu-slim outputs: activated: ${{ steps.check_membership.outputs.is_team_member == 'true' }} @@ -1544,7 +1631,7 @@ jobs: GH_AW_ALLOWED_DOMAINS: "*.vsblob.vsassets.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.nuget.org,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,azuresearch-usnc.nuget.org,azuresearch-ussc.nuget.org,builds.dotnet.microsoft.com,ci.dot.net,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,dc.services.visualstudio.com,dist.nuget.org,dot.net,dotnet.microsoft.com,dotnetcli.blob.core.windows.net,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,nuget.org,nuget.pkg.github.com,nugetregistryv2prod.blob.core.windows.net,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,oneocsp.microsoft.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkgs.dev.azure.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com,www.microsoft.com" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} - GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"hide_older_comments\":true,\"max\":1},\"create_pull_request_review_comment\":{\"max\":10,\"side\":\"RIGHT\"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"report_incomplete\":{}}" + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"hide_older_comments\":true,\"max\":5},\"create_pull_request_review_comment\":{\"max\":25,\"side\":\"RIGHT\"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":5,\"report-as-issue\":\"false\"},\"report_incomplete\":{}}" with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | diff --git a/.github/workflows/build-failure-analysis.md b/.github/workflows/build-failure-analysis.md index b18ba6c7cd..669aebf46a 100644 --- a/.github/workflows/build-failure-analysis.md +++ b/.github/workflows/build-failure-analysis.md @@ -34,6 +34,18 @@ on: # the companion `build-failure-analysis-command.md` workflow. roles: [admin, maintainer, write] reaction: "eyes" + # Make `pre_activation` and `activation` wait for the custom `build` job + # defined below. Combined with the top-level `if:`, this gates the entire + # AI agent pipeline on build failure — so transient Copilot AI flakes can + # never surface as a red workflow check on a successful build. + needs: [build] + +# Skip activation (and therefore the agent job) when the build job reported +# success. gh-aw applies top-level `if:` to the `activation` job, which is a +# dependency of `agent`, so a skipped activation cascades into a skipped +# agent — no AI calls, no safe-output validation, no chance of a noop-loop +# from a transient AI server error on an otherwise green build. +if: needs.build.outputs.outcome == 'failure' permissions: contents: read @@ -57,74 +69,129 @@ network: imports: - shared/build-failure-analysis-shared.md -# Deterministic setup that runs before the AI agent starts. By the time the -# agent boots: dotnet is on PATH, the binlog has been produced (whether the -# build succeeded or failed), the binlog path and build outcome are exported -# as `GH_AW_*` env vars, `binlog-mcp` is installed, and the binlog data has -# been dumped to `/tmp/binlog-data/*.json` files for the agent to `cat`. -# -# `continue-on-error: true` is essential on the build step: a failed build -# must not abort the job before the agent gets to analyse it. -steps: - - name: Build with binary log - id: build - continue-on-error: true - run: | - set -o pipefail - ./build.sh --binaryLog 2>&1 | tee /tmp/build-output.log +# Custom build job that runs unconditionally on every PR. It produces the +# binlog and (on failure) dumps it to JSON files which are uploaded as an +# artifact for the agent job to consume. The agent pipeline only runs when +# this job reports `outcome == 'failure'` (see top-level `if:` above). +jobs: + build: + name: Build (for analysis) + runs-on: ubuntu-latest + timeout-minutes: 30 + # Mirror the workflow's `forks: []` trigger filter: skip fork PRs at the + # build-job level too. Without this guard the build job would still run + # for fork PRs (paying CI time and exposing dotnet-tools auth-gated + # installs to forks) even though the agent pipeline never runs for them. + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository + permissions: + contents: read + outputs: + outcome: ${{ steps.build.outcome }} + binlog-found: ${{ steps.find-binlog.outputs.found }} + binlog-relative-path: ${{ steps.find-binlog.outputs.relative-path }} + env: + BINLOG_MCP_VERSION: '1.0.0-preview.26272.1' + steps: + - uses: actions/checkout@v6 - - name: Put dotnet on the path - if: always() - run: echo "$PWD/.dotnet" >> $GITHUB_PATH + - name: Build with binary log + id: build + continue-on-error: true + run: | + set -uo pipefail + ./build.sh --binaryLog 2>&1 | tee /tmp/build-output.log + # `tee` is best-effort: rely on the build's own exit code so a + # logging-pipeline glitch never misclassifies a green build as + # failed (which would otherwise trigger the AI agent and + # re-expose us to the Copilot-flake red-X bug). + exit "${PIPESTATUS[0]}" - - name: Locate binlog - id: find-binlog - run: | - BINLOG=$(find artifacts/log -name '*.binlog' -type f -printf '%T@ %p\n' 2>/dev/null \ - | sort -rn | head -1 | cut -d' ' -f2-) - if [ -n "$BINLOG" ] && [ -f "$BINLOG" ]; then - BINLOG=$(realpath "$BINLOG") - echo "found=true" >> "$GITHUB_OUTPUT" - echo "path=$BINLOG" >> "$GITHUB_OUTPUT" - else - echo "found=false" >> "$GITHUB_OUTPUT" - fi + - name: Put dotnet on the path + if: always() + run: echo "$PWD/.dotnet" >> $GITHUB_PATH - - name: Install binlog-mcp - if: steps.build.outcome == 'failure' && steps.find-binlog.outputs.found == 'true' - run: | - mkdir -p /tmp/binlog-tool - cat > /tmp/binlog-tool/nuget.config <<'EOF' - - - - - - - - EOF - dotnet tool install --global Microsoft.AITools.BinlogMcp \ - --configfile /tmp/binlog-tool/nuget.config \ - --version "$BINLOG_MCP_VERSION" - echo "$HOME/.dotnet/tools" >> "$GITHUB_PATH" + - name: Locate binlog + id: find-binlog + if: always() + run: | + BINLOG=$(find artifacts/log -name '*.binlog' -type f -printf '%T@ %p\n' 2>/dev/null \ + | sort -rn | head -1 | cut -d' ' -f2-) + if [ -n "$BINLOG" ] && [ -f "$BINLOG" ]; then + REL=$(realpath --relative-to="$PWD" "$BINLOG") + echo "found=true" >> "$GITHUB_OUTPUT" + echo "relative-path=$REL" >> "$GITHUB_OUTPUT" + else + echo "found=false" >> "$GITHUB_OUTPUT" + fi + + - name: Install binlog-mcp + if: steps.build.outcome == 'failure' && steps.find-binlog.outputs.found == 'true' + continue-on-error: true + run: | + mkdir -p /tmp/binlog-tool + cat > /tmp/binlog-tool/nuget.config <<'EOF' + + + + + + + + EOF + dotnet tool install --global Microsoft.AITools.BinlogMcp \ + --configfile /tmp/binlog-tool/nuget.config \ + --version "$BINLOG_MCP_VERSION" + echo "$HOME/.dotnet/tools" >> "$GITHUB_PATH" + + - name: Dump binlog as JSON + if: steps.build.outcome == 'failure' && steps.find-binlog.outputs.found == 'true' + continue-on-error: true + env: + BINLOG_REL_PATH: ${{ steps.find-binlog.outputs.relative-path }} + run: | + mkdir -p /tmp/binlog-data + timeout 180 dotnet run --project .github/workflows/scripts/DumpBinlog -- \ + "$BINLOG_REL_PATH" \ + /tmp/binlog-data + + # Upload everything the agent needs. Always upload when the build + # failed (even if dump-binlog failed), so the agent gets the raw + # build output log and can still emit a "build failed, no binlog + # data" comment. + - name: Upload analysis artifact + if: always() && steps.build.outcome == 'failure' + continue-on-error: true + uses: actions/upload-artifact@v7 + with: + name: build-failure-analysis-data + path: | + /tmp/binlog-data/ + /tmp/build-output.log + if-no-files-found: warn + retention-days: 1 + +# Steps that run in the agent job. Because the top-level `if:` gates +# activation on `needs.build.outputs.outcome == 'failure'`, these only run +# for failed builds — the agent never executes on a successful build and a +# transient Copilot AI flake can no longer surface as a red workflow check +# on a passing PR. +steps: + - name: Download analysis artifact + uses: actions/download-artifact@v8 + with: + name: build-failure-analysis-data + path: /tmp/ + + - name: Setup .NET (for NuGet MCP Server) + uses: actions/setup-dotnet@v5 + with: + dotnet-version: '9.0.x' - name: Install NuGet MCP Server - if: steps.build.outcome == 'failure' && steps.find-binlog.outputs.found == 'true' continue-on-error: true run: dotnet tool install --global NuGet.Mcp.Server --version "$NUGET_MCP_VERSION" - - name: Dump binlog as JSON - if: steps.build.outcome == 'failure' && steps.find-binlog.outputs.found == 'true' - continue-on-error: true - env: - BINLOG_PATH: ${{ steps.find-binlog.outputs.path }} - run: | - mkdir -p /tmp/binlog-data - timeout 180 dotnet run --project .github/workflows/scripts/DumpBinlog -- \ - "$BINLOG_PATH" \ - /tmp/binlog-data - # On `workflow_dispatch` runs, `github.sha` is the SHA of the dispatched ref # (usually the default branch), NOT the PR head. Look up the real PR head # SHA via the API so permalinks and inline comment placement match the PR. @@ -141,15 +208,24 @@ steps: - name: Export agent context env: - GH_AW_STEPS_BUILD_OUTCOME: ${{ steps.build.outcome }} - GH_AW_BINLOG_PATH_VALUE: ${{ steps.find-binlog.outputs.path }} + GH_AW_BUILD_OUTCOME_VALUE: ${{ needs.build.outputs.outcome }} + GH_AW_BINLOG_REL_VALUE: ${{ needs.build.outputs.binlog-relative-path }} GH_AW_PR_NUMBER_VALUE: ${{ github.event.pull_request.number || github.event.issue.number || inputs.pr-number }} GH_AW_PR_HEAD_SHA_VALUE: ${{ steps.resolve-pr-sha.outputs.sha || github.event.pull_request.head.sha || github.sha }} GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} run: | + # The binlog file itself is not transported between jobs (it is large + # and the agent only needs the pre-dumped JSON files). Set + # GH_AW_BINLOG_PATH to a synthetic workspace-relative path purely for + # display / permalink purposes; the agent must rely on + # /tmp/binlog-data/*.json for actual data (see shared body). + BINLOG_PATH="" + if [ -n "${GH_AW_BINLOG_REL_VALUE:-}" ]; then + BINLOG_PATH="${GH_AW_GITHUB_WORKSPACE}/${GH_AW_BINLOG_REL_VALUE}" + fi { - echo "GH_AW_BUILD_OUTCOME=${GH_AW_STEPS_BUILD_OUTCOME}" - echo "GH_AW_BINLOG_PATH=${GH_AW_BINLOG_PATH_VALUE}" + echo "GH_AW_BUILD_OUTCOME=${GH_AW_BUILD_OUTCOME_VALUE}" + echo "GH_AW_BINLOG_PATH=${BINLOG_PATH}" echo "GH_AW_PR_NUMBER=${GH_AW_PR_NUMBER_VALUE}" echo "GH_AW_PR_HEAD_SHA=${GH_AW_PR_HEAD_SHA_VALUE}" echo "GH_AW_WORKSPACE=${GH_AW_GITHUB_WORKSPACE}" @@ -172,18 +248,29 @@ tools: - "NuGet.Mcp.Server" safe-outputs: - # The agent runs on every PR (gh-aw has no agent-job-level `if:` hook), so a - # transient AI service flake on an otherwise-successful build would - # otherwise spam tracking issues (see issue #8685). Suppress those: the - # workflow is advisory, and run-level failures are still visible in the - # Actions UI for anyone who wants to investigate. + # The agent runs only when the build job reports failure (see top-level + # `if:` above). On a failed build the agent normally emits at most one + # `noop`, one summary comment, and a small set of inline review comments, + # but the Copilot CLI harness retries with `--continue` on + # mid-conversation AI flakes (up to 3 retries) and each retry re-emits + # every safe-output call it has issued so far. The caps below absorb that + # retry budget without spurious safe-output validation warnings: + # - noop max=5: covers 1 happy-path + 4 retry-amplified noops. + # - add-comment max=5: covers 1 summary + 4 retries (hide-older-comments + # auto-collapses the duplicates anyway). + # - create-pull-request-review-comment max=25: shared body asks the + # agent for "top 5 highest-priority issues" per run, so 5 × (1 + 3 + # retries) = 20 is the worst case under flake amplification. + # We also disable `report-as-issue` / `report-failure-as-issue` so + # transient flakes never spam tracking issues (see issue #8685). report-failure-as-issue: false add-comment: - max: 1 + max: 5 hide-older-comments: true create-pull-request-review-comment: - max: 10 + max: 25 noop: + max: 5 report-as-issue: false ---