Onboard build failure analysis workflow #1
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"1d8398a3e24de5eb4f627fd7cf55f25bf34c3e942ff739aaa617b8db25460d15","body_hash":"bab59b12461e621dd0828119194c30822ffb98a02d47d5f454c492e9739efd12","compiler_version":"v0.86.2","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.79"}} | ||
|
Check failure on line 1 in .github/workflows/build-failure-analysis.lock.yml
|
||
| # gh-aw-manifest: {"version":1,"secrets":["COPILOT_PAT_0","COPILOT_PAT_1","COPILOT_PAT_2","COPILOT_PAT_3","COPILOT_PAT_4","COPILOT_PAT_5","COPILOT_PAT_6","COPILOT_PAT_7","COPILOT_PAT_8","COPILOT_PAT_9","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"3d3c42e5aac5ba805825da76410c181273ba90b1","version":"v7.0.1"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"373c709c69115d41ff229c7e5df9f8788daa9553","version":"v9"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"820762786026740c76f36085b0efc47a31fe5020","version":"v7.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"6aab9e5b5c91c615506061f09bedd81a23babe3c","version":"v0.86.2"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.44","digest":"sha256:0d727725c737b58c7bdf51f640cffb928385ec46517e0917c7f1a02f1bada8b4","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.44@sha256:0d727725c737b58c7bdf51f640cffb928385ec46517e0917c7f1a02f1bada8b4"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.44","digest":"sha256:b50fbadba138f6e9aba94aca09711335c489bb3b15861220cb66f6092e042dc7","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.44@sha256:b50fbadba138f6e9aba94aca09711335c489bb3b15861220cb66f6092e042dc7"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.44","digest":"sha256:83e48bbe12c634be8c228a576832fe45f66c529ac3659db92bddbcf2eeb6d627","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.44@sha256:83e48bbe12c634be8c228a576832fe45f66c529ac3659db92bddbcf2eeb6d627"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.9","digest":"sha256:e5a1569aeaf41820fa7bdee3e94468cae448133cdbf00119ad24f5b74db1ab9f","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.9@sha256:e5a1569aeaf41820fa7bdee3e94468cae448133cdbf00119ad24f5b74db1ab9f"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:0d9f1fb5fd6610c0ac1f5194a38e45a8a1e81f8a390d5142d8e4e6f26a4b3196","pinned_image":"ghcr.io/github/gh-aw-node@sha256:0d9f1fb5fd6610c0ac1f5194a38e45a8a1e81f8a390d5142d8e4e6f26a4b3196"},{"image":"ghcr.io/github/github-mcp-server:v1.9.0","digest":"sha256:881b53d6f75f69bdbc1b5b10fc2f1361717c19054143b3a8529fb5c32061a50e","pinned_image":"ghcr.io/github/github-mcp-server:v1.9.0@sha256:881b53d6f75f69bdbc1b5b10fc2f1361717c19054143b3a8529fb5c32061a50e"},{"image":"mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-binlog-mcp-amd64","digest":"sha256:253736e28e0230269dfcdb70f5027da47e2e45e8526d15d6485ca08b2c2f1638","pinned_image":"mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-binlog-mcp-amd64@sha256:253736e28e0230269dfcdb70f5027da47e2e45e8526d15d6485ca08b2c2f1638"}]} | ||
| # This file was automatically generated by gh-aw (v0.86.2). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md | ||
| # | ||
| # ___ _ _ | ||
| # / _ \ | | (_) | ||
| # | |_| | __ _ ___ _ __ | |_ _ ___ | ||
| # | _ |/ _` |/ _ \ '_ \| __| |/ __| | ||
| # | | | | (_| | __/ | | | |_| | (__ | ||
| # \_| |_/\__, |\___|_| |_|\__|_|\___| | ||
| # __/ | | ||
| # _ _ |___/ | ||
| # | | | | / _| | | ||
| # | | | | ___ _ __ _ __| |_| | _____ ____ | ||
| # | |/\| |/ _ \ '__| |/ /| _| |/ _ \ \ /\ / / ___| | ||
| # \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ | ||
| # \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ | ||
| # | ||
| # | ||
| # To update this file, edit the corresponding .md file and run: | ||
| # gh aw compile | ||
| # Not all edits will cause changes to this file. | ||
| # | ||
| # For more information: https://github.github.com/gh-aw/introduction/overview/ | ||
| # | ||
| # When the Azure Pipelines PR build (`runtime`) fails, downloads the binary logs from its failed or canceled jobs — it does NOT rebuild — and delegates to the `build-failure-analyst` agent, which queries the binlogs live via the containerized `binlog-mcp` MCP server to identify root causes, post a PR comment summarizing them, and attach inline `suggestion` blocks tied to the diff. | ||
| # | ||
| # Resolved workflow manifest: | ||
| # Imports: | ||
| # - shared/build-failure-analysis-shared.md | ||
| # - shared/pat_pool.md | ||
| # | ||
| # Secrets used: | ||
| # - COPILOT_PAT_0 | ||
| # - COPILOT_PAT_1 | ||
| # - COPILOT_PAT_2 | ||
| # - COPILOT_PAT_3 | ||
| # - COPILOT_PAT_4 | ||
| # - COPILOT_PAT_5 | ||
| # - COPILOT_PAT_6 | ||
| # - COPILOT_PAT_7 | ||
| # - COPILOT_PAT_8 | ||
| # - COPILOT_PAT_9 | ||
| # - GH_AW_GITHUB_MCP_SERVER_TOKEN | ||
| # - GH_AW_GITHUB_TOKEN | ||
| # - GITHUB_TOKEN | ||
| # | ||
| # Custom actions used: | ||
| # - actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | ||
| # - actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | ||
| # - actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | ||
| # - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | ||
| # - actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9 | ||
| # - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| # - actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 | ||
| # - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | ||
| # - github/gh-aw-actions/setup@6aab9e5b5c91c615506061f09bedd81a23babe3c # v0.86.2 | ||
| # | ||
| # Container images used: | ||
| # - ghcr.io/github/gh-aw-firewall/agent:0.27.44@sha256:0d727725c737b58c7bdf51f640cffb928385ec46517e0917c7f1a02f1bada8b4 | ||
| # - ghcr.io/github/gh-aw-firewall/api-proxy:0.27.44@sha256:b50fbadba138f6e9aba94aca09711335c489bb3b15861220cb66f6092e042dc7 | ||
| # - ghcr.io/github/gh-aw-firewall/squid:0.27.44@sha256:83e48bbe12c634be8c228a576832fe45f66c529ac3659db92bddbcf2eeb6d627 | ||
| # - ghcr.io/github/gh-aw-mcpg:v0.4.9@sha256:e5a1569aeaf41820fa7bdee3e94468cae448133cdbf00119ad24f5b74db1ab9f | ||
| # - ghcr.io/github/gh-aw-node@sha256:0d9f1fb5fd6610c0ac1f5194a38e45a8a1e81f8a390d5142d8e4e6f26a4b3196 | ||
| # - ghcr.io/github/github-mcp-server:v1.9.0@sha256:881b53d6f75f69bdbc1b5b10fc2f1361717c19054143b3a8529fb5c32061a50e | ||
| # - mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-binlog-mcp-amd64@sha256:253736e28e0230269dfcdb70f5027da47e2e45e8526d15d6485ca08b2c2f1638 | ||
| name: "Build Failure Analysis" | ||
| on: | ||
| check_run: | ||
| types: | ||
| - completed | ||
| # roles: all # Roles processed as role check in pre-activation job | ||
| workflow_dispatch: | ||
| inputs: | ||
| aw_context: | ||
| default: "" | ||
| description: "Agent caller context (used internally by Agentic Workflows)." | ||
| required: false | ||
| type: string | ||
| ado-build-id: | ||
| description: Azure DevOps build id to analyze (dnceng-public/public). | ||
| required: true | ||
| type: string | ||
| pr-number: | ||
| description: PR number to post the analysis on. | ||
| required: true | ||
| type: string | ||
| permissions: {} | ||
| concurrency: | ||
| cancel-in-progress: true | ||
| group: ${{ (github.event_name == 'check_run' && github.event.check_run.name == 'runtime' && format('build-failure-analysis-{0}', github.event.check_run.pull_requests[0].number || github.event.check_run.head_sha)) || (github.event_name == 'workflow_dispatch' && format('build-failure-analysis-{0}', inputs['pr-number'])) || format('build-failure-analysis-run-{0}', github.run_id) }} | ||
| run-name: "Build Failure Analysis" | ||
| jobs: | ||
| activation: | ||
| needs: | ||
| - fetch-binlog | ||
| - pat_pool | ||
| - pre_activation | ||
| if: needs.pre_activation.outputs.activated == 'true' && (needs.fetch-binlog.outputs.binlog-found == 'true') | ||
| runs-on: ubuntu-slim | ||
| permissions: | ||
| actions: read | ||
| contents: read | ||
| env: | ||
| GH_AW_MAX_DAILY_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_DAILY_AI_CREDITS || '5000' }} | ||
| GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} | ||
| outputs: | ||
| comment_id: "" | ||
| comment_repo: "" | ||
| daily_ai_credits_exceeded: ${{ steps.daily-effective-workflow-guardrail.outputs.daily_ai_credits_exceeded == 'true' }} | ||
| daily_ai_credits_guardrail_status: ${{ steps.daily-effective-workflow-guardrail.outputs.daily_ai_credits_guardrail_status || '' }} | ||
| daily_ai_credits_threshold: ${{ steps.daily-effective-workflow-guardrail.outputs.daily_ai_credits_threshold || '' }} | ||
| daily_ai_credits_total_effective_tokens: ${{ steps.daily-effective-workflow-guardrail.outputs.daily_ai_credits_total_effective_tokens || '' }} | ||
| engine_id: ${{ steps.generate_aw_info.outputs.engine_id }} | ||
| lockdown_check_failed: ${{ steps.generate_aw_info.outputs.lockdown_check_failed == 'true' }} | ||
| model: ${{ steps.generate_aw_info.outputs.model }} | ||
| oauth_token_check_failed: ${{ steps.check-oauth-tokens.outputs.oauth_token_check_failed == 'true' }} | ||
| setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }} | ||
| setup-span-id: ${{ steps.setup.outputs.span-id }} | ||
| setup-trace-id: ${{ steps.setup.outputs.trace-id }} | ||
| stale_lock_file_failed: ${{ steps.check-lock-file.outputs.stale_lock_file_failed == 'true' }} | ||
| steps: | ||
| - name: Setup Scripts | ||
| id: setup | ||
| uses: github/gh-aw-actions/setup@6aab9e5b5c91c615506061f09bedd81a23babe3c # v0.86.2 | ||
| with: | ||
| destination: ${{ runner.temp }}/gh-aw/actions | ||
| job-name: ${{ github.job }} | ||
| trace-id: ${{ needs.pre_activation.outputs.setup-trace-id }} | ||
| parent-span-id: ${{ needs.pre_activation.outputs.setup-parent-span-id || needs.pre_activation.outputs.setup-span-id }} | ||
| safe-output-artifact-client: ${{ env.GH_AW_MAX_DAILY_AI_CREDITS != '' }} | ||
| env: | ||
| GH_AW_SETUP_WORKFLOW_NAME: "Build Failure Analysis" | ||
| GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/build-failure-analysis.lock.yml@${{ github.ref }} | ||
| GH_AW_INFO_VERSION: "1.0.79" | ||
| GH_AW_INFO_AWF_VERSION: "v0.27.44" | ||
| GH_AW_INFO_ENGINE_ID: "copilot" | ||
| - name: Generate agentic run info | ||
| id: generate_aw_info | ||
| env: | ||
| GH_AW_INFO_ENGINE_ID: "copilot" | ||
| GH_AW_INFO_ENGINE_NAME: "GitHub Copilot CLI" | ||
| GH_AW_INFO_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || vars.GH_AW_DEFAULT_MODEL_COPILOT || 'auto' }} | ||
| GH_AW_INFO_VERSION: "1.0.79" | ||
| GH_AW_INFO_AGENT_VERSION: "1.0.79" | ||
| GH_AW_INFO_CLI_VERSION: "v0.86.2" | ||
| GH_AW_INFO_WORKFLOW_NAME: "Build Failure Analysis" | ||
| GH_AW_INFO_EXPERIMENTAL: "false" | ||
| GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" | ||
| GH_AW_INFO_STAGED: "false" | ||
| GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","dotnet"]' | ||
| GH_AW_INFO_FIREWALL_ENABLED: "true" | ||
| GH_AW_INFO_AWF_VERSION: "v0.27.44" | ||
| GH_AW_INFO_AWMG_VERSION: "" | ||
| GH_AW_INFO_FIREWALL_TYPE: "squid" | ||
| GH_AW_INFO_AGENT_RUNTIME: "" | ||
| GH_AW_COMPILED_STRICT: "true" | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| with: | ||
| script: | | ||
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require('${{ runner.temp }}/gh-aw/actions/generate_aw_info.cjs'); | ||
| await main(core, context); | ||
| - name: Restore daily AIC usage cache | ||
| id: restore-daily-aic-cache | ||
| if: ${{ env.GH_AW_MAX_DAILY_AI_CREDITS != '' }} | ||
| continue-on-error: true | ||
| uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | ||
| with: | ||
| key: agentic-workflow-usage-buildfailureanalysis-${{ github.run_id }} | ||
| restore-keys: agentic-workflow-usage-buildfailureanalysis- | ||
| path: /tmp/gh-aw/agentic-workflow-usage-cache.jsonl | ||
| - name: Restore daily AIC usage cache (artifact fallback) | ||
| id: restore-daily-aic-cache-fallback | ||
| if: ${{ env.GH_AW_MAX_DAILY_AI_CREDITS != '' }} | ||
| continue-on-error: true | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| env: | ||
| GH_AW_RESTORE_DAILY_AIC_CACHE_HIT: ${{ steps.restore-daily-aic-cache.outputs.cache-hit }} | ||
| GH_AW_RESTORE_DAILY_AIC_CACHE_MATCHED_KEY: ${{ steps.restore-daily-aic-cache.outputs.cache-matched-key }} | ||
| with: | ||
| github-token: ${{ secrets.GITHUB_TOKEN }} | ||
| script: | | ||
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require('${{ runner.temp }}/gh-aw/actions/restore_aic_usage_cache_fallback.cjs'); | ||
| await main(); | ||
| - name: Check daily workflow token guardrail | ||
| id: daily-effective-workflow-guardrail | ||
| if: ${{ env.GH_AW_MAX_DAILY_AI_CREDITS != '' }} | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| env: | ||
| GH_AW_WORKFLOW_NAME: "Build Failure Analysis" | ||
| GH_AW_WORKFLOW_ID: "build-failure-analysis" | ||
| GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | ||
| GH_AW_WORKFLOW_DISPATCH_AW_CONTEXT: ${{ github.event.inputs.aw_context || '' }} | ||
| GH_AW_HAS_SLASH_COMMAND: "false" | ||
| GH_AW_HAS_LABEL_COMMAND: "false" | ||
| GH_AW_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| GH_AW_MAX_DAILY_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_DAILY_AI_CREDITS || '5000' }} | ||
| with: | ||
| github-token: ${{ secrets.GITHUB_TOKEN }} | ||
| script: | | ||
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require('${{ runner.temp }}/gh-aw/actions/check_daily_aic_workflow_guardrail.cjs'); | ||
| await main(); | ||
| - name: Check for OAuth tokens | ||
| id: check-oauth-tokens | ||
| run: bash "${RUNNER_TEMP}/gh-aw/actions/check_oauth_tokens.sh" | ||
| env: | ||
| COPILOT_GITHUB_TOKEN: ${{ case(needs.pat_pool.outputs.pat_number == '0', secrets.COPILOT_PAT_0, needs.pat_pool.outputs.pat_number == '1', secrets.COPILOT_PAT_1, needs.pat_pool.outputs.pat_number == '2', secrets.COPILOT_PAT_2, needs.pat_pool.outputs.pat_number == '3', secrets.COPILOT_PAT_3, needs.pat_pool.outputs.pat_number == '4', secrets.COPILOT_PAT_4, needs.pat_pool.outputs.pat_number == '5', secrets.COPILOT_PAT_5, needs.pat_pool.outputs.pat_number == '6', secrets.COPILOT_PAT_6, needs.pat_pool.outputs.pat_number == '7', secrets.COPILOT_PAT_7, needs.pat_pool.outputs.pat_number == '8', secrets.COPILOT_PAT_8, needs.pat_pool.outputs.pat_number == '9', secrets.COPILOT_PAT_9, 'NO COPILOT PAT AVAILABLE') }} | ||
| GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} | ||
| GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} | ||
| - name: Checkout .github and .agents folders | ||
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | ||
| with: | ||
| persist-credentials: false | ||
| sparse-checkout: | | ||
| .github | ||
| .agents | ||
| .claude | ||
| .codex | ||
| .gemini | ||
| .pi | ||
| sparse-checkout-cone-mode: true | ||
| fetch-depth: 1 | ||
| - name: Save agent config folders for base branch restoration | ||
| env: | ||
| GH_AW_AGENT_FOLDERS: ".agents .github" | ||
| GH_AW_AGENT_FILES: "AGENTS.md" | ||
| run: | | ||
| bash "${RUNNER_TEMP}/gh-aw/actions/save_base_github_folders.sh" | ||
| - name: Check workflow lock file | ||
| id: check-lock-file | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| env: | ||
| GH_AW_WORKFLOW_FILE: "build-failure-analysis.lock.yml" | ||
| GH_AW_CONTEXT_WORKFLOW_REF: "${{ github.workflow_ref }}" | ||
| with: | ||
| script: | | ||
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require('${{ runner.temp }}/gh-aw/actions/check_workflow_timestamp_api.cjs'); | ||
| await main(); | ||
| - name: Check compile-agentic version | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| env: | ||
| GH_AW_COMPILED_VERSION: "v0.86.2" | ||
| with: | ||
| script: | | ||
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require('${{ runner.temp }}/gh-aw/actions/check_version_updates.cjs'); | ||
| await main(); | ||
| - name: Log runtime features | ||
| if: ${{ contains(toJSON(vars), '"GH_AW_RUNTIME_FEATURES":') }} | ||
| run: bash "${RUNNER_TEMP}/gh-aw/actions/log_runtime_features_summary.sh" | ||
| - name: Create prompt with built-in context | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| env: | ||
| GH_AW_ACTIONS_DIR: ${{ runner.temp }}/gh-aw/actions | ||
| GH_AW_PROMPT: ${{ runner.temp }}/gh-aw/aw-prompts/prompt.txt | ||
| GH_AW_SAFE_OUTPUTS: ${{ runner.temp }}/gh-aw/safeoutputs/outputs.jsonl | ||
| GH_AW_PROMPT_CONFIG: "{\"items\":[{\"content_env\":\"GH_AW_PROMPT_CONTENT_0000\"},{\"file\":\"xpia.md\"},{\"file\":\"temp_folder_prompt.md\"},{\"file\":\"markdown.md\"},{\"file\":\"safe_outputs_prompt.md\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0001\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0002\"},{\"file\":\"mcp_cli_tools_prompt.md\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0003\"},{\"file\":\"github_mcp_tools_with_safeoutputs_prompt.md\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0004\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0005\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0006\"}]}" | ||
| GH_AW_EXPR_1A3A194A: ${{ github.event.discussion.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'discussion' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} | ||
| GH_AW_EXPR_463A214A: ${{ github.event.pull_request.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'pull_request' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} | ||
| GH_AW_EXPR_802A9F6A: ${{ github.event.issue.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'issue' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} | ||
| GH_AW_EXPR_FF1D34CE: ${{ github.event.comment.id || fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').comment_id }} | ||
| GH_AW_GITHUB_ACTOR: ${{ github.actor }} | ||
| GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} | ||
| GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} | ||
| GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} | ||
| GH_AW_PROMPT_CONTENT_0000: "<system>\n" | ||
| GH_AW_PROMPT_CONTENT_0001: "<safe-output-tools>\nTools: add_comment(max:5), create_pull_request_review_comment(max:25), missing_tool, missing_data, noop(max:5)\n" | ||
| GH_AW_PROMPT_CONTENT_0002: "</safe-output-tools>\n" | ||
| GH_AW_PROMPT_CONTENT_0003: "<github-context>\nThe following GitHub context information is available for this workflow:\n{{#if github.actor}}\n- **actor**: __GH_AW_GITHUB_ACTOR__\n{{/if}}\n{{#if github.repository}}\n- **repository**: __GH_AW_GITHUB_REPOSITORY__\n{{/if}}\n{{#if github.workspace}}\n- **workspace**: __GH_AW_GITHUB_WORKSPACE__\n{{/if}}\n{{#if github.event.issue.number || (github.aw.context.item_type == 'issue' && github.aw.context.item_number)}}\n- **issue-number**: #__GH_AW_EXPR_802A9F6A__\n{{/if}}\n{{#if github.event.discussion.number || (github.aw.context.item_type == 'discussion' && github.aw.context.item_number)}}\n- **discussion-number**: #__GH_AW_EXPR_1A3A194A__\n{{/if}}\n{{#if github.event.pull_request.number || (github.aw.context.item_type == 'pull_request' && github.aw.context.item_number)}}\n- **pull-request-number**: #__GH_AW_EXPR_463A214A__\n{{/if}}\n{{#if github.event.comment.id || github.aw.context.comment_id}}\n- **comment-id**: __GH_AW_EXPR_FF1D34CE__\n{{/if}}\n{{#if github.run_id}}\n- **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__\n{{/if}}\n</github-context>\n\n" | ||
| GH_AW_PROMPT_CONTENT_0004: "</system>\n" | ||
| GH_AW_PROMPT_CONTENT_0005: "{{#runtime-import .github/workflows/shared/build-failure-analysis-shared.md}}\n" | ||
| GH_AW_PROMPT_CONTENT_0006: "{{#runtime-import .github/workflows/build-failure-analysis.md}}\n" | ||
| with: | ||
| script: | | ||
| const { setupGlobals } = require(process.env.GH_AW_ACTIONS_DIR + '/setup_globals.cjs'); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require(process.env.GH_AW_ACTIONS_DIR + '/create_prompt.cjs'); | ||
| await main(core); | ||
| - name: Interpolate variables and render templates | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| env: | ||
| GH_AW_PROMPT: ${{ runner.temp }}/gh-aw/aw-prompts/prompt.txt | ||
| GH_AW_ENGINE_ID: "copilot" | ||
| with: | ||
| script: | | ||
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require('${{ runner.temp }}/gh-aw/actions/interpolate_prompt.cjs'); | ||
| await main(); | ||
| - name: Substitute placeholders | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| env: | ||
| GH_AW_PROMPT: ${{ runner.temp }}/gh-aw/aw-prompts/prompt.txt | ||
| GH_AW_EXPR_1A3A194A: ${{ github.event.discussion.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'discussion' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} | ||
| GH_AW_EXPR_463A214A: ${{ github.event.pull_request.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'pull_request' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} | ||
| GH_AW_EXPR_802A9F6A: ${{ github.event.issue.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'issue' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} | ||
| GH_AW_EXPR_FF1D34CE: ${{ github.event.comment.id || fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').comment_id }} | ||
| GH_AW_GITHUB_ACTOR: ${{ github.actor }} | ||
| GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} | ||
| GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} | ||
| GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} | ||
| GH_AW_MCP_CLI_SERVERS_LIST: "- `binlog-mcp` — run `binlog-mcp --help` to see available tools\n- `github` — run `github --help` to see available tools\n- `safeoutputs` — run `safeoutputs --help` to see available tools" | ||
| GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }} | ||
| with: | ||
| script: | | ||
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const substitutePlaceholders = require('${{ runner.temp }}/gh-aw/actions/substitute_placeholders.cjs'); | ||
| // Call the substitution function | ||
| return await substitutePlaceholders({ | ||
| file: process.env.GH_AW_PROMPT, | ||
| substitutions: { | ||
| GH_AW_EXPR_1A3A194A: process.env.GH_AW_EXPR_1A3A194A, | ||
| GH_AW_EXPR_463A214A: process.env.GH_AW_EXPR_463A214A, | ||
| GH_AW_EXPR_802A9F6A: process.env.GH_AW_EXPR_802A9F6A, | ||
| GH_AW_EXPR_FF1D34CE: process.env.GH_AW_EXPR_FF1D34CE, | ||
| GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR, | ||
| GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY, | ||
| GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID, | ||
| GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE, | ||
| GH_AW_MCP_CLI_SERVERS_LIST: process.env.GH_AW_MCP_CLI_SERVERS_LIST, | ||
| GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED | ||
| } | ||
| }); | ||
| - name: Validate prompt placeholders | ||
| env: | ||
| GH_AW_PROMPT: ${{ runner.temp }}/gh-aw/aw-prompts/prompt.txt | ||
| run: | | ||
| bash "${RUNNER_TEMP}/gh-aw/actions/validate_prompt_placeholders.sh" | ||
| - name: Print prompt | ||
| env: | ||
| GH_AW_PROMPT: ${{ runner.temp }}/gh-aw/aw-prompts/prompt.txt | ||
| run: | | ||
| bash "${RUNNER_TEMP}/gh-aw/actions/print_prompt_summary.sh" | ||
| - name: Stage prompt files for artifact upload | ||
| run: | | ||
| mkdir -p /tmp/gh-aw/aw-prompts | ||
| cp -a "${RUNNER_TEMP}/gh-aw/aw-prompts/." /tmp/gh-aw/aw-prompts/ | ||
| - name: Upload activation artifact | ||
| if: success() | ||
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | ||
| with: | ||
| name: activation | ||
| include-hidden-files: true | ||
| path: | | ||
| /tmp/gh-aw/aw_info.json | ||
| /tmp/gh-aw/models.json | ||
| /tmp/gh-aw/aw-prompts/prompt.txt | ||
| /tmp/gh-aw/aw-prompts/prompt-template.txt | ||
| /tmp/gh-aw/aw-prompts/prompt-import-tree.json | ||
| /tmp/gh-aw/github_rate_limits.jsonl | ||
| /tmp/gh-aw/base | ||
| /tmp/gh-aw/.github/agents | ||
| /tmp/gh-aw/.github/skills | ||
| if-no-files-found: ignore | ||
| retention-days: 1 | ||
| agent: | ||
| needs: | ||
| - activation | ||
| - fetch-binlog | ||
| - pat_pool | ||
| if: needs.activation.outputs.daily_ai_credits_exceeded != 'true' | ||
| runs-on: ubuntu-latest | ||
| environment: copilot-pat-pool | ||
| permissions: | ||
| contents: read | ||
| pull-requests: read | ||
| concurrency: | ||
| group: "gh-aw-copilot-${{ github.workflow }}" | ||
| queue: max | ||
| env: | ||
| DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} | ||
| GH_AW_ASSETS_ALLOWED_EXTS: "" | ||
| GH_AW_ASSETS_BRANCH: "" | ||
| GH_AW_ASSETS_MAX_SIZE_KB: 0 | ||
| GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs | ||
| GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} | ||
| GH_AW_WORKFLOW_ID_SANITIZED: buildfailureanalysis | ||
| outputs: | ||
| agentic_engine_timeout: ${{ steps.detect-agent-errors.outputs.agentic_engine_timeout || 'false' }} | ||
| ai_credits_rate_limit_error: ${{ steps.parse-mcp-gateway.outputs.ai_credits_rate_limit_error || 'false' }} | ||
| aic: ${{ steps.parse-mcp-gateway.outputs.aic }} | ||
| ambient_context: ${{ steps.parse-mcp-gateway.outputs.ambient_context }} | ||
| checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }} | ||
| effective_tokens: ${{ steps.parse-mcp-gateway.outputs.effective_tokens }} | ||
| has_patch: ${{ steps.collect_output.outputs.has_patch }} | ||
| http_400_response_error: ${{ steps.detect-agent-errors.outputs.http_400_response_error || 'false' }} | ||
| inference_access_error: ${{ steps.detect-agent-errors.outputs.inference_access_error || 'false' }} | ||
| invocation_cap_exceeded: ${{ steps.detect-agent-errors.outputs.invocation_cap_exceeded || 'false' }} | ||
| max_cache_misses_exceeded: ${{ steps.detect-agent-errors.outputs.max_cache_misses_exceeded || 'false' }} | ||
| mcp_policy_error: ${{ steps.detect-agent-errors.outputs.mcp_policy_error || 'false' }} | ||
| missing_model_pricing_error: ${{ steps.detect-agent-errors.outputs.missing_model_pricing_error || 'false' }} | ||
| missing_model_pricing_model_name: ${{ steps.detect-agent-errors.outputs.missing_model_pricing_model_name || '' }} | ||
| model: ${{ needs.activation.outputs.model }} | ||
| model_not_supported_error: ${{ steps.detect-agent-errors.outputs.model_not_supported_error || 'false' }} | ||
| output: ${{ steps.collect_output.outputs.output }} | ||
| output_types: ${{ steps.collect_output.outputs.output_types }} | ||
| setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }} | ||
| setup-span-id: ${{ steps.setup.outputs.span-id }} | ||
| setup-trace-id: ${{ steps.setup.outputs.trace-id }} | ||
| unknown_model_ai_credits: ${{ steps.parse-mcp-gateway.outputs.unknown_model_ai_credits || 'false' }} | ||
| steps: | ||
| - name: Setup Scripts | ||
| id: setup | ||
| uses: github/gh-aw-actions/setup@6aab9e5b5c91c615506061f09bedd81a23babe3c # v0.86.2 | ||
| with: | ||
| destination: ${{ runner.temp }}/gh-aw/actions | ||
| job-name: ${{ github.job }} | ||
| trace-id: ${{ needs.activation.outputs.setup-trace-id }} | ||
| parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} | ||
| env: | ||
| GH_AW_SETUP_WORKFLOW_NAME: "Build Failure Analysis" | ||
| GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/build-failure-analysis.lock.yml@${{ github.ref }} | ||
| GH_AW_INFO_VERSION: "1.0.79" | ||
| GH_AW_INFO_AWF_VERSION: "v0.27.44" | ||
| GH_AW_INFO_ENGINE_ID: "copilot" | ||
| - name: Set runtime paths | ||
| id: set-runtime-paths | ||
| run: | | ||
| { | ||
| echo "GH_AW_SAFE_OUTPUTS=${RUNNER_TEMP}/gh-aw/safeoutputs/outputs.jsonl" | ||
| echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" | ||
| echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/tools.json" | ||
| } >> "$GITHUB_OUTPUT" | ||
| - name: Checkout repository | ||
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | ||
| with: | ||
| persist-credentials: false | ||
| - 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 activation artifact | ||
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | ||
| with: | ||
| name: activation | ||
| path: /tmp/gh-aw | ||
| - name: Download analysis artifact | ||
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | ||
| with: | ||
| name: build-failure-analysis-data | ||
| path: /tmp/binlogs | ||
| - env: | ||
| GH_AW_ADO_BUILD_URL_VALUE: ${{ needs.fetch-binlog.outputs.ado-build-url }} | ||
| GH_AW_BINLOG_FOUND_VALUE: ${{ needs.fetch-binlog.outputs.binlog-found }} | ||
| GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} | ||
| GH_AW_PR_HEAD_SHA_VALUE: ${{ needs.fetch-binlog.outputs.pr-head-sha }} | ||
| GH_AW_PR_MERGE_SHA_VALUE: ${{ needs.fetch-binlog.outputs.pr-merge-sha }} | ||
| GH_AW_PR_NUMBER_VALUE: ${{ needs.fetch-binlog.outputs.pr-number }} | ||
| name: Export agent context | ||
| run: "# The binlogs are mounted into the binlog-mcp container at\n# `/data/binlogs`. Build the list of in-container binlog paths (one per\n# selected artifact) that the agent should query. `GH_AW_BINLOG_PATH` is\n# the first entry for tools/prompts that expect a single path.\nBINLOG_DIR=\"/data/binlogs\"\nLIST=\"\"\nif [ \"${GH_AW_BINLOG_FOUND_VALUE:-false}\" = \"true\" ] && [ -d /tmp/binlogs ]; then\n for f in /tmp/binlogs/*.binlog; do\n [ -f \"$f\" ] || continue\n LIST=\"${LIST}${BINLOG_DIR}/$(basename \"$f\")\"$'\\n'\n done\nfi\n# `shell: bash` puts this step under `-eo pipefail`, so take the first\n# entry with a parameter expansion instead of `printf | head -1`: a pipe\n# whose reader exits early would raise SIGPIPE and abort the step.\nFIRST=${LIST%%$'\\n'*}\n{\n echo \"GH_AW_BUILD_OUTCOME=failure\"\n echo \"GH_AW_BINLOG_DIR=${BINLOG_DIR}\"\n echo \"GH_AW_BINLOG_PATH=${FIRST}\"\n echo \"GH_AW_BINLOG_HOST_PATH=${GH_AW_ADO_BUILD_URL_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_PR_MERGE_SHA=${GH_AW_PR_MERGE_SHA_VALUE}\"\n echo \"GH_AW_WORKSPACE=${GH_AW_GITHUB_WORKSPACE}\"\n echo \"GH_AW_BINLOG_LIST<<GH_AW_EOF\"\n printf '%s' \"$LIST\"\n echo \"GH_AW_EOF\"\n} >> \"$GITHUB_ENV\"\n" | ||
| shell: bash | ||
| - name: Configure Git credentials | ||
| env: | ||
| GITHUB_REPOSITORY: ${{ github.repository }} | ||
| GITHUB_SERVER_URL: ${{ github.server_url }} | ||
| GITHUB_TOKEN: ${{ github.token }} | ||
| run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_git_credentials.sh" | ||
| - name: Checkout PR branch | ||
| id: checkout-pr | ||
| if: | | ||
| github.event.pull_request || github.event.issue.pull_request || github.event_name == 'workflow_dispatch' && fromJSON(github.event.inputs.aw_context || '{}').item_type == 'pull_request' | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | ||
| with: | ||
| github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | ||
| script: | | ||
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require('${{ runner.temp }}/gh-aw/actions/checkout_pr_branch.cjs'); | ||
| await main(); | ||
| - name: Install ripgrep | ||
| run: bash "${RUNNER_TEMP}/gh-aw/actions/install_ripgrep.sh" | ||
| - name: Install GitHub Copilot CLI | ||
| run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" | ||
| env: | ||
| GH_HOST: github.com | ||
| GH_AW_COMPILED_VERSION: v0.86.2 | ||
| - name: Install AWF binary | ||
| run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.27.44 --rootless | ||
| - name: Determine automatic lockdown mode for GitHub MCP Server | ||
| id: determine-automatic-lockdown | ||
| uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9 | ||
| env: | ||
| GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} | ||
| GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} | ||
| with: | ||
| script: | | ||
| const determineAutomaticLockdown = require('${{ runner.temp }}/gh-aw/actions/determine_automatic_lockdown.cjs'); | ||
| await determineAutomaticLockdown(github, context, core); | ||
| - name: Restore agent config folders from base branch | ||
| if: steps.checkout-pr.outcome == 'success' | ||
| env: | ||
| GH_AW_AGENT_FOLDERS: ".agents .github" | ||
| GH_AW_AGENT_FILES: "AGENTS.md" | ||
| run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_base_github_folders.sh" | ||
| - name: Restore inline sub-agents from activation artifact | ||
| env: | ||
| GH_AW_SUB_AGENT_DIR: ".github/agents" | ||
| GH_AW_SUB_AGENT_EXT: ".agent.md" | ||
| run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_inline_sub_agents.sh" | ||
| - name: Restore inline skills from activation artifact | ||
| env: | ||
| GH_AW_SKILL_DIR: ".github/skills" | ||
| run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_inline_skills.sh" | ||
| - name: Download container images | ||
| run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.27.44@sha256:0d727725c737b58c7bdf51f640cffb928385ec46517e0917c7f1a02f1bada8b4 ghcr.io/github/gh-aw-firewall/api-proxy:0.27.44@sha256:b50fbadba138f6e9aba94aca09711335c489bb3b15861220cb66f6092e042dc7 ghcr.io/github/gh-aw-firewall/squid:0.27.44@sha256:83e48bbe12c634be8c228a576832fe45f66c529ac3659db92bddbcf2eeb6d627 ghcr.io/github/gh-aw-mcpg:v0.4.9@sha256:e5a1569aeaf41820fa7bdee3e94468cae448133cdbf00119ad24f5b74db1ab9f ghcr.io/github/gh-aw-node@sha256:0d9f1fb5fd6610c0ac1f5194a38e45a8a1e81f8a390d5142d8e4e6f26a4b3196 ghcr.io/github/github-mcp-server:v1.9.0@sha256:881b53d6f75f69bdbc1b5b10fc2f1361717c19054143b3a8529fb5c32061a50e mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-binlog-mcp-amd64@sha256:253736e28e0230269dfcdb70f5027da47e2e45e8526d15d6485ca08b2c2f1638 | ||
| - name: Generate Safe Outputs Config | ||
| run: | | ||
| 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_c635ae1407bcc5a1_EOF' | ||
| {"add_comment":{"data_enabled":true,"data_schema":{"additionalProperties":false,"properties":{"artifact_kind":{"enum":["analysis","no-binlog"],"type":"string"},"workflow_artifact":{"enum":["build-failure-analysis"],"type":"string"}},"required":["artifact_kind","workflow_artifact"],"type":"object"},"hide_older_comments":true,"max":5,"target":"${{ github.event_name == 'workflow_dispatch' \u0026\u0026 inputs['pr-number'] || github.event.check_run.pull_requests[0].number }}"},"create_pull_request_review_comment":{"data_enabled":true,"data_schema":{"additionalProperties":false,"properties":{"artifact_kind":{"enum":["analysis","no-binlog"],"type":"string"},"workflow_artifact":{"enum":["build-failure-analysis"],"type":"string"}},"required":["artifact_kind","workflow_artifact"],"type":"object"},"max":25,"side":"RIGHT","target":"${{ github.event_name == 'workflow_dispatch' \u0026\u0026 inputs['pr-number'] || github.event.check_run.pull_requests[0].number }}"},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":5,"report-as-issue":"false"},"report_incomplete":{}} | ||
| GH_AW_SAFE_OUTPUTS_CONFIG_c635ae1407bcc5a1_EOF | ||
| - name: Generate Safe Outputs Tools | ||
| env: | ||
| GH_AW_TOOLS_META_JSON: | | ||
| { | ||
| "description_suffixes": { | ||
| "add_comment": " CONSTRAINTS: Maximum 5 comment(s) can be added. Target: ${{ github.event_name == 'workflow_dispatch' \u0026\u0026 inputs['pr-number'] || github.event.check_run.pull_requests[0].number }}. 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": [], | ||
| "property_injections": { | ||
| "add_comment": { | ||
| "data": { | ||
| "additionalProperties": false, | ||
| "properties": { | ||
| "artifact_kind": { | ||
| "enum": [ | ||
| "analysis", | ||
| "no-binlog" | ||
| ], | ||
| "type": "string" | ||
| }, | ||
| "workflow_artifact": { | ||
| "enum": [ | ||
| "build-failure-analysis" | ||
| ], | ||
| "type": "string" | ||
| } | ||
| }, | ||
| "required": [ | ||
| "artifact_kind", | ||
| "workflow_artifact" | ||
| ], | ||
| "type": "object" | ||
| } | ||
| }, | ||
| "create_issue": { | ||
| "data": { | ||
| "additionalProperties": false, | ||
| "properties": { | ||
| "artifact_kind": { | ||
| "enum": [ | ||
| "analysis", | ||
| "no-binlog" | ||
| ], | ||
| "type": "string" | ||
| }, | ||
| "workflow_artifact": { | ||
| "enum": [ | ||
| "build-failure-analysis" | ||
| ], | ||
| "type": "string" | ||
| } | ||
| }, | ||
| "required": [ | ||
| "artifact_kind", | ||
| "workflow_artifact" | ||
| ], | ||
| "type": "object" | ||
| } | ||
| }, | ||
| "create_pull_request": { | ||
| "data": { | ||
| "additionalProperties": false, | ||
| "properties": { | ||
| "artifact_kind": { | ||
| "enum": [ | ||
| "analysis", | ||
| "no-binlog" | ||
| ], | ||
| "type": "string" | ||
| }, | ||
| "workflow_artifact": { | ||
| "enum": [ | ||
| "build-failure-analysis" | ||
| ], | ||
| "type": "string" | ||
| } | ||
| }, | ||
| "required": [ | ||
| "artifact_kind", | ||
| "workflow_artifact" | ||
| ], | ||
| "type": "object" | ||
| } | ||
| }, | ||
| "create_pull_request_review_comment": { | ||
| "data": { | ||
| "additionalProperties": false, | ||
| "properties": { | ||
| "artifact_kind": { | ||
| "enum": [ | ||
| "analysis", | ||
| "no-binlog" | ||
| ], | ||
| "type": "string" | ||
| }, | ||
| "workflow_artifact": { | ||
| "enum": [ | ||
| "build-failure-analysis" | ||
| ], | ||
| "type": "string" | ||
| } | ||
| }, | ||
| "required": [ | ||
| "artifact_kind", | ||
| "workflow_artifact" | ||
| ], | ||
| "type": "object" | ||
| } | ||
| }, | ||
| "reply_to_pull_request_review_comment": { | ||
| "data": { | ||
| "additionalProperties": false, | ||
| "properties": { | ||
| "artifact_kind": { | ||
| "enum": [ | ||
| "analysis", | ||
| "no-binlog" | ||
| ], | ||
| "type": "string" | ||
| }, | ||
| "workflow_artifact": { | ||
| "enum": [ | ||
| "build-failure-analysis" | ||
| ], | ||
| "type": "string" | ||
| } | ||
| }, | ||
| "required": [ | ||
| "artifact_kind", | ||
| "workflow_artifact" | ||
| ], | ||
| "type": "object" | ||
| } | ||
| }, | ||
| "submit_pull_request_review": { | ||
| "data": { | ||
| "additionalProperties": false, | ||
| "properties": { | ||
| "artifact_kind": { | ||
| "enum": [ | ||
| "analysis", | ||
| "no-binlog" | ||
| ], | ||
| "type": "string" | ||
| }, | ||
| "workflow_artifact": { | ||
| "enum": [ | ||
| "build-failure-analysis" | ||
| ], | ||
| "type": "string" | ||
| } | ||
| }, | ||
| "required": [ | ||
| "artifact_kind", | ||
| "workflow_artifact" | ||
| ], | ||
| "type": "object" | ||
| } | ||
| } | ||
| } | ||
| } | ||
| GH_AW_VALIDATION_JSON: | | ||
| { | ||
| "add_comment": { | ||
| "defaultMax": 1, | ||
| "fields": { | ||
| "body": { | ||
| "required": true, | ||
| "type": "string", | ||
| "sanitize": true, | ||
| "maxLength": 65000 | ||
| }, | ||
| "comment_id": { | ||
| "optionalPositiveInteger": true | ||
| }, | ||
| "item_number": { | ||
| "issueOrPRNumber": true | ||
| }, | ||
| "pr": { | ||
| "issueOrPRNumber": true | ||
| }, | ||
| "pr_number": { | ||
| "issueOrPRNumber": true | ||
| }, | ||
| "reply_to_id": { | ||
| "type": "string", | ||
| "maxLength": 256 | ||
| }, | ||
| "repo": { | ||
| "type": "string", | ||
| "maxLength": 256 | ||
| }, | ||
| "target": { | ||
| "type": "string", | ||
| "enum": [ | ||
| "status" | ||
| ] | ||
| }, | ||
| "temporary_id": { | ||
| "type": "string", | ||
| "pattern": "^#?aw_[A-Za-z0-9_]{3,12}$" | ||
| } | ||
| }, | ||
| "dataEnabled": true, | ||
| "dataSchema": { | ||
| "additionalProperties": false, | ||
| "properties": { | ||
| "artifact_kind": { | ||
| "enum": [ | ||
| "analysis", | ||
| "no-binlog" | ||
| ], | ||
| "type": "string" | ||
| }, | ||
| "workflow_artifact": { | ||
| "enum": [ | ||
| "build-failure-analysis" | ||
| ], | ||
| "type": "string" | ||
| } | ||
| }, | ||
| "required": [ | ||
| "artifact_kind", | ||
| "workflow_artifact" | ||
| ], | ||
| "type": "object" | ||
| } | ||
| }, | ||
| "create_pull_request_review_comment": { | ||
| "defaultMax": 1, | ||
| "fields": { | ||
| "body": { | ||
| "required": true, | ||
| "type": "string", | ||
| "sanitize": true, | ||
| "maxLength": 65000 | ||
| }, | ||
| "line": { | ||
| "required": true, | ||
| "positiveInteger": true | ||
| }, | ||
| "path": { | ||
| "required": true, | ||
| "type": "string" | ||
| }, | ||
| "pull_request_number": { | ||
| "optionalPositiveInteger": true | ||
| }, | ||
| "repo": { | ||
| "type": "string", | ||
| "maxLength": 256 | ||
| }, | ||
| "side": { | ||
| "type": "string", | ||
| "enum": [ | ||
| "LEFT", | ||
| "RIGHT" | ||
| ] | ||
| }, | ||
| "start_line": { | ||
| "optionalPositiveInteger": true | ||
| } | ||
| }, | ||
| "customValidation": "startLineLessOrEqualLine", | ||
| "dataEnabled": true, | ||
| "dataSchema": { | ||
| "additionalProperties": false, | ||
| "properties": { | ||
| "artifact_kind": { | ||
| "enum": [ | ||
| "analysis", | ||
| "no-binlog" | ||
| ], | ||
| "type": "string" | ||
| }, | ||
| "workflow_artifact": { | ||
| "enum": [ | ||
| "build-failure-analysis" | ||
| ], | ||
| "type": "string" | ||
| } | ||
| }, | ||
| "required": [ | ||
| "artifact_kind", | ||
| "workflow_artifact" | ||
| ], | ||
| "type": "object" | ||
| } | ||
| }, | ||
| "missing_data": { | ||
| "defaultMax": 20, | ||
| "fields": { | ||
| "alternatives": { | ||
| "type": "string", | ||
| "sanitize": true, | ||
| "maxLength": 256 | ||
| }, | ||
| "context": { | ||
| "type": "string", | ||
| "sanitize": true, | ||
| "maxLength": 256 | ||
| }, | ||
| "data_type": { | ||
| "type": "string", | ||
| "sanitize": true, | ||
| "maxLength": 128 | ||
| }, | ||
| "reason": { | ||
| "type": "string", | ||
| "sanitize": true, | ||
| "maxLength": 256 | ||
| } | ||
| } | ||
| }, | ||
| "missing_tool": { | ||
| "defaultMax": 20, | ||
| "fields": { | ||
| "alternatives": { | ||
| "type": "string", | ||
| "sanitize": true, | ||
| "maxLength": 512 | ||
| }, | ||
| "reason": { | ||
| "required": true, | ||
| "type": "string", | ||
| "sanitize": true, | ||
| "maxLength": 256 | ||
| }, | ||
| "tool": { | ||
| "type": "string", | ||
| "sanitize": true, | ||
| "maxLength": 128 | ||
| } | ||
| } | ||
| }, | ||
| "noop": { | ||
| "defaultMax": 1, | ||
| "fields": { | ||
| "message": { | ||
| "required": true, | ||
| "type": "string", | ||
| "sanitize": true, | ||
| "maxLength": 65000 | ||
| } | ||
| } | ||
| }, | ||
| "report_incomplete": { | ||
| "defaultMax": 5, | ||
| "fields": { | ||
| "details": { | ||
| "type": "string", | ||
| "sanitize": true, | ||
| "maxLength": 65000 | ||
| }, | ||
| "reason": { | ||
| "required": true, | ||
| "type": "string", | ||
| "sanitize": true, | ||
| "maxLength": 1024 | ||
| } | ||
| } | ||
| } | ||
| } | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| with: | ||
| script: | | ||
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require('${{ runner.temp }}/gh-aw/actions/generate_safe_outputs_tools.cjs'); | ||
| await main(); | ||
| - name: Start MCP Gateway | ||
| id: start-mcp-gateway | ||
| env: | ||
| GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST: ${{ vars.GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST || 'true' }} | ||
| GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} | ||
| GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS_CONFIG_PATH }} | ||
| GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS_TOOLS_PATH }} | ||
| GH_AW_SINK_VISIBILITY: ${{ steps.determine-automatic-lockdown.outputs.visibility }} | ||
| GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }} | ||
| GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }} | ||
| GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | ||
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| run: | | ||
| set -eo pipefail | ||
| mkdir -p "${RUNNER_TEMP}/gh-aw/mcp-config" | ||
| # Export gateway environment variables for MCP config and gateway script | ||
| export MCP_GATEWAY_PORT="8080" | ||
| export MCP_GATEWAY_DOMAIN="awmg-mcpg" | ||
| export MCP_GATEWAY_HOST_DOMAIN="localhost" | ||
| MCP_GATEWAY_API_KEY=$(openssl rand -base64 45 | tr -d '/+=') | ||
| echo "::add-mask::${MCP_GATEWAY_API_KEY}" | ||
| export MCP_GATEWAY_API_KEY | ||
| export MCP_GATEWAY_PAYLOAD_DIR="/tmp/gh-aw/mcp-payloads" | ||
| mkdir -p "${MCP_GATEWAY_PAYLOAD_DIR}" | ||
| export MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD="524288" | ||
| export MCP_GATEWAY_ALLOWED_MOUNT_ROOTS="${GITHUB_WORKSPACE}:rw,${RUNNER_TEMP}/gh-aw:ro,${RUNNER_TEMP}/gh-aw/safeoutputs:rw,/opt:ro,/tmp:rw,/tmp/binlogs:ro,/usr/bin/gh:ro" | ||
| export DEBUG="*" | ||
| export GH_AW_ENGINE="copilot" | ||
| MCP_GATEWAY_UID=$(id -u 2>/dev/null || echo '0') | ||
| MCP_GATEWAY_GID=$(id -g 2>/dev/null || echo '0') | ||
| source "${RUNNER_TEMP}/gh-aw/actions/resolve_docker_socket_gid.sh" | ||
| export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network bridge -p 127.0.0.1:'"${MCP_GATEWAY_PORT}"':'"${MCP_GATEWAY_PORT}"' --name awmg-mcpg --add-host host.docker.internal:host-gateway --user '"${MCP_GATEWAY_UID}"':'"${MCP_GATEWAY_GID}"' --group-add '"${DOCKER_SOCK_GID}"' -v '"${DOCKER_SOCK_PATH}"':/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DOCKER_HOST=unix:///var/run/docker.sock -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GH_AW_SINK_VISIBILITY -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e RUNNER_TEMP -e MCP_GATEWAY_ALLOWED_MOUNT_ROOTS -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw -v '"${RUNNER_TEMP}"'/gh-aw/safeoutputs:'"${RUNNER_TEMP}"'/gh-aw/safeoutputs:rw ghcr.io/github/gh-aw-mcpg:v0.4.9' | ||
| mkdir -p "$HOME/.copilot" | ||
| GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) | ||
| cat << GH_AW_MCP_CONFIG_375405f67b9f40e6_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" | ||
| { | ||
| "mcpServers": { | ||
| "binlog-mcp": { | ||
| "type": "stdio", | ||
| "container": "mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-binlog-mcp-amd64", | ||
| "mounts": [ | ||
| "/tmp/binlogs:/data/binlogs:ro" | ||
| ], | ||
| "tools": [ | ||
| "*" | ||
| ], | ||
| "guard-policies": { | ||
| "write-sink": { | ||
| "accept": [ | ||
| "*" | ||
| ], | ||
| "sink-visibility": "${GH_AW_SINK_VISIBILITY}" | ||
| } | ||
| } | ||
| }, | ||
| "github": { | ||
| "type": "stdio", | ||
| "container": "ghcr.io/github/github-mcp-server:v1.9.0", | ||
| "env": { | ||
| "GITHUB_FEATURES": "fields_param", | ||
| "GITHUB_HOST": "${GITHUB_SERVER_URL}", | ||
| "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_MCP_SERVER_TOKEN}", | ||
| "GITHUB_READ_ONLY": "1", | ||
| "GITHUB_TOOLSETS": "pull_requests,repos" | ||
| }, | ||
| "guard-policies": { | ||
| "allow-only": { | ||
| "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY", | ||
| "repos": "$GITHUB_MCP_GUARD_REPOS" | ||
| } | ||
| } | ||
| }, | ||
| "safeoutputs": { | ||
| "type": "stdio", | ||
| "container": "ghcr.io/github/gh-aw-node", | ||
| "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw", "${RUNNER_TEMP}/gh-aw/safeoutputs:${RUNNER_TEMP}/gh-aw/safeoutputs:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], | ||
| "args": ["-w", "\${GITHUB_WORKSPACE}"], | ||
| "entrypoint": "sh", | ||
| "entrypointArgs": ["-c", "sh ${RUNNER_TEMP}/gh-aw/safeoutputs/start_safe_outputs_mcp.sh"], | ||
| "env": { | ||
| "DEBUG": "*", | ||
| "DEFAULT_BRANCH": "\${DEFAULT_BRANCH}", | ||
| "GH_AW_ASSETS_ALLOWED_EXTS": "\${GH_AW_ASSETS_ALLOWED_EXTS}", | ||
| "GH_AW_ASSETS_BRANCH": "\${GH_AW_ASSETS_BRANCH}", | ||
| "GH_AW_ASSETS_MAX_SIZE_KB": "\${GH_AW_ASSETS_MAX_SIZE_KB}", | ||
| "GH_AW_MCP_LOG_DIR": "\${GH_AW_MCP_LOG_DIR}", | ||
| "GH_AW_SAFE_OUTPUTS": "\${GH_AW_SAFE_OUTPUTS}", | ||
| "GH_AW_SAFE_OUTPUTS_CONFIG_PATH": "\${GH_AW_SAFE_OUTPUTS_CONFIG_PATH}", | ||
| "GH_AW_SAFE_OUTPUTS_TOOLS_PATH": "\${GH_AW_SAFE_OUTPUTS_TOOLS_PATH}", | ||
| "GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST": "\${GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST}", | ||
| "GITHUB_REPOSITORY": "\${GITHUB_REPOSITORY}", | ||
| "GITHUB_SHA": "\${GITHUB_SHA}", | ||
| "GITHUB_TOKEN": "\${GITHUB_TOKEN}", | ||
| "GITHUB_WORKSPACE": "\${GITHUB_WORKSPACE}", | ||
| "RUNNER_TEMP": "\${RUNNER_TEMP}" | ||
| }, | ||
| "guard-policies": { | ||
| "write-sink": { | ||
| "accept": [ | ||
| "*" | ||
| ], | ||
| "sink-visibility": "${GH_AW_SINK_VISIBILITY}" | ||
| } | ||
| } | ||
| } | ||
| }, | ||
| "gateway": { | ||
| "port": $MCP_GATEWAY_PORT, | ||
| "domain": "${MCP_GATEWAY_DOMAIN}", | ||
| "apiKey": "${MCP_GATEWAY_API_KEY}", | ||
| "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}", | ||
| "startupTimeout": 120 | ||
| } | ||
| } | ||
| GH_AW_MCP_CONFIG_375405f67b9f40e6_EOF | ||
| - name: Mount MCP servers as CLIs | ||
| id: mount-mcp-clis | ||
| continue-on-error: true | ||
| env: | ||
| MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }} | ||
| MCP_GATEWAY_DOMAIN: ${{ steps.start-mcp-gateway.outputs.gateway-domain }} | ||
| MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }} | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| with: | ||
| script: | | ||
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | ||
| setupGlobals(core, github, context, exec, io); | ||
| const { main } = require('${{ runner.temp }}/gh-aw/actions/mount_mcp_as_cli.cjs'); | ||
| await main(); | ||
| - name: Clean credentials | ||
| continue-on-error: true | ||
| run: bash "${RUNNER_TEMP}/gh-aw/actions/clean_git_credentials.sh" | ||
| - name: Audit pre-agent workspace | ||
| id: pre_agent_audit | ||
| continue-on-error: true | ||
| run: bash "${RUNNER_TEMP}/gh-aw/actions/audit_pre_agent_workspace.sh" | ||
| - name: Execute GitHub Copilot CLI | ||
| id: agentic_execution | ||
| # Copilot CLI tool arguments (sorted): | ||
| # --allow-tool binlog-mcp | ||
| # --allow-tool binlog-mcp(*) | ||
| # --allow-tool github | ||
| # --allow-tool safeoutputs | ||
| # --allow-tool shell(binlog-mcp:*) | ||
| # --allow-tool shell(cat) | ||
| # --allow-tool shell(date) | ||
| # --allow-tool shell(echo) | ||
| # --allow-tool shell(find) | ||
| # --allow-tool shell(github:*) | ||
| # --allow-tool shell(grep) | ||
| # --allow-tool shell(head) | ||
| # --allow-tool shell(ls) | ||
| # --allow-tool shell(printf) | ||
| # --allow-tool shell(pwd) | ||
| # --allow-tool shell(safeoutputs:*) | ||
| # --allow-tool shell(sort) | ||
| # --allow-tool shell(tail) | ||
| # --allow-tool shell(uniq) | ||
| # --allow-tool shell(wc) | ||
| # --allow-tool shell(yq) | ||
| # --allow-tool write | ||
| timeout-minutes: 30 | ||
| run: | | ||
| set -o pipefail | ||
| printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt | ||
| trap 'gh_aw_exit_code=$?; mkdir -p /tmp/gh-aw >/dev/null 2>&1 || true; printf "%s" "$gh_aw_exit_code" > /tmp/gh-aw/agent_execution_exit_code.txt || true; rm -f "$HOME/.copilot/settings.json"' EXIT | ||
| mkdir -p "$HOME/.copilot" | ||
| printf '%s' '{"builtInAgents":{"rubberDuck":false}}' > "$HOME/.copilot/settings.json" | ||
| export XDG_CONFIG_HOME="$HOME" | ||
| export GH_AW_MCP_CONFIG="$HOME/.copilot/mcp-config.json" | ||
| GH_AW_COPILOT_SRC="$(command -v copilot 2>/dev/null || true)" | ||
| if [ -z "$GH_AW_COPILOT_SRC" ] || [ ! -x "$GH_AW_COPILOT_SRC" ]; then | ||
| echo "GitHub Copilot CLI executable not found on PATH after installation" >&2 | ||
| exit 127 | ||
| fi | ||
| GH_AW_COPILOT_BIN="${RUNNER_TEMP}/gh-aw/bin/copilot" | ||
| mkdir -p "${RUNNER_TEMP}/gh-aw/bin" | ||
| if [ "$GH_AW_COPILOT_SRC" != "$GH_AW_COPILOT_BIN" ]; then | ||
| cp "$GH_AW_COPILOT_SRC" "$GH_AW_COPILOT_BIN" | ||
| fi | ||
| chmod 755 "$GH_AW_COPILOT_BIN" | ||
| touch /tmp/gh-aw/agent-step-summary.md | ||
| GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true) | ||
| export GH_AW_NODE_BIN | ||
| export COPILOT_API_KEY="$COPILOT_DUMMY_BYOK" | ||
| (umask 177 && touch /tmp/gh-aw/agent-stdio.log) | ||
| GH_AW_MAX_AI_CREDITS="${GH_AW_MAX_AI_CREDITS:-1000}" | ||
| printf '%s\n' "{\"\$schema\":\"https://github.com/github/gh-aw-firewall/releases/download/v0.27.44/awf-config.schema.json\",\"network\":{\"allowDomains\":[\"*.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\"],\"isolation\":true,\"topologyAttach\":[\"awmg-mcpg\"]},\"apiProxy\":{\"enabled\":true,\"enableTokenSteering\":true,\"maxRuns\":500,\"maxAiCredits\":${GH_AW_MAX_AI_CREDITS},\"maxCacheMisses\":5,\"models\":{\"agent\":[\"sonnet-6x\",\"gpt-5.4\",\"gpt-5.5\",\"gpt-5.6\",\"gpt-5.3\",\"gemini-pro\",\"any\"],\"antigravity\":[\"copilot/antigravity*\",\"google/antigravity*\",\"gemini/antigravity*\"],\"any\":[\"copilot/*\",\"anthropic/*\",\"openai/*\",\"google/*\",\"gemini/*\"],\"auto\":[\"copilot/auto\",\"large\"],\"claude\":[\"agent\"],\"codex\":[\"agent\"],\"coding\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\",\"gpt-5-codex\",\"kimi\"],\"computer-use\":[\"copilot/*computer-use*\",\"google/*computer-use*\",\"gemini/*computer-use*\",\"openai/*computer-use*\"],\"copilot\":[\"agent\"],\"deep-research\":[\"copilot/deep-research*\",\"copilot/o3-deep-research*\",\"copilot/o4-mini-deep-research*\",\"google/deep-research*\",\"gemini/deep-research*\",\"openai/o3-deep-research*\",\"openai/o4-mini-deep-research*\"],\"detection\":[\"small\"],\"evals\":[\"small\"],\"fable\":[\"copilot/*fable*\",\"anthropic/*fable*\"],\"gemini\":[\"agent\"],\"gemini-3-flash\":[\"copilot/gemini-3*flash*\",\"google/gemini-3*flash*\",\"gemini/gemini-3*flash*\"],\"gemini-3-pro\":[\"copilot/gemini-3*pro*\",\"google/gemini-3*pro*\",\"google/nano-banana*\",\"gemini/gemini-3*pro*\"],\"gemini-3.1-flash\":[\"copilot/gemini-3.1*flash*\",\"google/gemini-3.1*flash*\",\"gemini/gemini-3.1*flash*\"],\"gemini-3.1-pro\":[\"copilot/gemini-3.1*pro*\",\"google/gemini-3.1*pro*\",\"gemini/gemini-3.1*pro*\"],\"gemini-3.5-flash\":[\"copilot/gemini-3.5*flash*\",\"google/gemini-3.5*flash*\",\"gemini/gemini-3.5*flash*\"],\"gemini-3.6-flash\":[\"copilot/gemini-3.6*flash*\",\"google/gemini-3.6*flash*\",\"gemini/gemini-3.6*flash*\"],\"gemini-flash\":[\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"],\"gemini-flash-lite\":[\"copilot/gemini-*flash*lite*\",\"google/gemini-*flash*lite*\",\"gemini/gemini-*flash*lite*\"],\"gemini-omni\":[\"copilot/gemini-omni*\",\"google/gemini-omni*\",\"gemini/gemini-omni*\"],\"gemini-pro\":[\"copilot/gemini-*pro*\",\"google/gemini-*pro*\",\"gemini/gemini-*pro*\"],\"gemma\":[\"copilot/gemma*\",\"google/gemma*\",\"gemini/gemma*\"],\"gpt-5\":[\"copilot/gpt-5*\",\"openai/gpt-5*\"],\"gpt-5-codex\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\"],\"gpt-5-mini\":[\"copilot/gpt-5*mini*\",\"openai/gpt-5*mini*\"],\"gpt-5-nano\":[\"copilot/gpt-5*nano*\",\"openai/gpt-5*nano*\"],\"gpt-5-pro\":[\"copilot/gpt-5*pro*\",\"openai/gpt-5*pro*\"],\"gpt-5.1\":[\"copilot/gpt-5.1*\",\"openai/gpt-5.1*\"],\"gpt-5.2\":[\"copilot/gpt-5.2*\",\"openai/gpt-5.2*\"],\"gpt-5.3\":[\"copilot/gpt-5.3*\",\"openai/gpt-5.3*\"],\"gpt-5.4\":[\"copilot/gpt-5.4*\",\"openai/gpt-5.4*\"],\"gpt-5.5\":[\"copilot/gpt-5.5*\",\"openai/gpt-5.5*\"],\"gpt-5.6\":[\"copilot/gpt-5.6*\",\"openai/gpt-5.6*\"],\"grok\":[\"copilot/*grok*\",\"openai/*grok*\"],\"haiku\":[\"copilot/*haiku*\",\"anthropic/*haiku*\"],\"image-generation\":[\"copilot/gpt-image*\",\"openai/gpt-image*\",\"openai/chatgpt-image*\",\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"google/imagen*\"],\"kimi\":[\"copilot/kimi*\",\"openai/kimi*\"],\"kiwi\":[\"copilot/kiwi*\",\"openai/kiwi*\"],\"large\":[\"sonnet\",\"gpt-5-pro\",\"gpt-5\",\"gemini-pro\"],\"lyria\":[\"google/lyria*\",\"gemini/lyria*\",\"copilot/lyria*\"],\"mai-code\":[\"copilot/MAI-Code*\",\"copilot/mai-code*\",\"openai/MAI-Code*\"],\"mai-code-1-flash-picker\":[\"copilot/MAI-Code-1-Flash-picker*\",\"copilot/mai-code-1-flash-picker*\",\"openai/MAI-Code-1-Flash-picker*\"],\"mini\":[\"haiku\",\"gpt-5-mini\",\"gpt-5-nano\",\"gemini-flash-lite\"],\"nano-banana\":[\"copilot/nano-banana*\",\"google/nano-banana*\",\"gemini/nano-banana*\"],\"opus\":[\"copilot/*opus*\",\"anthropic/*opus*\"],\"opusplan\":[\"opus?effort=high\"],\"raptor-mini\":[\"copilot/raptor*\",\"openai/raptor*\"],\"reasoning\":[\"copilot/o1*\",\"copilot/o3*\",\"copilot/o4*\",\"openai/o1*\",\"openai/o3*\",\"openai/o4*\"],\"robotics\":[\"copilot/*robotics*\",\"google/*robotics*\",\"gemini/*robotics*\"],\"small\":[\"mini\"],\"small-agent\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash\"],\"sonnet\":[\"copilot/*sonnet*\",\"anthropic/*sonnet*\"],\"sonnet-6x\":[\"copilot/*sonnet-4.5*\",\"copilot/*sonnet-4.6*\",\"copilot/*sonnet-5*\",\"copilot/*sonnet-4-5-*\",\"anthropic/*sonnet-4-5-*\",\"copilot/*sonnet-4-6*\",\"anthropic/*sonnet-4-6*\",\"anthropic/*sonnet-5*\"],\"summarization\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash-lite\",\"mini\"],\"veo\":[\"google/veo*\",\"gemini/veo*\"],\"vision\":[\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"]}},\"container\":{\"imageTag\":\"0.27.44,squid=sha256:83e48bbe12c634be8c228a576832fe45f66c529ac3659db92bddbcf2eeb6d627,agent=sha256:0d727725c737b58c7bdf51f640cffb928385ec46517e0917c7f1a02f1bada8b4,api-proxy=sha256:b50fbadba138f6e9aba94aca09711335c489bb3b15861220cb66f6092e042dc7,cli-proxy=sha256:c064d15974f7c933ec7d3f7b4038f4fd203547b3154bdc821afd379144887eff\"},\"logging\":{\"proxyLogsDir\":\"/tmp/gh-aw/sandbox/firewall/logs\",\"auditDir\":\"/tmp/gh-aw/sandbox/firewall/audit\"}}" > "${RUNNER_TEMP}/gh-aw/awf-config.json" | ||
| cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json | ||
| export GH_AW_MODELS_JSON_PATH="/tmp/gh-aw/models.json" | ||
| GH_AW_DOCKER_HOST="" | ||
| if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then | ||
| GH_AW_DOCKER_HOST="${DOCKER_HOST}" | ||
| fi | ||
| if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then | ||
| GH_AW_CHROOT_BINARIES_SOURCE_PATH="${RUNNER_TEMP}/gh-aw" GH_AW_CHROOT_IDENTITY_HOME="${RUNNER_TEMP}/gh-aw/home" node "${RUNNER_TEMP}/gh-aw/actions/patch_awf_chroot_config.cjs" | ||
| fi | ||
| GH_AW_TOOL_CACHE_MOUNT="" | ||
| GH_AW_TOOL_CACHE="${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}" | ||
| if [ -d "$GH_AW_TOOL_CACHE" ]; then | ||
| if [[ "$GH_AW_TOOL_CACHE" != /opt/* ]]; then | ||
| GH_AW_TOOL_CACHE_MOUNT="$GH_AW_TOOL_CACHE:$GH_AW_TOOL_CACHE:ro" | ||
| fi | ||
| fi | ||
| # shellcheck disable=SC1003,SC2016,SC2086 | ||
| awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_TOOL_CACHE_MOUNT:+--mount "$GH_AW_TOOL_CACHE_MOUNT"} ${GH_AW_DOCKER_HOST:+--docker-host "$GH_AW_DOCKER_HOST"} --env-all --exclude-env ACTIONS_ID_TOKEN_REQUEST_TOKEN --exclude-env ACTIONS_ID_TOKEN_REQUEST_URL --exclude-env COPILOT_GITHUB_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --mount /tmp/gh-aw:/tmp/gh-aw:rw --log-level info --skip-pull \ | ||
| -- /bin/bash -c 'set +o histexpand; export PATH="${RUNNER_TEMP}/gh-aw/mcp-cli/bin:$PATH" && : "${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}"; GH_AW_TOOL_CACHE="$RUNNER_TOOL_CACHE"; export PATH="$(find "$GH_AW_TOOL_CACHE" -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true; [ -n "$ERLANG_HOME" ] && export PATH="$ERLANG_HOME/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; GH_AW_NPM_GLOBAL_ROOT="$(npm root -g 2>/dev/null || true)"; if [ -n "$GH_AW_NPM_GLOBAL_ROOT" ]; then export NODE_PATH="${GH_AW_NPM_GLOBAL_ROOT}${NODE_PATH:+:${NODE_PATH}}"; fi; "$GH_AW_NODE_EXEC" "${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs" "${RUNNER_TEMP}/gh-aw/bin/copilot" --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-tool binlog-mcp --allow-tool '\''binlog-mcp(*)'\'' --allow-tool github --allow-tool safeoutputs --allow-tool '\''shell(binlog-mcp:*)'\'' --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(date)'\'' --allow-tool '\''shell(echo)'\'' --allow-tool '\''shell(find)'\'' --allow-tool '\''shell(github:*)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(printf)'\'' --allow-tool '\''shell(pwd)'\'' --allow-tool '\''shell(safeoutputs:*)'\'' --allow-tool '\''shell(sort)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(uniq)'\'' --allow-tool '\''shell(wc)'\'' --allow-tool '\''shell(yq)'\'' --allow-tool write --allow-all-paths --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log | ||
| env: | ||
| AWF_REFLECT_ENABLED: 1 | ||
| COPILOT_AGENT_RUNNER_TYPE: STANDALONE | ||
| COPILOT_DUMMY_BYOK: dummy-byok-key-for-offline-mode | ||
| COPILOT_GITHUB_TOKEN: ${{ case(needs.pat_pool.outputs.pat_number == '0', secrets.COPILOT_PAT_0, needs.pat_pool.outputs.pat_number == '1', secrets.COPILOT_PAT_1, needs.pat_pool.outputs.pat_number == '2', secrets.COPILOT_PAT_2, needs.pat_pool.outputs.pat_number == '3', secrets.COPILOT_PAT_3, needs.pat_pool.outputs.pat_number == '4', secrets.COPILOT_PAT_4, needs.pat_pool.outputs.pat_number == '5', secrets.COPILOT_PAT_5, needs.pat_pool.outputs.pat_number == '6', secrets.COPILOT_PAT_6, needs.pat_pool.outputs.pat_number == '7', secrets.COPILOT_PAT_7, needs.pat_pool.outputs.pat_number == '8', secrets.COPILOT_PAT_8, needs.pat_pool.outputs.pat_number == '9', secrets.COPILOT_PAT_9, 'NO COPILOT PAT AVAILABLE') }} | ||
| COPILOT_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || vars.GH_AW_DEFAULT_MODEL_COPILOT || 'auto' }} | ||
| GH_AW_LLM_PROVIDER: github | ||
| GH_AW_MAX_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_AI_CREDITS || '1000' }} | ||
| GH_AW_MAX_TURNS: ${{ vars.GH_AW_DEFAULT_MAX_TURNS || '' }} | ||
| GH_AW_PHASE: agent | ||
| GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt | ||
| GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} | ||
| GH_AW_TIMEOUT_MINUTES: 30 | ||
| GH_AW_VERSION: v0.86.2 | ||
| GITHUB_API_URL: ${{ github.api_url }} | ||
| GITHUB_AW: true | ||
| GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows | ||
| GITHUB_HEAD_REF: ${{ github.head_ref }} | ||
| GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | ||
| GITHUB_REF_NAME: ${{ github.ref_name }} | ||
| GITHUB_SERVER_URL: ${{ github.server_url }} | ||
| GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md | ||
| GITHUB_WORKSPACE: ${{ github.workspace }} | ||
| GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com | ||
| GIT_AUTHOR_NAME: github-actions[bot] | ||
| GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com | ||
| GIT_COMMITTER_NAME: github-actions[bot] | ||
| RUNNER_TEMP: ${{ runner.temp }} | ||
| TRACEPARENT: ${{ env.GITHUB_AW_OTEL_TRACE_ID != '' && env.GITHUB_AW_OTEL_PARENT_SPAN_ID != '' && format('00-{0}-{1}-01', env.GITHUB_AW_OTEL_TRACE_ID, env.GITHUB_AW_OTEL_PARENT_SPAN_ID) || '' }} | ||
| - name: Detect agent errors | ||
| if: always() | ||
| id: detect-agent-errors | ||
| continue-on-error: true | ||
| run: node "${RUNNER_TEMP}/gh-aw/actions/detect_agent_errors.cjs" | ||
| - name: Configure Git credentials | ||
| env: | ||
| GITHUB_REPOSITORY: ${{ github.repository }} | ||
| GITHUB_SERVER_URL: ${{ github.server_url }} | ||
| GITHUB_TOKEN: ${{ github.token }} | ||
| run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_git_credentials.sh" | ||
| - name: Copy Copilot session state files to logs | ||
| if: always() | ||
| continue-on-error: true | ||
| run: bash "${RUNNER_TEMP}/gh-aw/actions/copy_copilot_session_state.sh" | ||
| - name: Stop MCP Gateway | ||
| if: always() | ||
| continue-on-error: true | ||
| env: | ||
| MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }} | ||
| MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }} | ||
| GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }} | ||
| run: | | ||
| bash "${RUNNER_TEMP}/gh-aw/actions/stop_mcp_gateway.sh" "$GATEWAY_PID" | ||
| - name: Redact secrets in logs | ||
| if: always() | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| with: | ||
| script: | | ||
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require('${{ runner.temp }}/gh-aw/actions/redact_secrets.cjs'); | ||
| await main(); | ||
| env: | ||
| GH_AW_SECRET_NAMES: 'COPILOT_PAT_0,COPILOT_PAT_1,COPILOT_PAT_2,COPILOT_PAT_3,COPILOT_PAT_4,COPILOT_PAT_5,COPILOT_PAT_6,COPILOT_PAT_7,COPILOT_PAT_8,COPILOT_PAT_9,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN' | ||
| SECRET_COPILOT_PAT_0: ${{ secrets.COPILOT_PAT_0 }} | ||
| SECRET_COPILOT_PAT_1: ${{ secrets.COPILOT_PAT_1 }} | ||
| SECRET_COPILOT_PAT_2: ${{ secrets.COPILOT_PAT_2 }} | ||
| SECRET_COPILOT_PAT_3: ${{ secrets.COPILOT_PAT_3 }} | ||
| SECRET_COPILOT_PAT_4: ${{ secrets.COPILOT_PAT_4 }} | ||
| SECRET_COPILOT_PAT_5: ${{ secrets.COPILOT_PAT_5 }} | ||
| SECRET_COPILOT_PAT_6: ${{ secrets.COPILOT_PAT_6 }} | ||
| SECRET_COPILOT_PAT_7: ${{ secrets.COPILOT_PAT_7 }} | ||
| SECRET_COPILOT_PAT_8: ${{ secrets.COPILOT_PAT_8 }} | ||
| SECRET_COPILOT_PAT_9: ${{ secrets.COPILOT_PAT_9 }} | ||
| SECRET_GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} | ||
| SECRET_GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} | ||
| SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| - name: Append agent step summary | ||
| if: always() | ||
| run: bash "${RUNNER_TEMP}/gh-aw/actions/append_agent_step_summary.sh" | ||
| - name: Copy Safe Outputs | ||
| if: always() | ||
| env: | ||
| GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} | ||
| run: | | ||
| mkdir -p /tmp/gh-aw | ||
| cp "$GH_AW_SAFE_OUTPUTS" /tmp/gh-aw/safeoutputs.jsonl 2>/dev/null || true | ||
| - name: Ingest agent output | ||
| id: collect_output | ||
| if: always() | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| env: | ||
| GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} | ||
| 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 }} | ||
| with: | ||
| script: | | ||
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require('${{ runner.temp }}/gh-aw/actions/collect_ndjson_output.cjs'); | ||
| await main(); | ||
| - name: Parse agent logs for step summary | ||
| if: always() | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| env: | ||
| GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/ | ||
| GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} | ||
| with: | ||
| script: | | ||
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_copilot_log.cjs'); | ||
| await main(); | ||
| - name: Parse MCP Gateway logs for step summary | ||
| if: always() | ||
| id: parse-mcp-gateway | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| with: | ||
| script: | | ||
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_mcp_gateway_log.cjs'); | ||
| await main(); | ||
| - name: Print firewall logs | ||
| if: always() | ||
| continue-on-error: true | ||
| env: | ||
| AWF_LOGS_DIR: /tmp/gh-aw/sandbox/firewall/logs | ||
| run: bash "${RUNNER_TEMP}/gh-aw/actions/print_firewall_logs.sh" --rootless | ||
| - name: Parse token usage for step summary | ||
| if: always() | ||
| continue-on-error: true | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| with: | ||
| script: | | ||
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_token_usage.cjs'); | ||
| await main(); | ||
| - name: Print AWF reflect summary | ||
| if: always() | ||
| continue-on-error: true | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| with: | ||
| script: | | ||
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require('${{ runner.temp }}/gh-aw/actions/awf_reflect_summary.cjs'); | ||
| await main(); | ||
| - name: Write agent output placeholder if missing | ||
| if: always() | ||
| run: | | ||
| if [ ! -f /tmp/gh-aw/agent_output.json ]; then | ||
| echo '{"items":[]}' > /tmp/gh-aw/agent_output.json | ||
| fi | ||
| - name: Upload agent artifacts | ||
| if: always() | ||
| continue-on-error: true | ||
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | ||
| with: | ||
| name: agent | ||
| path: | | ||
| /tmp/gh-aw/aw-prompts/prompt.txt | ||
| /tmp/gh-aw/sandbox/agent/logs/ | ||
| /tmp/gh-aw/redacted-urls.log | ||
| /tmp/gh-aw/mcp-logs/ | ||
| /tmp/gh-aw/agent_usage.json | ||
| /tmp/gh-aw/agent-stdio.log | ||
| /tmp/gh-aw/pre-agent-audit.txt | ||
| /tmp/gh-aw/agent/ | ||
| /tmp/gh-aw/github_rate_limits.jsonl | ||
| /tmp/gh-aw/safeoutputs.jsonl | ||
| /tmp/gh-aw/agent_output.json | ||
| /tmp/gh-aw/aw-*.patch | ||
| /tmp/gh-aw/aw-*.bundle | ||
| /tmp/gh-aw/awf-config.json | ||
| /tmp/gh-aw/sandbox/firewall/logs/ | ||
| /tmp/gh-aw/sandbox/firewall/audit/ | ||
| /tmp/gh-aw/sandbox/firewall/awf-reflect.json | ||
| if-no-files-found: ignore | ||
| conclusion: | ||
| needs: | ||
| - activation | ||
| - agent | ||
| - detection | ||
| - fetch-binlog | ||
| - pat_pool | ||
| - safe_outputs | ||
| if: > | ||
| always() && (needs.agent.result != 'skipped' || needs.activation.outputs.lockdown_check_failed == 'true' || | ||
| needs.activation.outputs.oauth_token_check_failed == 'true' || needs.activation.outputs.stale_lock_file_failed == 'true' || | ||
| needs.activation.outputs.daily_ai_credits_exceeded == 'true') | ||
| runs-on: ubuntu-slim | ||
| environment: copilot-pat-pool | ||
| permissions: | ||
| actions: read | ||
| issues: write | ||
| pull-requests: write | ||
| concurrency: | ||
| group: "gh-aw-conclusion-build-failure-analysis" | ||
| cancel-in-progress: false | ||
| queue: max | ||
| env: | ||
| GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} | ||
| outputs: | ||
| incomplete_count: ${{ steps.report_incomplete.outputs.incomplete_count }} | ||
| noop_message: ${{ steps.noop.outputs.noop_message }} | ||
| tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} | ||
| total_count: ${{ steps.missing_tool.outputs.total_count }} | ||
| steps: | ||
| - name: Setup Scripts | ||
| id: setup | ||
| uses: github/gh-aw-actions/setup@6aab9e5b5c91c615506061f09bedd81a23babe3c # v0.86.2 | ||
| with: | ||
| destination: ${{ runner.temp }}/gh-aw/actions | ||
| job-name: ${{ github.job }} | ||
| trace-id: ${{ needs.activation.outputs.setup-trace-id }} | ||
| parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} | ||
| env: | ||
| GH_AW_SETUP_WORKFLOW_NAME: "Build Failure Analysis" | ||
| GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/build-failure-analysis.lock.yml@${{ github.ref }} | ||
| GH_AW_INFO_VERSION: "1.0.79" | ||
| GH_AW_INFO_AWF_VERSION: "v0.27.44" | ||
| GH_AW_INFO_ENGINE_ID: "copilot" | ||
| - name: Download agent output artifact | ||
| id: download-agent-output | ||
| continue-on-error: true | ||
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | ||
| with: | ||
| name: agent | ||
| path: /tmp/gh-aw/ | ||
| - name: Setup agent output environment variable | ||
| id: setup-agent-output-env | ||
| if: steps.download-agent-output.outcome == 'success' | ||
| run: | | ||
| mkdir -p /tmp/gh-aw/ | ||
| find "/tmp/gh-aw/" -type f -print | ||
| echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT" | ||
| - name: Download Safe Outputs Items Manifest | ||
| id: download-safe-outputs-manifest | ||
| if: always() | ||
| continue-on-error: true | ||
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | ||
| with: | ||
| name: safe-outputs-items | ||
| path: /tmp/gh-aw/ | ||
| - name: Collect usage artifact files | ||
| if: always() | ||
| continue-on-error: true | ||
| run: bash "${RUNNER_TEMP}/gh-aw/actions/collect_usage_artifact_files.sh" | ||
| - name: Upload usage artifact | ||
| if: always() | ||
| continue-on-error: true | ||
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | ||
| with: | ||
| name: usage | ||
| path: | | ||
| /tmp/gh-aw/usage/aw_info.json | ||
| /tmp/gh-aw/usage/aw-info.jsonl | ||
| /tmp/gh-aw/usage/agent_usage.json | ||
| /tmp/gh-aw/usage/agent_usage.jsonl | ||
| /tmp/gh-aw/usage/detection_usage.jsonl | ||
| /tmp/gh-aw/usage/evals.jsonl | ||
| /tmp/gh-aw/usage/github_rate_limits.jsonl | ||
| /tmp/gh-aw/usage/agent/token_usage.jsonl | ||
| /tmp/gh-aw/usage/detection/token_usage.jsonl | ||
| /tmp/gh-aw/usage/activity/summary.json | ||
| if-no-files-found: ignore | ||
| - name: Restore daily AIC usage cache | ||
| id: restore-daily-aic-cache-conclusion | ||
| if: always() | ||
| continue-on-error: true | ||
| uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | ||
| with: | ||
| key: agentic-workflow-usage-buildfailureanalysis-${{ github.run_id }} | ||
| restore-keys: agentic-workflow-usage-buildfailureanalysis- | ||
| path: /tmp/gh-aw/agentic-workflow-usage-cache.jsonl | ||
| - name: Write daily AIC usage cache entry | ||
| id: write-daily-aic-cache | ||
| if: always() | ||
| continue-on-error: true | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| with: | ||
| github-token: ${{ github.token }} | ||
| script: | | ||
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | ||
| setupGlobals(core, github, context); | ||
| const { main } = require('${{ runner.temp }}/gh-aw/actions/write_daily_aic_usage_cache.cjs'); | ||
| await main(); | ||
| - name: Save daily AIC usage cache | ||
| id: save-daily-aic-cache | ||
| if: always() | ||
| continue-on-error: true | ||
| uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | ||
| with: | ||
| key: agentic-workflow-usage-buildfailureanalysis-${{ github.run_id }} | ||
| path: /tmp/gh-aw/agentic-workflow-usage-cache.jsonl | ||
| - name: Upload daily AIC usage cache artifact | ||
| id: upload-daily-aic-cache | ||
| if: always() | ||
| continue-on-error: true | ||
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | ||
| with: | ||
| name: aic-usage-cache | ||
| path: /tmp/gh-aw/agentic-workflow-usage-cache.jsonl | ||
| if-no-files-found: ignore | ||
| retention-days: 7 | ||
| - name: Process no-op messages | ||
| id: noop | ||
| 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: "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 }} | ||
| GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} | ||
| GH_AW_NOOP_REPORT_AS_ISSUE: "false" | ||
| GH_AW_AIC: ${{ needs.agent.outputs.aic }} | ||
| GH_AW_THREAT_DETECTION_AIC: ${{ needs.detection.outputs.aic }} | ||
| GH_AW_AMBIENT_CONTEXT: ${{ needs.agent.outputs.ambient_context }} | ||
| GH_AW_WORKFLOW_ID: "build-failure-analysis" | ||
| with: | ||
| github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | ||
| script: | | ||
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_noop_message.cjs'); | ||
| await main(); | ||
| - name: Log detection run | ||
| id: detection_runs | ||
| 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_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 }} | ||
| GH_AW_DETECTION_CONCLUSION: ${{ needs.detection.outputs.detection_conclusion }} | ||
| GH_AW_DETECTION_REASON: ${{ needs.detection.outputs.detection_reason }} | ||
| with: | ||
| github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | ||
| script: | | ||
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_detection_runs.cjs'); | ||
| await main(); | ||
| - name: Record missing tool | ||
| id: missing_tool | ||
| 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_MISSING_TOOL_CREATE_ISSUE: "true" | ||
| 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" | ||
| with: | ||
| github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | ||
| script: | | ||
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require('${{ runner.temp }}/gh-aw/actions/missing_tool.cjs'); | ||
| await main(); | ||
| - name: Record incomplete | ||
| id: report_incomplete | ||
| 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_REPORT_INCOMPLETE_CREATE_ISSUE: "true" | ||
| 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" | ||
| with: | ||
| github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | ||
| script: | | ||
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require('${{ runner.temp }}/gh-aw/actions/report_incomplete_handler.cjs'); | ||
| await main(); | ||
| - name: Handle agent failure | ||
| id: handle_agent_failure | ||
| if: always() | ||
| 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_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 }} | ||
| GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} | ||
| GH_AW_WORKFLOW_ID: "build-failure-analysis" | ||
| GH_AW_ACTION_FAILURE_ISSUE_EXPIRES_HOURS: "168" | ||
| GH_AW_ENGINE_ID: "copilot" | ||
| GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }} | ||
| GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens || '' }} | ||
| GH_AW_AI_CREDITS_RATE_LIMIT_ERROR: ${{ needs.agent.outputs.ai_credits_rate_limit_error || 'false' }} | ||
| GH_AW_UNKNOWN_MODEL_AI_CREDITS: ${{ needs.agent.outputs.unknown_model_ai_credits || 'false' }} | ||
| GH_AW_AIC: ${{ needs.agent.outputs.aic }} | ||
| GH_AW_THREAT_DETECTION_AIC: ${{ needs.detection.outputs.aic }} | ||
| GH_AW_MAX_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_AI_CREDITS || '1000' }} | ||
| GH_AW_INFERENCE_ACCESS_ERROR: ${{ needs.agent.outputs.inference_access_error }} | ||
| GH_AW_MCP_POLICY_ERROR: ${{ needs.agent.outputs.mcp_policy_error }} | ||
| GH_AW_AGENTIC_ENGINE_TIMEOUT: ${{ needs.agent.outputs.agentic_engine_timeout }} | ||
| GH_AW_MODEL_NOT_SUPPORTED_ERROR: ${{ needs.agent.outputs.model_not_supported_error }} | ||
| GH_AW_HTTP_400_RESPONSE_ERROR: ${{ needs.agent.outputs.http_400_response_error }} | ||
| GH_AW_MAX_CACHE_MISSES_EXCEEDED: ${{ needs.agent.outputs.max_cache_misses_exceeded }} | ||
| GH_AW_MISSING_MODEL_PRICING_ERROR: ${{ needs.agent.outputs.missing_model_pricing_error }} | ||
| GH_AW_MISSING_MODEL_PRICING_MODEL_NAME: ${{ needs.agent.outputs.missing_model_pricing_model_name }} | ||
| GH_AW_ENGINE_API_HOSTS: "api.enterprise.githubcopilot.com,api.githubcopilot.com,api.business.githubcopilot.com,api.individual.githubcopilot.com" | ||
| GH_AW_LOCKDOWN_CHECK_FAILED: ${{ needs.activation.outputs.lockdown_check_failed }} | ||
| GH_AW_OAUTH_TOKEN_CHECK_FAILED: ${{ needs.activation.outputs.oauth_token_check_failed }} | ||
| GH_AW_STALE_LOCK_FILE_FAILED: ${{ needs.activation.outputs.stale_lock_file_failed }} | ||
| GH_AW_DAILY_AI_CREDITS_EXCEEDED: ${{ needs.activation.outputs.daily_ai_credits_exceeded }} | ||
| GH_AW_DAILY_AI_CREDITS_TOTAL_EFFECTIVE_TOKENS: ${{ needs.activation.outputs.daily_ai_credits_total_effective_tokens }} | ||
| GH_AW_DAILY_AI_CREDITS_THRESHOLD: ${{ needs.activation.outputs.daily_ai_credits_threshold }} | ||
| GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 🤖 **Automated content by GitHub Copilot.** Generated by the [{workflow_name}]({agentic_workflow_url}) workflow.{ai_credits_suffix} · [◷]({history_link})\"}" | ||
| GH_AW_GROUP_REPORTS: "false" | ||
| GH_AW_FAILURE_REPORT_AS_ISSUE: "false" | ||
| GH_AW_MISSING_TOOL_REPORT_AS_FAILURE: "true" | ||
| GH_AW_MISSING_DATA_REPORT_AS_FAILURE: "true" | ||
| GH_AW_TIMEOUT_MINUTES: "30" | ||
| with: | ||
| github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | ||
| script: | | ||
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); | ||
| await main(); | ||
| - name: Report failed jobs | ||
| id: report_failed_jobs | ||
| if: always() | ||
| 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_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 }} | ||
| GH_AW_REPORT_FAILED_JOBS: "true" | ||
| with: | ||
| github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | ||
| script: | | ||
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require('${{ runner.temp }}/gh-aw/actions/report_failed_jobs.cjs'); | ||
| await main(); | ||
| detection: | ||
| needs: | ||
| - activation | ||
| - agent | ||
| - pat_pool | ||
| if: always() && needs.agent.result != 'skipped' | ||
| runs-on: ubuntu-latest | ||
| environment: copilot-pat-pool | ||
| permissions: | ||
| contents: read | ||
| env: | ||
| GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} | ||
| outputs: | ||
| aic: ${{ steps.parse_detection_token_usage.outputs.aic }} | ||
| detection_conclusion: ${{ steps.detection_conclusion.outputs.conclusion }} | ||
| detection_reason: ${{ steps.detection_conclusion.outputs.reason }} | ||
| detection_success: ${{ steps.detection_conclusion.outputs.success }} | ||
| steps: | ||
| - name: Setup Scripts | ||
| id: setup | ||
| uses: github/gh-aw-actions/setup@6aab9e5b5c91c615506061f09bedd81a23babe3c # v0.86.2 | ||
| with: | ||
| destination: ${{ runner.temp }}/gh-aw/actions | ||
| job-name: ${{ github.job }} | ||
| trace-id: ${{ needs.activation.outputs.setup-trace-id }} | ||
| parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} | ||
| env: | ||
| GH_AW_SETUP_WORKFLOW_NAME: "Build Failure Analysis" | ||
| GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/build-failure-analysis.lock.yml@${{ github.ref }} | ||
| GH_AW_INFO_VERSION: "1.0.79" | ||
| GH_AW_INFO_AWF_VERSION: "v0.27.44" | ||
| GH_AW_INFO_ENGINE_ID: "copilot" | ||
| - name: Download agent output artifact | ||
| id: download-agent-output | ||
| continue-on-error: true | ||
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | ||
| with: | ||
| name: agent | ||
| path: /tmp/gh-aw/ | ||
| - name: Setup agent output environment variable | ||
| id: setup-agent-output-env | ||
| if: steps.download-agent-output.outcome == 'success' | ||
| run: | | ||
| mkdir -p /tmp/gh-aw/ | ||
| find "/tmp/gh-aw/" -type f -print | ||
| echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT" | ||
| - name: Checkout repository for patch context | ||
| if: needs.agent.outputs.has_patch == 'true' | ||
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | ||
| with: | ||
| persist-credentials: false | ||
| # --- Threat Detection --- | ||
| - name: Clean stale firewall files from agent artifact | ||
| run: | | ||
| rm -rf /tmp/gh-aw/sandbox/firewall/logs | ||
| rm -rf /tmp/gh-aw/sandbox/firewall/audit | ||
| - name: Download container images | ||
| run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.27.44@sha256:0d727725c737b58c7bdf51f640cffb928385ec46517e0917c7f1a02f1bada8b4 ghcr.io/github/gh-aw-firewall/api-proxy:0.27.44@sha256:b50fbadba138f6e9aba94aca09711335c489bb3b15861220cb66f6092e042dc7 ghcr.io/github/gh-aw-firewall/squid:0.27.44@sha256:83e48bbe12c634be8c228a576832fe45f66c529ac3659db92bddbcf2eeb6d627 | ||
| - name: Check if detection needed | ||
| id: detection_guard | ||
| if: always() | ||
| env: | ||
| OUTPUT_TYPES: ${{ needs.agent.outputs.output_types }} | ||
| HAS_PATCH: ${{ needs.agent.outputs.has_patch }} | ||
| run: | | ||
| if [[ -n "$OUTPUT_TYPES" || "$HAS_PATCH" == "true" ]]; then | ||
| echo "run_detection=true" >> "$GITHUB_OUTPUT" | ||
| echo "Detection will run: output_types=$OUTPUT_TYPES, has_patch=$HAS_PATCH" | ||
| else | ||
| echo "run_detection=false" >> "$GITHUB_OUTPUT" | ||
| echo "Detection skipped: no agent outputs or patches to analyze" | ||
| fi | ||
| - name: Clear MCP Config for detection | ||
| if: always() && steps.detection_guard.outputs.run_detection == 'true' | ||
| run: | | ||
| rm -f "${RUNNER_TEMP}/gh-aw/mcp-config/mcp-servers.json" | ||
| rm -f "$HOME/.copilot/mcp-config.json" | ||
| rm -f "$GITHUB_WORKSPACE/.gemini/settings.json" | ||
| - name: Prepare threat detection files | ||
| if: always() && steps.detection_guard.outputs.run_detection == 'true' | ||
| run: | | ||
| bash "${RUNNER_TEMP}/gh-aw/actions/prepare_threat_detection_files.sh" | ||
| - name: Setup threat detection | ||
| if: always() && steps.detection_guard.outputs.run_detection == 'true' | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| env: | ||
| WORKFLOW_NAME: "Build Failure Analysis" | ||
| WORKFLOW_DESCRIPTION: "When the Azure Pipelines PR build (`runtime`) fails, downloads the binary logs from its failed or canceled jobs — it does NOT rebuild — and delegates to the `build-failure-analyst` agent, which queries the binlogs live via the containerized `binlog-mcp` MCP server to identify root causes, post a PR comment summarizing them, and attach inline `suggestion` blocks tied to the diff." | ||
| HAS_PATCH: ${{ needs.agent.outputs.has_patch }} | ||
| GH_AW_DETECTION_CONTINUE_ON_ERROR: "true" | ||
| with: | ||
| script: | | ||
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require('${{ runner.temp }}/gh-aw/actions/setup_threat_detection.cjs'); | ||
| await main(); | ||
| - name: Ensure threat-detection directory and log | ||
| if: always() && steps.detection_guard.outputs.run_detection == 'true' | ||
| run: | | ||
| mkdir -p /tmp/gh-aw/threat-detection | ||
| touch /tmp/gh-aw/threat-detection/detection.log | ||
| rm -f /tmp/gh-aw/step-summary.md | ||
| touch /tmp/gh-aw/step-summary.md | ||
| - name: Setup Node.js | ||
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 | ||
| with: | ||
| node-version: '24' | ||
| package-manager-cache: false | ||
| - name: Install ripgrep | ||
| run: bash "${RUNNER_TEMP}/gh-aw/actions/install_ripgrep.sh" | ||
| - name: Install GitHub Copilot CLI | ||
| run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" | ||
| env: | ||
| GH_HOST: github.com | ||
| GH_AW_COMPILED_VERSION: v0.86.2 | ||
| - name: Install AWF binary | ||
| run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.27.44 | ||
| - name: Execute GitHub Copilot CLI | ||
| if: always() && steps.detection_guard.outputs.run_detection == 'true' | ||
| continue-on-error: true | ||
| id: detection_agentic_execution | ||
| # Copilot CLI tool arguments (sorted): | ||
| timeout-minutes: 20 | ||
| run: | | ||
| set -o pipefail | ||
| printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt | ||
| trap 'gh_aw_exit_code=$?; mkdir -p /tmp/gh-aw >/dev/null 2>&1 || true; printf "%s" "$gh_aw_exit_code" > /tmp/gh-aw/agent_execution_exit_code.txt || true; rm -f "$HOME/.copilot/settings.json"' EXIT | ||
| mkdir -p "$HOME/.copilot" | ||
| printf '%s' '{"builtInAgents":{"rubberDuck":false}}' > "$HOME/.copilot/settings.json" | ||
| export XDG_CONFIG_HOME="$HOME" | ||
| GH_AW_COPILOT_SRC="$(command -v copilot 2>/dev/null || true)" | ||
| if [ -z "$GH_AW_COPILOT_SRC" ] || [ ! -x "$GH_AW_COPILOT_SRC" ]; then | ||
| echo "GitHub Copilot CLI executable not found on PATH after installation" >&2 | ||
| exit 127 | ||
| fi | ||
| GH_AW_COPILOT_BIN="${RUNNER_TEMP}/gh-aw/bin/copilot" | ||
| mkdir -p "${RUNNER_TEMP}/gh-aw/bin" | ||
| if [ "$GH_AW_COPILOT_SRC" != "$GH_AW_COPILOT_BIN" ]; then | ||
| cp "$GH_AW_COPILOT_SRC" "$GH_AW_COPILOT_BIN" | ||
| fi | ||
| chmod 755 "$GH_AW_COPILOT_BIN" | ||
| touch /tmp/gh-aw/agent-step-summary.md | ||
| GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true) | ||
| export GH_AW_NODE_BIN | ||
| export COPILOT_API_KEY="$COPILOT_DUMMY_BYOK" | ||
| (umask 177 && touch /tmp/gh-aw/threat-detection/detection.log) | ||
| GH_AW_MAX_AI_CREDITS="${GH_AW_MAX_AI_CREDITS:-400}" | ||
| printf '%s\n' "{\"\$schema\":\"https://github.com/github/gh-aw-firewall/releases/download/v0.27.44/awf-config.schema.json\",\"network\":{\"allowDomains\":[\"api.business.githubcopilot.com\",\"api.enterprise.githubcopilot.com\",\"api.github.com\",\"api.githubcopilot.com\",\"api.individual.githubcopilot.com\",\"github.com\",\"host.docker.internal\",\"registry.npmjs.org\",\"telemetry.enterprise.githubcopilot.com\"]},\"apiProxy\":{\"enabled\":true,\"enableTokenSteering\":true,\"maxRuns\":500,\"maxAiCredits\":${GH_AW_MAX_AI_CREDITS},\"maxCacheMisses\":5,\"models\":{\"agent\":[\"sonnet-6x\",\"gpt-5.4\",\"gpt-5.5\",\"gpt-5.6\",\"gpt-5.3\",\"gemini-pro\",\"any\"],\"antigravity\":[\"copilot/antigravity*\",\"google/antigravity*\",\"gemini/antigravity*\"],\"any\":[\"copilot/*\",\"anthropic/*\",\"openai/*\",\"google/*\",\"gemini/*\"],\"auto\":[\"copilot/auto\",\"large\"],\"claude\":[\"agent\"],\"codex\":[\"agent\"],\"coding\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\",\"gpt-5-codex\",\"kimi\"],\"computer-use\":[\"copilot/*computer-use*\",\"google/*computer-use*\",\"gemini/*computer-use*\",\"openai/*computer-use*\"],\"copilot\":[\"agent\"],\"deep-research\":[\"copilot/deep-research*\",\"copilot/o3-deep-research*\",\"copilot/o4-mini-deep-research*\",\"google/deep-research*\",\"gemini/deep-research*\",\"openai/o3-deep-research*\",\"openai/o4-mini-deep-research*\"],\"detection\":[\"small\"],\"evals\":[\"small\"],\"fable\":[\"copilot/*fable*\",\"anthropic/*fable*\"],\"gemini\":[\"agent\"],\"gemini-3-flash\":[\"copilot/gemini-3*flash*\",\"google/gemini-3*flash*\",\"gemini/gemini-3*flash*\"],\"gemini-3-pro\":[\"copilot/gemini-3*pro*\",\"google/gemini-3*pro*\",\"google/nano-banana*\",\"gemini/gemini-3*pro*\"],\"gemini-3.1-flash\":[\"copilot/gemini-3.1*flash*\",\"google/gemini-3.1*flash*\",\"gemini/gemini-3.1*flash*\"],\"gemini-3.1-pro\":[\"copilot/gemini-3.1*pro*\",\"google/gemini-3.1*pro*\",\"gemini/gemini-3.1*pro*\"],\"gemini-3.5-flash\":[\"copilot/gemini-3.5*flash*\",\"google/gemini-3.5*flash*\",\"gemini/gemini-3.5*flash*\"],\"gemini-3.6-flash\":[\"copilot/gemini-3.6*flash*\",\"google/gemini-3.6*flash*\",\"gemini/gemini-3.6*flash*\"],\"gemini-flash\":[\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"],\"gemini-flash-lite\":[\"copilot/gemini-*flash*lite*\",\"google/gemini-*flash*lite*\",\"gemini/gemini-*flash*lite*\"],\"gemini-omni\":[\"copilot/gemini-omni*\",\"google/gemini-omni*\",\"gemini/gemini-omni*\"],\"gemini-pro\":[\"copilot/gemini-*pro*\",\"google/gemini-*pro*\",\"gemini/gemini-*pro*\"],\"gemma\":[\"copilot/gemma*\",\"google/gemma*\",\"gemini/gemma*\"],\"gpt-5\":[\"copilot/gpt-5*\",\"openai/gpt-5*\"],\"gpt-5-codex\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\"],\"gpt-5-mini\":[\"copilot/gpt-5*mini*\",\"openai/gpt-5*mini*\"],\"gpt-5-nano\":[\"copilot/gpt-5*nano*\",\"openai/gpt-5*nano*\"],\"gpt-5-pro\":[\"copilot/gpt-5*pro*\",\"openai/gpt-5*pro*\"],\"gpt-5.1\":[\"copilot/gpt-5.1*\",\"openai/gpt-5.1*\"],\"gpt-5.2\":[\"copilot/gpt-5.2*\",\"openai/gpt-5.2*\"],\"gpt-5.3\":[\"copilot/gpt-5.3*\",\"openai/gpt-5.3*\"],\"gpt-5.4\":[\"copilot/gpt-5.4*\",\"openai/gpt-5.4*\"],\"gpt-5.5\":[\"copilot/gpt-5.5*\",\"openai/gpt-5.5*\"],\"gpt-5.6\":[\"copilot/gpt-5.6*\",\"openai/gpt-5.6*\"],\"grok\":[\"copilot/*grok*\",\"openai/*grok*\"],\"haiku\":[\"copilot/*haiku*\",\"anthropic/*haiku*\"],\"image-generation\":[\"copilot/gpt-image*\",\"openai/gpt-image*\",\"openai/chatgpt-image*\",\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"google/imagen*\"],\"kimi\":[\"copilot/kimi*\",\"openai/kimi*\"],\"kiwi\":[\"copilot/kiwi*\",\"openai/kiwi*\"],\"large\":[\"sonnet\",\"gpt-5-pro\",\"gpt-5\",\"gemini-pro\"],\"lyria\":[\"google/lyria*\",\"gemini/lyria*\",\"copilot/lyria*\"],\"mai-code\":[\"copilot/MAI-Code*\",\"copilot/mai-code*\",\"openai/MAI-Code*\"],\"mai-code-1-flash-picker\":[\"copilot/MAI-Code-1-Flash-picker*\",\"copilot/mai-code-1-flash-picker*\",\"openai/MAI-Code-1-Flash-picker*\"],\"mini\":[\"haiku\",\"gpt-5-mini\",\"gpt-5-nano\",\"gemini-flash-lite\"],\"nano-banana\":[\"copilot/nano-banana*\",\"google/nano-banana*\",\"gemini/nano-banana*\"],\"opus\":[\"copilot/*opus*\",\"anthropic/*opus*\"],\"opusplan\":[\"opus?effort=high\"],\"raptor-mini\":[\"copilot/raptor*\",\"openai/raptor*\"],\"reasoning\":[\"copilot/o1*\",\"copilot/o3*\",\"copilot/o4*\",\"openai/o1*\",\"openai/o3*\",\"openai/o4*\"],\"robotics\":[\"copilot/*robotics*\",\"google/*robotics*\",\"gemini/*robotics*\"],\"small\":[\"mini\"],\"small-agent\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash\"],\"sonnet\":[\"copilot/*sonnet*\",\"anthropic/*sonnet*\"],\"sonnet-6x\":[\"copilot/*sonnet-4.5*\",\"copilot/*sonnet-4.6*\",\"copilot/*sonnet-5*\",\"copilot/*sonnet-4-5-*\",\"anthropic/*sonnet-4-5-*\",\"copilot/*sonnet-4-6*\",\"anthropic/*sonnet-4-6*\",\"anthropic/*sonnet-5*\"],\"summarization\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash-lite\",\"mini\"],\"veo\":[\"google/veo*\",\"gemini/veo*\"],\"vision\":[\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"]}},\"container\":{\"imageTag\":\"0.27.44,squid=sha256:83e48bbe12c634be8c228a576832fe45f66c529ac3659db92bddbcf2eeb6d627,agent=sha256:0d727725c737b58c7bdf51f640cffb928385ec46517e0917c7f1a02f1bada8b4,api-proxy=sha256:b50fbadba138f6e9aba94aca09711335c489bb3b15861220cb66f6092e042dc7,cli-proxy=sha256:c064d15974f7c933ec7d3f7b4038f4fd203547b3154bdc821afd379144887eff\"},\"logging\":{\"proxyLogsDir\":\"/tmp/gh-aw/sandbox/firewall/logs\",\"auditDir\":\"/tmp/gh-aw/sandbox/firewall/audit\"}}" > "${RUNNER_TEMP}/gh-aw/awf-config.json" | ||
| cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json | ||
| export GH_AW_MODELS_JSON_PATH="/tmp/gh-aw/models.json" | ||
| GH_AW_DOCKER_HOST="" | ||
| if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then | ||
| GH_AW_DOCKER_HOST="${DOCKER_HOST}" | ||
| fi | ||
| if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then | ||
| _GH_AW_CHROOT_JSON=$(jq -c --arg src "${RUNNER_TEMP}/gh-aw" --arg user "$(id -un)" --argjson uid "$(id -u)" --argjson gid "$(id -g)" --arg home "${RUNNER_TEMP}/gh-aw/home" '.chroot={"binariesSourcePath":$src,"identity":{"user":$user,"uid":$uid,"gid":$gid,"home":$home}}' "${RUNNER_TEMP}/gh-aw/awf-config.json") || { echo "chroot config patch failed" >&2; exit 1; } | ||
| printf '%s\n' "$_GH_AW_CHROOT_JSON" > "${RUNNER_TEMP}/gh-aw/awf-config.json" | ||
| printf '%s\n' "$_GH_AW_CHROOT_JSON" > "${RUNNER_TEMP}/gh-aw/awf-config.json" | ||
| fi | ||
| GH_AW_TOOL_CACHE_MOUNT="" | ||
| GH_AW_TOOL_CACHE="${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}" | ||
| if [ -d "$GH_AW_TOOL_CACHE" ]; then | ||
| if [[ "$GH_AW_TOOL_CACHE" != /opt/* ]]; then | ||
| GH_AW_TOOL_CACHE_MOUNT="$GH_AW_TOOL_CACHE:$GH_AW_TOOL_CACHE:ro" | ||
| fi | ||
| fi | ||
| # shellcheck disable=SC1003,SC2016,SC2086 | ||
| awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_TOOL_CACHE_MOUNT:+--mount "$GH_AW_TOOL_CACHE_MOUNT"} ${GH_AW_DOCKER_HOST:+--docker-host "$GH_AW_DOCKER_HOST"} --env-all --exclude-env ACTIONS_ID_TOKEN_REQUEST_TOKEN --exclude-env ACTIONS_ID_TOKEN_REQUEST_URL --exclude-env COPILOT_GITHUB_TOKEN --mount /tmp/gh-aw:/tmp/gh-aw:rw --log-level info --skip-pull \ | ||
| -- /bin/bash -c 'set +o histexpand; : "${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}"; GH_AW_TOOL_CACHE="$RUNNER_TOOL_CACHE"; export PATH="$(find "$GH_AW_TOOL_CACHE" -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true; [ -n "$ERLANG_HOME" ] && export PATH="$ERLANG_HOME/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; GH_AW_NPM_GLOBAL_ROOT="$(npm root -g 2>/dev/null || true)"; if [ -n "$GH_AW_NPM_GLOBAL_ROOT" ]; then export NODE_PATH="${GH_AW_NPM_GLOBAL_ROOT}${NODE_PATH:+:${NODE_PATH}}"; fi; "$GH_AW_NODE_EXEC" "${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs" "${RUNNER_TEMP}/gh-aw/bin/copilot" --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log | ||
| env: | ||
| GITHUB_STEP_SUMMARY: /tmp/gh-aw/step-summary.md | ||
| AWF_REFLECT_ENABLED: 1 | ||
| COPILOT_AGENT_RUNNER_TYPE: STANDALONE | ||
| COPILOT_DUMMY_BYOK: dummy-byok-key-for-offline-mode | ||
| COPILOT_GITHUB_TOKEN: ${{ case(needs.pat_pool.outputs.pat_number == '0', secrets.COPILOT_PAT_0, needs.pat_pool.outputs.pat_number == '1', secrets.COPILOT_PAT_1, needs.pat_pool.outputs.pat_number == '2', secrets.COPILOT_PAT_2, needs.pat_pool.outputs.pat_number == '3', secrets.COPILOT_PAT_3, needs.pat_pool.outputs.pat_number == '4', secrets.COPILOT_PAT_4, needs.pat_pool.outputs.pat_number == '5', secrets.COPILOT_PAT_5, needs.pat_pool.outputs.pat_number == '6', secrets.COPILOT_PAT_6, needs.pat_pool.outputs.pat_number == '7', secrets.COPILOT_PAT_7, needs.pat_pool.outputs.pat_number == '8', secrets.COPILOT_PAT_8, needs.pat_pool.outputs.pat_number == '9', secrets.COPILOT_PAT_9, 'NO COPILOT PAT AVAILABLE') }} | ||
| COPILOT_MODEL: detection | ||
| GH_AW_LLM_PROVIDER: github | ||
| GH_AW_MAX_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_DETECTION_MAX_AI_CREDITS || '400' }} | ||
| GH_AW_MAX_TURNS: ${{ vars.GH_AW_DEFAULT_MAX_TURNS || '' }} | ||
| GH_AW_PHASE: detection | ||
| GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt | ||
| GH_AW_TIMEOUT_MINUTES: 20 | ||
| GH_AW_VERSION: v0.86.2 | ||
| GITHUB_API_URL: ${{ github.api_url }} | ||
| GITHUB_AW: true | ||
| GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows | ||
| GITHUB_HEAD_REF: ${{ github.head_ref }} | ||
| GITHUB_REF_NAME: ${{ github.ref_name }} | ||
| GITHUB_SERVER_URL: ${{ github.server_url }} | ||
| GITHUB_WORKSPACE: ${{ github.workspace }} | ||
| GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com | ||
| GIT_AUTHOR_NAME: github-actions[bot] | ||
| GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com | ||
| GIT_COMMITTER_NAME: github-actions[bot] | ||
| RUNNER_TEMP: ${{ runner.temp }} | ||
| TRACEPARENT: ${{ env.GITHUB_AW_OTEL_TRACE_ID != '' && env.GITHUB_AW_OTEL_PARENT_SPAN_ID != '' && format('00-{0}-{1}-01', env.GITHUB_AW_OTEL_TRACE_ID, env.GITHUB_AW_OTEL_PARENT_SPAN_ID) || '' }} | ||
| - name: Echo detection step summary | ||
| if: always() && steps.detection_guard.outputs.run_detection == 'true' | ||
| continue-on-error: true | ||
| run: | | ||
| if [ -s /tmp/gh-aw/step-summary.md ]; then | ||
| cat /tmp/gh-aw/step-summary.md | ||
| fi | ||
| - name: Render detection log | ||
| if: always() && steps.detection_guard.outputs.run_detection == 'true' | ||
| continue-on-error: true | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| with: | ||
| script: | | ||
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require('${{ runner.temp }}/gh-aw/actions/render_detection_log.cjs'); | ||
| await main(); | ||
| - name: Parse threat detection token usage for step summary | ||
| id: parse_detection_token_usage | ||
| if: always() | ||
| continue-on-error: true | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| env: | ||
| GH_AW_TOKEN_USAGE_SUMMARY_TITLE: Threat Detection Token Usage | ||
| with: | ||
| script: | | ||
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_token_usage.cjs'); | ||
| await main(); | ||
| - name: Upload threat detection log | ||
| if: always() && steps.detection_guard.outputs.run_detection == 'true' | ||
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | ||
| with: | ||
| name: detection | ||
| path: /tmp/gh-aw/threat-detection/detection.log | ||
| if-no-files-found: ignore | ||
| - name: Parse and conclude threat detection | ||
| id: detection_conclusion | ||
| if: always() | ||
| continue-on-error: true | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| env: | ||
| RUN_DETECTION: ${{ steps.detection_guard.outputs.run_detection }} | ||
| DETECTION_AGENTIC_EXECUTION_OUTCOME: ${{ steps.detection_agentic_execution.outcome }} | ||
| GH_AW_DETECTION_CONTINUE_ON_ERROR: "true" | ||
| with: | ||
| script: | | ||
| try { | ||
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_threat_detection_results.cjs'); | ||
| await main(); | ||
| } catch (loadErr) { | ||
| const continueOnError = process.env.GH_AW_DETECTION_CONTINUE_ON_ERROR !== 'false'; | ||
| const detectionExecutionFailed = process.env.DETECTION_AGENTIC_EXECUTION_OUTCOME === 'failure'; | ||
| const msg = 'ERR_SYSTEM: \u274C Unexpected error loading threat detection module: ' + (loadErr && loadErr.message ? loadErr.message : String(loadErr)); | ||
| core.error(msg); | ||
| core.setOutput('reason', 'parse_error'); | ||
| if (continueOnError && !detectionExecutionFailed) { | ||
| core.warning('\u26A0\uFE0F ' + msg); | ||
| core.setOutput('conclusion', 'warning'); | ||
| core.setOutput('success', 'false'); | ||
| } else { | ||
| core.setOutput('conclusion', 'failure'); | ||
| core.setOutput('success', 'false'); | ||
| core.setFailed(msg); | ||
| } | ||
| } | ||
| fetch-binlog: | ||
| name: Fetch binlogs (Azure Pipelines) | ||
| if: > | ||
| github.event_name == 'workflow_dispatch' || (github.event.check_run.name == 'runtime' && github.event.check_run.conclusion == 'failure') | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: read | ||
| pull-requests: read | ||
| timeout-minutes: 15 | ||
| outputs: | ||
| ado-build-id: ${{ steps.fetch.outputs.ado-build-id }} | ||
| ado-build-url: ${{ steps.fetch.outputs.ado-build-url }} | ||
| binlog-found: ${{ steps.fetch.outputs.binlog-found }} | ||
| pr-head-sha: ${{ steps.fetch.outputs.pr-head-sha }} | ||
| pr-merge-sha: ${{ steps.fetch.outputs.pr-merge-sha }} | ||
| pr-number: ${{ steps.fetch.outputs.pr-number }} | ||
| steps: | ||
| - name: Configure GH_HOST for enterprise compatibility | ||
| id: ghes-host-config | ||
| shell: bash | ||
| run: | # zizmor: ignore[github-env] - GITHUB_SERVER_URL is set by GitHub Actions, not user input. | ||
| # 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" | ||
| - name: Download binlogs from the failed Azure Pipelines build | ||
| id: fetch | ||
| run: | | ||
| # Advisory + best-effort: on any gap emit binlog-found=false and the | ||
| # agent pipeline stays inert. | ||
| set +e | ||
| set +o pipefail | ||
| emit_none() { echo "binlog-found=false" >> "$GITHUB_OUTPUT"; exit 0; } | ||
| # --- 1. Resolve the Azure DevOps build id --- | ||
| if [ "${EVENT_NAME}" = "workflow_dispatch" ]; then | ||
| BUILD_ID="${DISPATCH_BUILD_ID}" | ||
| else | ||
| # details_url looks like: .../_build/results?buildId=NNN&view=... | ||
| BUILD_ID=$(printf '%s' "${CHECK_DETAILS_URL}" | grep -oE 'buildId=[0-9]+' | head -1 | cut -d= -f2) | ||
| fi | ||
| echo "Azure DevOps build id: '${BUILD_ID}'" | ||
| [ -z "${BUILD_ID}" ] && { echo "::warning::Could not resolve an ADO build id."; emit_none; } | ||
| # The build id feeds directly into ADO API URLs below; require it to | ||
| # be purely numeric (esp. on workflow_dispatch, where it is free-form | ||
| # input) so a malformed value can't alter the request path/query. | ||
| if ! printf '%s' "${BUILD_ID}" | grep -qE '^[0-9]+$'; then | ||
| echo "::warning::Resolved ADO build id '${BUILD_ID}' is not numeric; refusing."; emit_none | ||
| fi | ||
| # Fetch the build metadata once, up front: it is the authoritative | ||
| # source for the definition/result/revision validated in step 4. | ||
| # The PR number remains event-owned so safe outputs can be bound to | ||
| # the same trusted value before the fetch job runs. | ||
| build_json=$(curl -sSL --retry 3 "${ADO_API}/build/builds/${BUILD_ID}?api-version=7.1") | ||
| RESULT=$(printf '%s' "${build_json}" | jq -r '.result // empty') | ||
| DEF_ID=$(printf '%s' "${build_json}" | jq -r '.definition.id // empty') | ||
| SRC_BRANCH=$(printf '%s' "${build_json}" | jq -r '.sourceBranch // empty') | ||
| # --- 2. Resolve the PR number + head SHA --- | ||
| if [ "${EVENT_NAME}" = "workflow_dispatch" ]; then | ||
| PR_NUMBER="${DISPATCH_PR_NUMBER}" | ||
| HEAD_SHA="" | ||
| else | ||
| # Safe outputs are bound to check_run.pull_requests[0] below. Use | ||
| # that same event-owned PR number here and fail closed when it is | ||
| # absent; the sourceBranch validation in step 4 ensures the ADO | ||
| # build belongs to this exact PR before any analysis can run. | ||
| PR_NUMBER="${CHECK_PR_NUMBER}" | ||
| HEAD_SHA="${CHECK_HEAD_SHA}" | ||
| fi | ||
| [ -z "${PR_NUMBER}" ] && { echo "::warning::Could not resolve a PR number."; emit_none; } | ||
| # PR_NUMBER feeds `gh api .../pulls/<n>` and the `refs/pull/<n>/merge` | ||
| # comparison; require it numeric so a malformed value can't reach the | ||
| # GitHub API path (traversal-like input) or skew the branch match. | ||
| if ! printf '%s' "${PR_NUMBER}" | grep -qE '^[0-9]+$'; then | ||
| echo "::warning::Resolved PR number '${PR_NUMBER}' is not numeric; refusing."; emit_none | ||
| fi | ||
| # --- 3. Scope check: only analyse PRs targeting main / release/* --- | ||
| PR_JSON=$(gh api "repos/${GH_AW_REPO}/pulls/${PR_NUMBER}" 2>/dev/null) | ||
| BASE_REF=$(printf '%s' "${PR_JSON}" | jq -r '.base.ref // empty') | ||
| # An empty BASE_REF means the `gh api` call failed or returned no | ||
| # data (rate limit / transient error), NOT that the PR targets an | ||
| # out-of-scope branch. Treat it as a data-resolution failure so a | ||
| # valid PR isn't silently skipped and misreported as base '' out of | ||
| # scope. | ||
| [ -z "${BASE_REF}" ] && { echo "::warning::Could not resolve the base ref for PR #${PR_NUMBER} (GitHub API returned no data); treating as a data-resolution failure, not an out-of-scope branch."; emit_none; } | ||
| [ -z "${HEAD_SHA}" ] && HEAD_SHA=$(printf '%s' "${PR_JSON}" | jq -r '.head.sha // empty') | ||
| case "${BASE_REF}" in | ||
| main|release/*) echo "PR #${PR_NUMBER} base '${BASE_REF}' is in scope." ;; | ||
| *) echo "::warning::PR #${PR_NUMBER} base '${BASE_REF}' is out of scope (main, release/*); skipping."; emit_none ;; | ||
| esac | ||
| # --- 4. Validate the build for EVERY trigger (not just dispatch): | ||
| # it must be the runtime definition (129), have failed, and | ||
| # belong to this PR (sourceBranch == refs/pull/<PR>/merge). | ||
| # For `check_run` the build id is parsed from a check payload | ||
| # we don't fully trust; for dispatch the build id and PR | ||
| # number are independent inputs. Validating on both paths | ||
| # prevents downloading an unrelated build or posting its | ||
| # analysis to the wrong PR. | ||
| echo "ADO build ${BUILD_ID}: result='${RESULT}' definition='${DEF_ID}' sourceBranch='${SRC_BRANCH}'" | ||
| if [ "${DEF_ID}" != "${ADO_BUILD_DEFINITION_ID}" ]; then | ||
| echo "::warning::ADO build ${BUILD_ID} is definition '${DEF_ID}', not runtime (${ADO_BUILD_DEFINITION_ID}); refusing."; emit_none | ||
| fi | ||
| if [ "${RESULT}" != "failed" ]; then | ||
| echo "::warning::ADO build ${BUILD_ID} did not fail (result='${RESULT}'); nothing to analyze."; emit_none | ||
| fi | ||
| if [ "${SRC_BRANCH}" != "refs/pull/${PR_NUMBER}/merge" ]; then | ||
| echo "::warning::ADO build ${BUILD_ID} sourceBranch '${SRC_BRANCH}' does not match PR #${PR_NUMBER} (refs/pull/${PR_NUMBER}/merge); refusing to avoid posting to the wrong PR."; emit_none | ||
| fi | ||
| # Require the build's analyzed revision to equal the PR's CURRENT | ||
| # head. gh-aw safe-output review comments carry no `commit_id` — they | ||
| # target the current PR diff — so analyzing a stale revision would | ||
| # produce inline suggestions that get rejected or land on the wrong | ||
| # lines. If the PR has advanced since this build ran, skip: a newer | ||
| # build/check for the current head will cover it. | ||
| BUILD_PR_SHA=$(printf '%s' "${build_json}" | jq -r '.triggerInfo["pr.sourceSha"] // empty') | ||
| CURRENT_HEAD=$(printf '%s' "${PR_JSON}" | jq -r '.head.sha // empty') | ||
| # ADO builds GitHub's `refs/pull/<n>/merge` ref, so build_json.sourceVersion | ||
| # is the merge commit GitHub produced at build time and equals the PR's | ||
| # `merge_commit_sha` then. If the base branch advances (even with the PR | ||
| # head unchanged) GitHub recomputes that merge and merge_commit_sha | ||
| # changes, so this catches base-advance staleness the head check misses. | ||
| BUILD_MERGE_SHA=$(printf '%s' "${build_json}" | jq -r '.sourceVersion // empty') | ||
| CURRENT_MERGE=$(printf '%s' "${PR_JSON}" | jq -r '.merge_commit_sha // empty') | ||
| # Fail CLOSED: if either the build's analyzed revision or the current | ||
| # PR head can't be resolved, skip — we must not analyze a possibly | ||
| # stale binlog against the current diff (inline comments have no | ||
| # commit_id and target the current PR diff). | ||
| if [ -z "${BUILD_PR_SHA}" ] || [ -z "${CURRENT_HEAD}" ]; then | ||
| echo "::warning::Could not resolve build revision ('${BUILD_PR_SHA}') and/or current PR head ('${CURRENT_HEAD}'); skipping to avoid analyzing a stale binlog against the current diff." | ||
| emit_none | ||
| fi | ||
| if [ "${BUILD_PR_SHA}" != "${CURRENT_HEAD}" ]; then | ||
| echo "::warning::Build ${BUILD_ID} analyzed revision '${BUILD_PR_SHA}' but PR #${PR_NUMBER} head is now '${CURRENT_HEAD}'; skipping stale build (a newer build/check will cover the current revision)." | ||
| emit_none | ||
| fi | ||
| # When both merge revisions are known and differ, the base branch moved | ||
| # since the build — the binlog reflects an obsolete merge. Skip. | ||
| if [ -n "${BUILD_MERGE_SHA}" ] && [ -n "${CURRENT_MERGE}" ] && [ "${BUILD_MERGE_SHA}" != "${CURRENT_MERGE}" ]; then | ||
| echo "::warning::Build ${BUILD_ID} merge revision '${BUILD_MERGE_SHA}' but PR #${PR_NUMBER} current merge is '${CURRENT_MERGE}' (base branch advanced); skipping stale merge." | ||
| emit_none | ||
| fi | ||
| # Consistent now: build revision == current PR head. Use it for | ||
| # permalinks so they line up with the inline comments' diff target. | ||
| HEAD_SHA="${CURRENT_HEAD}" | ||
| echo "Analyzing build ${BUILD_ID} at PR head revision '${HEAD_SHA}'." | ||
| # --- 5. Download failed-job Logs_Build_* artifacts and binlogs ---- | ||
| # Runtime publishes roughly 150 Logs_Build_* artifacts per PR build. | ||
| # Use the timeline to select only failed/canceled jobs; downloading | ||
| # every successful leg would exceed this advisory workflow's time and | ||
| # disk budgets without adding evidence about the failing job. | ||
| timeline_json=$(curl -sSL --fail --retry 3 \ | ||
| "${ADO_API}/build/builds/${BUILD_ID}/timeline?api-version=7.1") | ||
| mapfile -t failed_job_keys < <( | ||
| printf '%s' "${timeline_json}" | | ||
| jq -r '.records // [] | map(select(.type == "Job" and (.result == "failed" or .result == "canceled"))) | .[].name' | | ||
| while IFS= read -r job_name; do | ||
| printf '%s' "${job_name}" | tr '[:upper:]' '[:lower:]' | tr -cd '[:alnum:]' | ||
| printf '\n' | ||
| done | | ||
| awk 'NF && !seen[$0]++' | ||
| ) | ||
| [ "${#failed_job_keys[@]}" -eq 0 ] && { echo "::warning::No failed or canceled jobs found in the timeline for build ${BUILD_ID}."; emit_none; } | ||
| artifacts_json=$(curl -sSL --retry 3 "${ADO_API}/build/builds/${BUILD_ID}/artifacts?api-version=7.1") | ||
| mapfile -t all_names < <(printf '%s' "${artifacts_json}" | jq -r '.value // [] | map(select(.name | test("^Logs_Build_"))) | .[].name') | ||
| mapfile -t names < <( | ||
| for name in "${all_names[@]}"; do | ||
| # Remove only the transport/retry prefix, then compare the | ||
| # normalized job portion exactly. A substring comparison makes | ||
| # `..._NativeAOT` also match the distinct successful | ||
| # `..._NativeAOT_Libraries` job. | ||
| artifact_job_name=$(printf '%s' "${name}" | sed -E 's/^Logs_Build_(Attempt[0-9]+_)?//') | ||
| artifact_key=$(printf '%s' "${artifact_job_name}" | tr '[:upper:]' '[:lower:]' | tr -cd '[:alnum:]') | ||
| for job_key in "${failed_job_keys[@]}"; do | ||
| if [[ "${artifact_key}" == "${job_key}" ]]; then | ||
| printf '%s\n' "${name}" | ||
| break | ||
| fi | ||
| done | ||
| done | ||
| ) | ||
| [ "${#names[@]}" -eq 0 ] && { echo "::warning::No Logs_Build_* artifacts matched the failed or canceled jobs in build ${BUILD_ID}; the failure is likely outside a build leg."; emit_none; } | ||
| echo "Selected ${#names[@]} of ${#all_names[@]} Logs_Build_* artifacts for ${#failed_job_keys[@]} failed or canceled jobs." | ||
| # Guards for untrusted PR-produced archives: cap the compressed | ||
| # download and the reported uncompressed size per artifact, bound | ||
| # extraction time, AND enforce a cumulative uncompressed budget across | ||
| # all legs so many individually-small artifacts can't collectively | ||
| # exhaust the runner's disk. | ||
| MAX_ZIP_BYTES=524288000 # 500 MB compressed per artifact | ||
| MAX_UNZIP_BYTES=2147483648 # 2 GB uncompressed per artifact | ||
| MAX_TOTAL_BYTES=4294967296 # 4 GB uncompressed across all artifacts | ||
| TOTAL_BYTES=0 | ||
| mkdir -p /tmp/binlogs | ||
| count=0 | ||
| staged_legs=0 | ||
| ai=0 | ||
| for name in "${names[@]}"; do | ||
| # `name` is PR-controlled ADO artifact metadata and the | ||
| # `^Logs_Build_` filter only anchors the prefix, so sanitize it | ||
| # before using it in any on-disk path or workflow command (guards | ||
| # against path traversal and command injection); keep the original | ||
| # `name` only for the artifacts_json lookup. | ||
| safe_name=$(printf '%s' "${name}" | tr -c 'A-Za-z0-9._-' '_') | ||
| ai=$((ai + 1)) | ||
| url=$(printf '%s' "${artifacts_json}" | jq -r --arg n "${name}" '.value[] | select(.name==$n) | .resource.downloadUrl // empty') | ||
| [ -z "${url}" ] && continue | ||
| rm -rf /tmp/ax /tmp/a.zip | ||
| mkdir -p /tmp/ax | ||
| # Download to a file, never a pipe: curl retries transient | ||
| # 5xx/429/timeouts but can only rewind seekable output, so through | ||
| # a pipe the retried body is APPENDED — a 503 error page followed | ||
| # by a retry yields a corrupt `<error page><zip>` that still exits | ||
| # 0. `--fail` keeps error bodies off disk. | ||
| # `ulimit -f` is only a disk backstop for a response that declares | ||
| # no Content-Length; the `-ge MAX_ZIP_BYTES` guard below is | ||
| # authoritative. Divide by 512 so the cap is >= MAX_ZIP_BYTES under | ||
| # either block-size reading (bash uses 1024, POSIX says 512). | ||
| # SIGXFSZ is ignored so hitting the cap is an ordinary write error | ||
| # (23) rather than a "File size limit exceeded (core dumped)" log. | ||
| ( | ||
| ulimit -f $((MAX_ZIP_BYTES / 512)) | ||
| trap '' XFSZ | ||
| curl -sSL --fail --retry 3 --retry-delay 2 --max-time 600 -o /tmp/a.zip "${url}" | ||
| ) 2>/dev/null | ||
| curl_rc=$? | ||
| ZIP_BYTES=$(stat -c%s /tmp/a.zip 2>/dev/null || echo 0) | ||
| if [ "${ZIP_BYTES}" -eq 0 ]; then | ||
| echo "::warning::Skipping ${safe_name}: empty or failed download."; continue | ||
| fi | ||
| if [ "${ZIP_BYTES}" -ge "${MAX_ZIP_BYTES}" ]; then | ||
| echo "::warning::Skipping ${safe_name}: download reached the ${MAX_ZIP_BYTES}-byte cap."; continue | ||
| fi | ||
| # After the size guards: hitting the ulimit cap is reported as an | ||
| # oversized artifact above, not as a generic transfer failure. | ||
| if [ "${curl_rc}" -ne 0 ]; then | ||
| echo "::warning::Skipping ${safe_name}: download failed or was truncated (curl exit ${curl_rc})."; continue | ||
| fi | ||
| # `unzip -Zt` prints ONE summary line ("<n> files, <x> bytes | ||
| # uncompressed, ..."), so the total comes from a fixed column | ||
| # instead of the shifting last row of `unzip -l`. Use `END{}`: | ||
| # Info-ZIP prepends warnings on STDOUT for a recoverable archive, | ||
| # and a multi-line value would still pass the `grep -qE` check | ||
| # below, since `grep -q` matches if ANY line matches. `timeout` | ||
| # bounds a hostile archive; pipefail + fail-closed because a killed | ||
| # probe's partial output can end in a numeric column and undercount. | ||
| UNCOMP=$(set -o pipefail; timeout 60 unzip -Zt /tmp/a.zip 2>/dev/null | awk 'END{print $3}') \ | ||
| || { echo "::warning::Skipping ${safe_name}: 'unzip -Zt' failed or timed out; cannot verify uncompressed size."; continue; } | ||
| # Fail safe: a non-numeric size (corrupt zip, unexpected or | ||
| # timed-out output) can't be verified, so skip rather than let it | ||
| # bypass the guards below. | ||
| if ! printf '%s' "${UNCOMP}" | grep -qE '^[0-9]+$'; then | ||
| echo "::warning::Skipping ${safe_name}: could not determine uncompressed size (unparseable/timed-out unzip output)."; continue | ||
| fi | ||
| # ZIP64 sizes can reach ~20 digits, overflowing Bash's signed | ||
| # 64-bit `-gt` (and the `$((...))` below), which under `set +e` | ||
| # would let an oversized archive through. More digits than the | ||
| # limit is unambiguously larger, so reject on length first. | ||
| if [ "${#UNCOMP}" -gt "${#MAX_UNZIP_BYTES}" ]; then | ||
| echo "::warning::Skipping ${safe_name}: uncompressed size has ${#UNCOMP} digits, exceeding the ${MAX_UNZIP_BYTES} guard (possible zip bomb)."; continue | ||
| fi | ||
| if [ "${UNCOMP}" -gt "${MAX_UNZIP_BYTES}" ]; then | ||
| echo "::warning::Skipping ${safe_name}: uncompressed size ${UNCOMP} exceeds ${MAX_UNZIP_BYTES} guard (possible zip bomb)."; continue | ||
| fi | ||
| if [ $((TOTAL_BYTES + UNCOMP)) -gt "${MAX_TOTAL_BYTES}" ]; then | ||
| echo "::warning::Cumulative uncompressed budget ${MAX_TOTAL_BYTES} reached at ${safe_name}; stopping extraction."; break | ||
| fi | ||
| # Refuse the archive if any entry path is absolute or has a `..` | ||
| # component (defense-in-depth over unzip's own traversal guard), | ||
| # then extract `*.binlog` entries *preserving* their in-archive | ||
| # paths (no `-j`) under a fresh dir + timeout, so two binlogs that | ||
| # share a basename in different folders don't overwrite each other. | ||
| # The listing is streamed through `grep` (no full in-memory buffer | ||
| # of entry names) and PIPESTATUS separates the failure modes: a | ||
| # non-zero listing exit (error/timeout) FAILS CLOSED; a grep match | ||
| # means a suspicious absolute/`..` path. | ||
| timeout 60 unzip -Z1 /tmp/a.zip 2>/dev/null | grep -qE '(^/|(^|/)\.\.(/|$))' | ||
| zscan_rc=("${PIPESTATUS[@]}") | ||
| if [ "${zscan_rc[0]}" -ne 0 ]; then | ||
| echo "::warning::Skipping ${safe_name}: could not list archive entries (unzip -Z1 rc=${zscan_rc[0]})."; continue | ||
| fi | ||
| if [ "${zscan_rc[1]}" -eq 0 ]; then | ||
| echo "::warning::Skipping ${safe_name}: archive has a suspicious (absolute or ..) entry path."; continue | ||
| fi | ||
| timeout 120 unzip -o /tmp/a.zip '*.binlog' -d /tmp/ax >/dev/null 2>&1 \ | ||
| || { echo "::warning::Skipping ${safe_name}: extraction failed or timed out."; continue; } | ||
| # Consume the budget only once the archive actually extracted, so a | ||
| # skipped leg can't exhaust it and force later legs to be dropped. | ||
| TOTAL_BYTES=$((TOTAL_BYTES + UNCOMP)) | ||
| i=0 | ||
| leg_staged=0 | ||
| while IFS= read -r bl; do | ||
| [ -f "${bl}" ] || continue | ||
| # Prefixing with the artifact index (`ai`) and per-file counter | ||
| # (`i`) keeps destinations unique, so neither a cross-artifact | ||
| # sanitize collision nor same-basename entries can overwrite a | ||
| # staged binlog. `safe_name` is kept only for readability. | ||
| dest="/tmp/binlogs/${ai}_${i}_${safe_name}.binlog" | ||
| # Count only a successful copy — `set +e` is on, so a failed `cp` | ||
| # must not inflate the counts. | ||
| if cp "${bl}" "${dest}"; then | ||
| count=$((count + 1)) | ||
| i=$((i + 1)) | ||
| leg_staged=1 | ||
| else | ||
| echo "::warning::Failed to stage ${bl}; skipping." | ||
| fi | ||
| done < <(find /tmp/ax -type f -name '*.binlog') | ||
| # This leg produced at least one usable binlog. | ||
| [ "${leg_staged}" -eq 1 ] && staged_legs=$((staged_legs + 1)) | ||
| done | ||
| echo "Extracted ${count} binlog(s) from ${staged_legs}/${#names[@]} selected artifacts into /tmp/binlogs:" | ||
| ls -la /tmp/binlogs || true | ||
| [ "${count}" -eq 0 ] && { echo "::warning::No *.binlog found in the selected Logs_Build_* artifacts of build ${BUILD_ID}."; emit_none; } | ||
| # Fail CLOSED on a partial selected set: a missing artifact could be | ||
| # the failed attempt that contains the root cause. | ||
| if [ "${staged_legs}" -ne "${#names[@]}" ]; then | ||
| echo "::warning::Only ${staged_legs} of ${#names[@]} selected Logs_Build_* artifacts produced a usable binlog; skipping incomplete failed-job data." | ||
| emit_none | ||
| fi | ||
| # The download/extract loop above can take minutes. Re-read the PR | ||
| # head right before activating and fail CLOSED if it moved or can't | ||
| # be resolved: a force-push during that window would otherwise leave | ||
| # the analyzed binlog stale relative to the current diff (inline | ||
| # comments carry no commit_id and target the current diff). | ||
| LATEST_PR=$(gh api "repos/${GH_AW_REPO}/pulls/${PR_NUMBER}" 2>/dev/null) | ||
| LATEST_HEAD=$(printf '%s' "${LATEST_PR}" | jq -r '.head.sha // empty') | ||
| LATEST_MERGE=$(printf '%s' "${LATEST_PR}" | jq -r '.merge_commit_sha // empty') | ||
| if [ -z "${LATEST_HEAD}" ] || [ "${LATEST_HEAD}" != "${HEAD_SHA}" ]; then | ||
| echo "::warning::PR #${PR_NUMBER} head changed during artifact download ('${HEAD_SHA}' -> '${LATEST_HEAD}') or could not be re-resolved; skipping to avoid posting stale-build suggestions against the new diff." | ||
| emit_none | ||
| fi | ||
| # The base branch may also have advanced during the download; if the | ||
| # merge revision moved from what the build analyzed, skip (stale merge). | ||
| if [ -n "${BUILD_MERGE_SHA}" ] && [ -n "${LATEST_MERGE}" ] && [ "${LATEST_MERGE}" != "${BUILD_MERGE_SHA}" ]; then | ||
| echo "::warning::PR #${PR_NUMBER} merge revision changed during artifact download ('${BUILD_MERGE_SHA}' -> '${LATEST_MERGE}'); skipping stale merge." | ||
| emit_none | ||
| fi | ||
| { | ||
| echo "binlog-found=true" | ||
| echo "pr-number=${PR_NUMBER}" | ||
| echo "pr-head-sha=${HEAD_SHA}" | ||
| echo "pr-merge-sha=${BUILD_MERGE_SHA}" | ||
| echo "ado-build-id=${BUILD_ID}" | ||
| echo "ado-build-url=${ADO_BUILD_UI}?buildId=${BUILD_ID}" | ||
| } >> "$GITHUB_OUTPUT" | ||
| env: | ||
| ADO_API: https://dev.azure.com/dnceng-public/public/_apis | ||
| ADO_BUILD_DEFINITION_ID: "129" | ||
| ADO_BUILD_UI: https://dev.azure.com/dnceng-public/public/_build/results | ||
| CHECK_DETAILS_URL: ${{ github.event.check_run.details_url }} | ||
| CHECK_HEAD_SHA: ${{ github.event.check_run.head_sha }} | ||
| CHECK_PR_NUMBER: ${{ github.event.check_run.pull_requests[0].number }} | ||
| DISPATCH_BUILD_ID: ${{ inputs['ado-build-id'] }} | ||
| DISPATCH_PR_NUMBER: ${{ inputs['pr-number'] }} | ||
| EVENT_NAME: ${{ github.event_name }} | ||
| GH_AW_REPO: ${{ github.repository }} | ||
| GH_TOKEN: ${{ github.token }} | ||
| shell: bash | ||
| - name: Upload analysis artifact | ||
| if: steps.fetch.outputs.binlog-found == 'true' | ||
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | ||
| with: | ||
| if-no-files-found: warn | ||
| name: build-failure-analysis-data | ||
| path: /tmp/binlogs | ||
| retention-days: 1 | ||
| pat_pool: | ||
| needs: pre_activation | ||
| runs-on: ubuntu-slim | ||
| environment: copilot-pat-pool | ||
| outputs: | ||
| pat_number: ${{ steps.select-pat-number.outputs.copilot_pat_number }} | ||
| steps: | ||
| - name: Configure GH_HOST for enterprise compatibility | ||
| id: ghes-host-config | ||
| shell: bash | ||
| run: | # zizmor: ignore[github-env] - GITHUB_SERVER_URL is set by GitHub Actions, not user input. | ||
| # 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" | ||
| - name: Select Copilot token from pool | ||
| id: select-pat-number | ||
| run: | | ||
| # Collect pool entries with non-empty secrets from COPILOT_PAT_0..COPILOT_PAT_9. | ||
| PAT_NUMBERS=() | ||
| POOL_INDICATORS=(➖ ➖ ➖ ➖ ➖ ➖ ➖ ➖ ➖ ➖) | ||
| for i in $(seq 0 9); do | ||
| var="COPILOT_PAT_${i}" | ||
| val="${!var}" | ||
| if [ -n "$val" ]; then | ||
| PAT_NUMBERS+=(${i}) | ||
| POOL_INDICATORS[${i}]="🟪" | ||
| fi | ||
| done | ||
| # If none of the entries in the pool have values, emit a warning | ||
| # and do not set an output value. The consumer can fall back to | ||
| # using COPILOT_GITHUB_TOKEN. | ||
| if [ ${#PAT_NUMBERS[@]} -eq 0 ]; then | ||
| warning_message="::warning::None of the PAT pool entries had values " | ||
| warning_message+="(checked COPILOT_PAT_0 through COPILOT_PAT_9)" | ||
| echo "$warning_message" | ||
| exit 0 | ||
| fi | ||
| # Select a random index using the seed if specified | ||
| if [ -n "$RANDOM_SEED" ]; then | ||
| RANDOM=$RANDOM_SEED | ||
| fi | ||
| PAT_INDEX=$(( RANDOM % ${#PAT_NUMBERS[@]} )) | ||
| PAT_NUMBER="${PAT_NUMBERS[$PAT_INDEX]}" | ||
| POOL_INDICATORS[${PAT_NUMBER}]="✅" | ||
| echo "Pool size: ${#PAT_NUMBERS[@]}" | ||
| echo "Selected PAT number ${PAT_NUMBER} (index: ${PAT_INDEX})" | ||
| # Emit a markdown table of the pool entries to the step summary | ||
| echo "|0|1|2|3|4|5|6|7|8|9|" >> "$GITHUB_STEP_SUMMARY" | ||
| echo "|-|-|-|-|-|-|-|-|-|-|" >> "$GITHUB_STEP_SUMMARY" | ||
| (IFS='|'; printf '|%s' "${POOL_INDICATORS[@]}"; printf '|\n') >> "$GITHUB_STEP_SUMMARY" | ||
| # Set the PAT number as the output | ||
| echo "copilot_pat_number=${PAT_NUMBER}" >> "$GITHUB_OUTPUT" | ||
| env: | ||
| COPILOT_PAT_0: ${{ secrets.COPILOT_PAT_0 }} | ||
| COPILOT_PAT_1: ${{ secrets.COPILOT_PAT_1 }} | ||
| COPILOT_PAT_2: ${{ secrets.COPILOT_PAT_2 }} | ||
| COPILOT_PAT_3: ${{ secrets.COPILOT_PAT_3 }} | ||
| COPILOT_PAT_4: ${{ secrets.COPILOT_PAT_4 }} | ||
| COPILOT_PAT_5: ${{ secrets.COPILOT_PAT_5 }} | ||
| COPILOT_PAT_6: ${{ secrets.COPILOT_PAT_6 }} | ||
| COPILOT_PAT_7: ${{ secrets.COPILOT_PAT_7 }} | ||
| COPILOT_PAT_8: ${{ secrets.COPILOT_PAT_8 }} | ||
| COPILOT_PAT_9: ${{ secrets.COPILOT_PAT_9 }} | ||
| RANDOM_SEED: ${{ github.aw.import-inputs.random_seed }} | ||
| shell: bash | ||
| pre_activation: | ||
| needs: fetch-binlog | ||
| runs-on: ubuntu-slim | ||
| environment: copilot-pat-pool | ||
| env: | ||
| GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} | ||
| outputs: | ||
| activated: ${{ 'true' }} | ||
| matched_command: '' | ||
| setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }} | ||
| setup-span-id: ${{ steps.setup.outputs.span-id }} | ||
| setup-trace-id: ${{ steps.setup.outputs.trace-id }} | ||
| steps: | ||
| - name: Setup Scripts | ||
| id: setup | ||
| uses: github/gh-aw-actions/setup@6aab9e5b5c91c615506061f09bedd81a23babe3c # v0.86.2 | ||
| with: | ||
| destination: ${{ runner.temp }}/gh-aw/actions | ||
| job-name: ${{ github.job }} | ||
| env: | ||
| GH_AW_SETUP_WORKFLOW_NAME: "Build Failure Analysis" | ||
| GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/build-failure-analysis.lock.yml@${{ github.ref }} | ||
| GH_AW_INFO_VERSION: "1.0.79" | ||
| GH_AW_INFO_AWF_VERSION: "v0.27.44" | ||
| GH_AW_INFO_ENGINE_ID: "copilot" | ||
| safe_outputs: | ||
| needs: | ||
| - activation | ||
| - agent | ||
| - detection | ||
| if: (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success' | ||
| runs-on: ubuntu-slim | ||
| environment: copilot-pat-pool | ||
| permissions: | ||
| issues: write | ||
| pull-requests: write | ||
| timeout-minutes: 45 | ||
| env: | ||
| GH_AW_AGENT_AIC: ${{ needs.agent.outputs.aic }} | ||
| GH_AW_AIC: ${{ needs.agent.outputs.aic }} | ||
| GH_AW_AMBIENT_CONTEXT: ${{ needs.agent.outputs.ambient_context }} | ||
| GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/build-failure-analysis" | ||
| GH_AW_DETECTION_CONCLUSION: ${{ needs.detection.outputs.detection_conclusion }} | ||
| GH_AW_DETECTION_REASON: ${{ needs.detection.outputs.detection_reason }} | ||
| GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens }} | ||
| GH_AW_ENGINE_ID: "copilot" | ||
| GH_AW_ENGINE_MODEL: ${{ needs.agent.outputs.model }} | ||
| GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} | ||
| GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 🤖 **Automated content by GitHub Copilot.** Generated by the [{workflow_name}]({agentic_workflow_url}) workflow.{ai_credits_suffix} · [◷]({history_link})\"}" | ||
| GH_AW_THREAT_DETECTION_AIC: ${{ needs.detection.outputs.aic }} | ||
| GH_AW_WORKFLOW_ID: "build-failure-analysis" | ||
| 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" | ||
| outputs: | ||
| code_push_failure_count: ${{ steps.process_safe_outputs.outputs.code_push_failure_count }} | ||
| code_push_failure_errors: ${{ steps.process_safe_outputs.outputs.code_push_failure_errors }} | ||
| comment_id: ${{ steps.process_safe_outputs.outputs.comment_id }} | ||
| comment_url: ${{ steps.process_safe_outputs.outputs.comment_url }} | ||
| create_discussion_error_count: ${{ steps.process_safe_outputs.outputs.create_discussion_error_count }} | ||
| create_discussion_errors: ${{ steps.process_safe_outputs.outputs.create_discussion_errors }} | ||
| process_safe_outputs_items_applied: ${{ steps.process_safe_outputs.outputs.items_applied }} | ||
| process_safe_outputs_items_cancelled: ${{ steps.process_safe_outputs.outputs.items_cancelled }} | ||
| process_safe_outputs_items_deferred: ${{ steps.process_safe_outputs.outputs.items_deferred }} | ||
| process_safe_outputs_items_failed: ${{ steps.process_safe_outputs.outputs.items_failed }} | ||
| process_safe_outputs_items_skipped: ${{ steps.process_safe_outputs.outputs.items_skipped }} | ||
| process_safe_outputs_items_succeeded: ${{ steps.process_safe_outputs.outputs.items_succeeded }} | ||
| process_safe_outputs_items_warnings: ${{ steps.process_safe_outputs.outputs.items_warnings }} | ||
| process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} | ||
| process_safe_outputs_status: ${{ steps.process_safe_outputs.outputs.status }} | ||
| process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} | ||
| steps: | ||
| - name: Setup Scripts | ||
| id: setup | ||
| uses: github/gh-aw-actions/setup@6aab9e5b5c91c615506061f09bedd81a23babe3c # v0.86.2 | ||
| with: | ||
| destination: ${{ runner.temp }}/gh-aw/actions | ||
| job-name: ${{ github.job }} | ||
| trace-id: ${{ needs.activation.outputs.setup-trace-id }} | ||
| parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} | ||
| env: | ||
| GH_AW_SETUP_WORKFLOW_NAME: "Build Failure Analysis" | ||
| GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/build-failure-analysis.lock.yml@${{ github.ref }} | ||
| GH_AW_INFO_VERSION: "1.0.79" | ||
| GH_AW_INFO_AWF_VERSION: "v0.27.44" | ||
| GH_AW_INFO_ENGINE_ID: "copilot" | ||
| - name: Download agent output artifact | ||
| id: download-agent-output | ||
| continue-on-error: true | ||
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | ||
| with: | ||
| name: agent | ||
| path: /tmp/gh-aw/ | ||
| - name: Setup agent output environment variable | ||
| id: setup-agent-output-env | ||
| if: steps.download-agent-output.outcome == 'success' | ||
| run: | | ||
| mkdir -p /tmp/gh-aw/ | ||
| find "/tmp/gh-aw/" -type f -print | ||
| echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT" | ||
| - name: Configure GH_HOST for enterprise compatibility | ||
| id: ghes-host-config | ||
| shell: bash | ||
| run: | # zizmor: ignore[github-env] - GITHUB_SERVER_URL is set by GitHub Actions, not user input. | ||
| # 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" | ||
| - name: Process Safe Outputs | ||
| id: process_safe_outputs | ||
| 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_COMMENT_ID: ${{ needs.activation.outputs.comment_id }} | ||
| 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\":5,\"target\":\"${{ github.event_name == 'workflow_dispatch' \\u0026\\u0026 inputs['pr-number'] || github.event.check_run.pull_requests[0].number }}\"},\"create_pull_request_review_comment\":{\"max\":25,\"side\":\"RIGHT\",\"target\":\"${{ github.event_name == 'workflow_dispatch' \\u0026\\u0026 inputs['pr-number'] || github.event.check_run.pull_requests[0].number }}\"},\"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: | | ||
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require('${{ runner.temp }}/gh-aw/actions/process_safe_outputs.cjs'); | ||
| await main(); | ||
| - name: Upload Safe Outputs Items | ||
| if: always() | ||
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | ||
| with: | ||
| name: safe-outputs-items | ||
| path: | | ||
| /tmp/gh-aw/safe-output-items.jsonl | ||
| /tmp/gh-aw/temporary-id-map.json | ||
| if-no-files-found: ignore | ||