From b92123fdf25d15252db8828ecb6e7292d0acc8b4 Mon Sep 17 00:00:00 2001 From: Shahzaib Date: Fri, 13 Mar 2026 10:28:35 -0700 Subject: [PATCH 1/7] Add workflow for CI feedback --- .../workflows/copilot-ci-feedback.lock.yml | 1175 +++++++++++++++++ .github/workflows/copilot-ci-feedback.md | 115 ++ 2 files changed, 1290 insertions(+) create mode 100644 .github/workflows/copilot-ci-feedback.lock.yml create mode 100644 .github/workflows/copilot-ci-feedback.md diff --git a/.github/workflows/copilot-ci-feedback.lock.yml b/.github/workflows/copilot-ci-feedback.lock.yml new file mode 100644 index 0000000000..bee63c9e8d --- /dev/null +++ b/.github/workflows/copilot-ci-feedback.lock.yml @@ -0,0 +1,1175 @@ +# +# ___ _ _ +# / _ \ | | (_) +# | |_| | __ _ ___ _ __ | |_ _ ___ +# | _ |/ _` |/ _ \ '_ \| __| |/ __| +# | | | | (_| | __/ | | | |_| | (__ +# \_| |_/\__, |\___|_| |_|\__|_|\___| +# __/ | +# _ _ |___/ +# | | | | / _| | +# | | | | ___ _ __ _ __| |_| | _____ ____ +# | |/\| |/ _ \ '__| |/ /| _| |/ _ \ \ /\ / / ___| +# \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ +# \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ +# +# This file was automatically generated by gh-aw (v0.58.0). DO NOT EDIT. +# +# 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/ +# +# Investigates CI failures on Copilot coding agent PRs and posts @copilot comments with specific errors so the agent can self-correct. +# +# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"c61683ac0cc10d1d7d2de8afae8472638ec9c03064eaa364fb4df472a251f737","compiler_version":"v0.58.0","strict":true} + +name: "Copilot CI Feedback" +"on": + check_suite: + types: + - completed + # skip-bots: # Skip-bots processed as bot check in pre-activation job + # - github-actions # Skip-bots processed as bot check in pre-activation job + # - copilot # Skip-bots processed as bot check in pre-activation job + workflow_dispatch: + +permissions: {} + +concurrency: + group: "gh-aw-${{ github.workflow }}" + +run-name: "Copilot CI Feedback" + +jobs: + activation: + needs: pre_activation + if: needs.pre_activation.outputs.activated == 'true' + runs-on: ubuntu-slim + permissions: + contents: read + outputs: + comment_id: "" + comment_repo: "" + model: ${{ steps.generate_aw_info.outputs.model }} + secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} + steps: + - name: Setup Scripts + uses: github/gh-aw/actions/setup@cb7966564184443e601bd6135d5fbb534300070e # v0.58.0 + with: + destination: /opt/gh-aw/actions + - 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 || '' }} + GH_AW_INFO_VERSION: "" + GH_AW_INFO_AGENT_VERSION: "latest" + GH_AW_INFO_CLI_VERSION: "v0.58.0" + GH_AW_INFO_WORKFLOW_NAME: "Copilot CI Feedback" + GH_AW_INFO_EXPERIMENTAL: "false" + GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" + GH_AW_INFO_STAGED: "false" + GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]' + GH_AW_INFO_FIREWALL_ENABLED: "true" + GH_AW_INFO_AWF_VERSION: "v0.23.0" + GH_AW_INFO_AWMG_VERSION: "" + GH_AW_INFO_FIREWALL_TYPE: "squid" + GH_AW_COMPILED_STRICT: "true" + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs'); + await main(core, context); + - name: Validate COPILOT_GITHUB_TOKEN secret + id: validate-secret + run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default + env: + COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + - name: Checkout .github and .agents folders + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + sparse-checkout: | + .github + .agents + sparse-checkout-cone-mode: true + fetch-depth: 1 + - name: Check workflow file timestamps + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_WORKFLOW_FILE: "copilot-ci-feedback.lock.yml" + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs'); + await main(); + - name: Create prompt with built-in context + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_GITHUB_ACTOR: ${{ github.actor }} + GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} + GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} + GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} + GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} + GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} + GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} + GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} + run: | + bash /opt/gh-aw/actions/create_prompt_first.sh + { + cat << 'GH_AW_PROMPT_EOF' + + GH_AW_PROMPT_EOF + cat "/opt/gh-aw/prompts/xpia.md" + cat "/opt/gh-aw/prompts/temp_folder_prompt.md" + cat "/opt/gh-aw/prompts/markdown.md" + cat "/opt/gh-aw/prompts/safe_outputs_prompt.md" + cat << 'GH_AW_PROMPT_EOF' + + Tools: add_comment, missing_tool, missing_data, noop + + + The following GitHub context information is available for this workflow: + {{#if __GH_AW_GITHUB_ACTOR__ }} + - **actor**: __GH_AW_GITHUB_ACTOR__ + {{/if}} + {{#if __GH_AW_GITHUB_REPOSITORY__ }} + - **repository**: __GH_AW_GITHUB_REPOSITORY__ + {{/if}} + {{#if __GH_AW_GITHUB_WORKSPACE__ }} + - **workspace**: __GH_AW_GITHUB_WORKSPACE__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ }} + - **issue-number**: #__GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ }} + - **discussion-number**: #__GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ }} + - **pull-request-number**: #__GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_COMMENT_ID__ }} + - **comment-id**: __GH_AW_GITHUB_EVENT_COMMENT_ID__ + {{/if}} + {{#if __GH_AW_GITHUB_RUN_ID__ }} + - **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__ + {{/if}} + + + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + {{#runtime-import .github/workflows/copilot-ci-feedback.md}} + GH_AW_PROMPT_EOF + } > "$GH_AW_PROMPT" + - name: Interpolate variables and render templates + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs'); + await main(); + - name: Substitute placeholders + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_GITHUB_ACTOR: ${{ github.actor }} + GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} + GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} + GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} + GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} + GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} + GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} + GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} + GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }} + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + + const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs'); + + // Call the substitution function + return await substitutePlaceholders({ + file: process.env.GH_AW_PROMPT, + substitutions: { + GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR, + GH_AW_GITHUB_EVENT_COMMENT_ID: process.env.GH_AW_GITHUB_EVENT_COMMENT_ID, + GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: process.env.GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER, + GH_AW_GITHUB_EVENT_ISSUE_NUMBER: process.env.GH_AW_GITHUB_EVENT_ISSUE_NUMBER, + GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: process.env.GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER, + 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_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED + } + }); + - name: Validate prompt placeholders + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh + - name: Print prompt + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + run: bash /opt/gh-aw/actions/print_prompt_summary.sh + - name: Upload activation artifact + if: success() + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + with: + name: activation + path: | + /tmp/gh-aw/aw_info.json + /tmp/gh-aw/aw-prompts/prompt.txt + retention-days: 1 + + agent: + needs: activation + runs-on: ubuntu-latest + permissions: + actions: read + checks: read + contents: read + pull-requests: read + concurrency: + group: "gh-aw-copilot-${{ github.workflow }}" + 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_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl + GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json + GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json + GH_AW_WORKFLOW_ID_SANITIZED: copilotcifeedback + outputs: + checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }} + detection_conclusion: ${{ steps.detection_conclusion.outputs.conclusion }} + detection_success: ${{ steps.detection_conclusion.outputs.success }} + has_patch: ${{ steps.collect_output.outputs.has_patch }} + inference_access_error: ${{ steps.detect-inference-error.outputs.inference_access_error || 'false' }} + model: ${{ needs.activation.outputs.model }} + output: ${{ steps.collect_output.outputs.output }} + output_types: ${{ steps.collect_output.outputs.output_types }} + steps: + - name: Setup Scripts + uses: github/gh-aw/actions/setup@cb7966564184443e601bd6135d5fbb534300070e # v0.58.0 + with: + destination: /opt/gh-aw/actions + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - name: Setup Node.js + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + with: + node-version: '24' + package-manager-cache: false + - name: Create gh-aw temp directory + run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh + - name: Configure Git credentials + env: + REPO_NAME: ${{ github.repository }} + SERVER_URL: ${{ github.server_url }} + run: | + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git config --global user.name "github-actions[bot]" + git config --global am.keepcr true + # Re-authenticate git with GitHub token + SERVER_URL_STRIPPED="${SERVER_URL#https://}" + git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" + echo "Git configured with standard GitHub Actions identity" + - name: Checkout PR branch + id: checkout-pr + if: | + (github.event.pull_request) || (github.event.issue.pull_request) + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + 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('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs'); + await main(); + - name: Install GitHub Copilot CLI + run: /opt/gh-aw/actions/install_copilot_cli.sh latest + - name: Install AWF binary + run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.23.0 + - name: Determine automatic lockdown mode for GitHub MCP Server + id: determine-automatic-lockdown + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + 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('/opt/gh-aw/actions/determine_automatic_lockdown.cjs'); + await determineAutomaticLockdown(github, context, core); + - name: Download container images + run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.23.0 ghcr.io/github/gh-aw-firewall/api-proxy:0.23.0 ghcr.io/github/gh-aw-firewall/squid:0.23.0 ghcr.io/github/gh-aw-mcpg:v0.1.14 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine + - name: Write Safe Outputs Config + run: | + mkdir -p /opt/gh-aw/safeoutputs + mkdir -p /tmp/gh-aw/safeoutputs + mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs + cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF' + {"add_comment":{"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}} + GH_AW_SAFE_OUTPUTS_CONFIG_EOF + - name: Write Safe Outputs Tools + run: | + cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF' + [ + { + "description": "Add a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. For creating new items, use create_issue, create_discussion, or create_pull_request instead. IMPORTANT: Comments are subject to validation constraints enforced by the MCP server - maximum 65536 characters for the complete comment (including footer which is added automatically), 10 mentions (@username), and 50 links. Exceeding these limits will result in an immediate error with specific guidance. NOTE: By default, this tool requires discussions:write permission. If your GitHub App lacks Discussions permission, set 'discussions: false' in the workflow's safe-outputs.add-comment configuration to exclude this permission. CONSTRAINTS: Maximum 1 comment(s) can be added.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "body": { + "description": "The comment text in Markdown format. This is the 'body' field - do not use 'comment_body' or other variations. Provide helpful, relevant information that adds value to the conversation. CONSTRAINTS: The complete comment (your body text + automatically added footer) must not exceed 65536 characters total. Maximum 10 mentions (@username), maximum 50 links (http/https URLs). A footer (~200-500 characters) is automatically appended with workflow attribution, so leave adequate space. If these limits are exceeded, the tool call will fail with a detailed error message indicating which constraint was violated.", + "type": "string" + }, + "integrity": { + "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").", + "type": "string" + }, + "item_number": { + "description": "The issue, pull request, or discussion number to comment on. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123). Can also be a temporary_id (e.g., 'aw_abc123') from a previously created issue in the same workflow run. If omitted, the tool auto-targets the issue, PR, or discussion that triggered this workflow. Auto-targeting only works for issue, pull_request, discussion, and comment event triggers — it does NOT work for schedule, workflow_dispatch, push, or workflow_run triggers. For those trigger types, always provide item_number explicitly, or the tool call will fail with an error.", + "type": [ + "number", + "string" + ] + }, + "secrecy": { + "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").", + "type": "string" + }, + "temporary_id": { + "description": "Unique temporary identifier for this comment. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Auto-generated if not provided. The temporary ID is returned in the tool response so you can reference this comment later.", + "pattern": "^aw_[A-Za-z0-9]{3,12}$", + "type": "string" + } + }, + "required": [ + "body" + ], + "type": "object" + }, + "name": "add_comment" + }, + { + "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "alternatives": { + "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).", + "type": "string" + }, + "integrity": { + "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").", + "type": "string" + }, + "reason": { + "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).", + "type": "string" + }, + "secrecy": { + "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").", + "type": "string" + }, + "tool": { + "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.", + "type": "string" + } + }, + "required": [ + "reason" + ], + "type": "object" + }, + "name": "missing_tool" + }, + { + "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "integrity": { + "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").", + "type": "string" + }, + "message": { + "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').", + "type": "string" + }, + "secrecy": { + "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").", + "type": "string" + } + }, + "required": [ + "message" + ], + "type": "object" + }, + "name": "noop" + }, + { + "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "alternatives": { + "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).", + "type": "string" + }, + "context": { + "description": "Additional context about the missing data or where it should come from (max 256 characters).", + "type": "string" + }, + "data_type": { + "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.", + "type": "string" + }, + "integrity": { + "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").", + "type": "string" + }, + "reason": { + "description": "Explanation of why this data is needed to complete the task (max 256 characters).", + "type": "string" + }, + "secrecy": { + "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").", + "type": "string" + } + }, + "required": [], + "type": "object" + }, + "name": "missing_data" + } + ] + GH_AW_SAFE_OUTPUTS_TOOLS_EOF + cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF' + { + "add_comment": { + "defaultMax": 1, + "fields": { + "body": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 65000 + }, + "item_number": { + "issueOrPRNumber": true + }, + "repo": { + "type": "string", + "maxLength": 256 + } + } + }, + "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 + } + } + } + } + GH_AW_SAFE_OUTPUTS_VALIDATION_EOF + - name: Generate Safe Outputs MCP Server Config + id: safe-outputs-config + run: | + # Generate a secure random API key (360 bits of entropy, 40+ chars) + # Mask immediately to prevent timing vulnerabilities + API_KEY=$(openssl rand -base64 45 | tr -d '/+=') + echo "::add-mask::${API_KEY}" + + PORT=3001 + + # Set outputs for next steps + { + echo "safe_outputs_api_key=${API_KEY}" + echo "safe_outputs_port=${PORT}" + } >> "$GITHUB_OUTPUT" + + echo "Safe Outputs MCP server will run on port ${PORT}" + + - name: Start Safe Outputs MCP HTTP Server + id: safe-outputs-start + env: + DEBUG: '*' + GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }} + GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }} + GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json + GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json + GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs + run: | + # Environment variables are set above to prevent template injection + export DEBUG + export GH_AW_SAFE_OUTPUTS_PORT + export GH_AW_SAFE_OUTPUTS_API_KEY + export GH_AW_SAFE_OUTPUTS_TOOLS_PATH + export GH_AW_SAFE_OUTPUTS_CONFIG_PATH + export GH_AW_MCP_LOG_DIR + + bash /opt/gh-aw/actions/start_safe_outputs_server.sh + + - name: Start MCP Gateway + id: start-mcp-gateway + env: + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }} + GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }} + GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + run: | + set -eo pipefail + mkdir -p /tmp/gh-aw/mcp-config + + # Export gateway environment variables for MCP config and gateway script + export MCP_GATEWAY_PORT="80" + export MCP_GATEWAY_DOMAIN="host.docker.internal" + 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 DEBUG="*" + + export GH_AW_ENGINE="copilot" + export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/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 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_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_LOCKDOWN -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 GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -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 ghcr.io/github/gh-aw-mcpg:v0.1.14' + + mkdir -p /home/runner/.copilot + cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh + { + "mcpServers": { + "ado": { + "type": "stdio", + "container": "node:lts-alpine", + "entrypoint": "npx", + "entrypointArgs": [ + "npx", + "-y", + "@azure-devops/mcp", + "IdentityDivision", + "-d", + "pipelines" + ], + "tools": [ + "*" + ] + }, + "github": { + "type": "stdio", + "container": "ghcr.io/github/github-mcp-server:v0.32.0", + "env": { + "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN", + "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}", + "GITHUB_READ_ONLY": "1", + "GITHUB_TOOLSETS": "pull_requests" + } + }, + "safeoutputs": { + "type": "http", + "url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT", + "headers": { + "Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}" + } + } + }, + "gateway": { + "port": $MCP_GATEWAY_PORT, + "domain": "${MCP_GATEWAY_DOMAIN}", + "apiKey": "${MCP_GATEWAY_API_KEY}", + "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}" + } + } + GH_AW_MCP_CONFIG_EOF + - name: Download activation artifact + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: activation + path: /tmp/gh-aw + - name: Clean git credentials + run: bash /opt/gh-aw/actions/clean_git_credentials.sh + - name: Execute GitHub Copilot CLI + id: agentic_execution + # Copilot CLI tool arguments (sorted): + # --allow-tool ado + # --allow-tool ado(*) + # --allow-tool github + # --allow-tool safeoutputs + # --allow-tool shell(cat) + # --allow-tool shell(curl) + # --allow-tool shell(date) + # --allow-tool shell(echo) + # --allow-tool shell(grep) + # --allow-tool shell(head) + # --allow-tool shell(jq) + # --allow-tool shell(ls) + # --allow-tool shell(pwd) + # --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: 20 + run: | + set -o pipefail + touch /tmp/gh-aw/agent-step-summary.md + # shellcheck disable=SC1003 + sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,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,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,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,packagecloud.io,packages.cloud.google.com,packages.microsoft.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" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.23.0 --skip-pull --enable-api-proxy \ + -- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool ado --allow-tool '\''ado(*)'\'' --allow-tool github --allow-tool safeoutputs --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(curl)'\'' --allow-tool '\''shell(date)'\'' --allow-tool '\''shell(echo)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(pwd)'\'' --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 --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log + env: + COPILOT_AGENT_RUNNER_TYPE: STANDALONE + COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + COPILOT_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || '' }} + GH_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json + GH_AW_PHASE: agent + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_VERSION: v0.58.0 + GITHUB_API_URL: ${{ github.api_url }} + GITHUB_AW: true + 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] + XDG_CONFIG_HOME: /home/runner + - name: Detect inference access error + id: detect-inference-error + if: always() + continue-on-error: true + run: bash /opt/gh-aw/actions/detect_inference_access_error.sh + - name: Configure Git credentials + env: + REPO_NAME: ${{ github.repository }} + SERVER_URL: ${{ github.server_url }} + run: | + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git config --global user.name "github-actions[bot]" + git config --global am.keepcr true + # Re-authenticate git with GitHub token + SERVER_URL_STRIPPED="${SERVER_URL#https://}" + git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" + echo "Git configured with standard GitHub Actions identity" + - name: Copy Copilot session state files to logs + if: always() + continue-on-error: true + run: | + # Copy Copilot session state files to logs folder for artifact collection + # This ensures they are in /tmp/gh-aw/ where secret redaction can scan them + SESSION_STATE_DIR="$HOME/.copilot/session-state" + LOGS_DIR="/tmp/gh-aw/sandbox/agent/logs" + + if [ -d "$SESSION_STATE_DIR" ]; then + echo "Copying Copilot session state files from $SESSION_STATE_DIR to $LOGS_DIR" + mkdir -p "$LOGS_DIR" + cp -v "$SESSION_STATE_DIR"/*.jsonl "$LOGS_DIR/" 2>/dev/null || true + echo "Session state files copied successfully" + else + echo "No session-state directory found at $SESSION_STATE_DIR" + fi + - 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 /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID" + - name: Redact secrets in logs + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs'); + await main(); + env: + GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN' + SECRET_COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + 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 /opt/gh-aw/actions/append_agent_step_summary.sh + - name: Copy Safe Outputs + if: always() + 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@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,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,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,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,packagecloud.io,packages.cloud.google.com,packages.microsoft.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" + GITHUB_SERVER_URL: ${{ github.server_url }} + GITHUB_API_URL: ${{ github.api_url }} + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs'); + await main(); + - name: Parse agent logs for step summary + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/ + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs'); + await main(); + - name: Parse MCP Gateway logs for step summary + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/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: | + # Fix permissions on firewall logs so they can be uploaded as artifacts + # AWF runs with sudo, creating files owned by root + sudo chmod -R a+r /tmp/gh-aw/sandbox/firewall/logs 2>/dev/null || true + # Only run awf logs summary if awf command exists (it may not be installed if workflow failed before install step) + if command -v awf &> /dev/null; then + awf logs summary | tee -a "$GITHUB_STEP_SUMMARY" + else + echo 'AWF binary not installed, skipping firewall log summary' + fi + - name: Upload agent artifacts + if: always() + continue-on-error: true + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + 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/sandbox/firewall/logs/ + /tmp/gh-aw/agent-stdio.log + /tmp/gh-aw/agent/ + /tmp/gh-aw/safeoutputs.jsonl + /tmp/gh-aw/agent_output.json + if-no-files-found: ignore + # --- Threat Detection (inline) --- + - name: Check if detection needed + id: detection_guard + if: always() + env: + OUTPUT_TYPES: ${{ steps.collect_output.outputs.output_types }} + HAS_PATCH: ${{ steps.collect_output.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 configuration for detection + if: always() && steps.detection_guard.outputs.run_detection == 'true' + run: | + rm -f /tmp/gh-aw/mcp-config/mcp-servers.json + rm -f /home/runner/.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: | + mkdir -p /tmp/gh-aw/threat-detection/aw-prompts + cp /tmp/gh-aw/aw-prompts/prompt.txt /tmp/gh-aw/threat-detection/aw-prompts/prompt.txt 2>/dev/null || true + cp /tmp/gh-aw/agent_output.json /tmp/gh-aw/threat-detection/agent_output.json 2>/dev/null || true + for f in /tmp/gh-aw/aw-*.patch; do + [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true + done + echo "Prepared threat detection files:" + ls -la /tmp/gh-aw/threat-detection/ 2>/dev/null || true + - name: Setup threat detection + if: always() && steps.detection_guard.outputs.run_detection == 'true' + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + WORKFLOW_NAME: "Copilot CI Feedback" + WORKFLOW_DESCRIPTION: "Investigates CI failures on Copilot coding agent PRs and posts @copilot comments with specific errors so the agent can self-correct." + HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }} + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/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 + - name: Execute GitHub Copilot CLI + if: always() && steps.detection_guard.outputs.run_detection == 'true' + id: detection_agentic_execution + # Copilot CLI tool arguments (sorted): + # --allow-tool shell(cat) + # --allow-tool shell(grep) + # --allow-tool shell(head) + # --allow-tool shell(jq) + # --allow-tool shell(ls) + # --allow-tool shell(tail) + # --allow-tool shell(wc) + timeout-minutes: 20 + run: | + set -o pipefail + touch /tmp/gh-aw/agent-step-summary.md + # shellcheck disable=SC1003 + sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.23.0 --skip-pull --enable-api-proxy \ + -- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log + env: + COPILOT_AGENT_RUNNER_TYPE: STANDALONE + COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + COPILOT_MODEL: ${{ vars.GH_AW_MODEL_DETECTION_COPILOT || '' }} + GH_AW_PHASE: detection + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_VERSION: v0.58.0 + GITHUB_API_URL: ${{ github.api_url }} + GITHUB_AW: true + GITHUB_HEAD_REF: ${{ github.head_ref }} + 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] + XDG_CONFIG_HOME: /home/runner + - name: Parse threat detection results + id: parse_detection_results + if: always() && steps.detection_guard.outputs.run_detection == 'true' + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs'); + await main(); + - name: Upload threat detection log + if: always() && steps.detection_guard.outputs.run_detection == 'true' + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + with: + name: detection + path: /tmp/gh-aw/threat-detection/detection.log + if-no-files-found: ignore + - name: Set detection conclusion + id: detection_conclusion + if: always() + env: + RUN_DETECTION: ${{ steps.detection_guard.outputs.run_detection }} + DETECTION_SUCCESS: ${{ steps.parse_detection_results.outputs.success }} + run: | + if [[ "$RUN_DETECTION" != "true" ]]; then + echo "conclusion=skipped" >> "$GITHUB_OUTPUT" + echo "success=true" >> "$GITHUB_OUTPUT" + echo "Detection was not needed, marking as skipped" + elif [[ "$DETECTION_SUCCESS" == "true" ]]; then + echo "conclusion=success" >> "$GITHUB_OUTPUT" + echo "success=true" >> "$GITHUB_OUTPUT" + echo "Detection passed successfully" + else + echo "conclusion=failure" >> "$GITHUB_OUTPUT" + echo "success=false" >> "$GITHUB_OUTPUT" + echo "Detection found issues" + fi + + conclusion: + needs: + - activation + - agent + - safe_outputs + if: (always()) && (needs.agent.result != 'skipped') + runs-on: ubuntu-slim + permissions: + contents: read + discussions: write + issues: write + pull-requests: write + concurrency: + group: "gh-aw-conclusion-copilot-ci-feedback" + cancel-in-progress: false + outputs: + 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 + uses: github/gh-aw/actions/setup@cb7966564184443e601bd6135d5fbb534300070e # v0.58.0 + with: + destination: /opt/gh-aw/actions + - 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 + 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_ENV" + - name: Process No-Op Messages + id: noop + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_NOOP_MAX: "1" + GH_AW_WORKFLOW_NAME: "Copilot CI Feedback" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/noop.cjs'); + await main(); + - name: Record Missing Tool + id: missing_tool + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_WORKFLOW_NAME: "Copilot CI Feedback" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/missing_tool.cjs'); + await main(); + - name: Handle Agent Failure + id: handle_agent_failure + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_WORKFLOW_NAME: "Copilot CI Feedback" + 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: "copilot-ci-feedback" + GH_AW_SECRET_VERIFICATION_RESULT: ${{ needs.activation.outputs.secret_verification_result }} + GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }} + GH_AW_INFERENCE_ACCESS_ERROR: ${{ needs.agent.outputs.inference_access_error }} + GH_AW_GROUP_REPORTS: "false" + GH_AW_FAILURE_REPORT_AS_ISSUE: "true" + GH_AW_TIMEOUT_MINUTES: "20" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs'); + await main(); + - name: Handle No-Op Message + id: handle_noop_message + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_WORKFLOW_NAME: "Copilot CI Feedback" + GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} + GH_AW_NOOP_MESSAGE: ${{ steps.noop.outputs.noop_message }} + GH_AW_NOOP_REPORT_AS_ISSUE: "true" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs'); + await main(); + + pre_activation: + runs-on: ubuntu-slim + outputs: + activated: ${{ (steps.check_membership.outputs.is_team_member == 'true') && (steps.check_skip_bots.outputs.skip_bots_ok == 'true') }} + matched_command: '' + steps: + - name: Setup Scripts + uses: github/gh-aw/actions/setup@cb7966564184443e601bd6135d5fbb534300070e # v0.58.0 + with: + destination: /opt/gh-aw/actions + - name: Check team membership for workflow + id: check_membership + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_REQUIRED_ROLES: admin,maintainer,write + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/check_membership.cjs'); + await main(); + - name: Check skip-bots + id: check_skip_bots + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_SKIP_BOTS: github-actions,copilot + GH_AW_WORKFLOW_NAME: "Copilot CI Feedback" + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/check_skip_bots.cjs'); + await main(); + + safe_outputs: + needs: agent + if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (needs.agent.outputs.detection_success == 'true') + runs-on: ubuntu-slim + permissions: + contents: read + discussions: write + issues: write + pull-requests: write + timeout-minutes: 15 + env: + GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/copilot-ci-feedback" + GH_AW_ENGINE_ID: "copilot" + GH_AW_WORKFLOW_ID: "copilot-ci-feedback" + GH_AW_WORKFLOW_NAME: "Copilot CI Feedback" + 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_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} + steps: + - name: Setup Scripts + uses: github/gh-aw/actions/setup@cb7966564184443e601bd6135d5fbb534300070e # v0.58.0 + with: + destination: /opt/gh-aw/actions + - 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 + 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_ENV" + - name: Process Safe Outputs + id: process_safe_outputs + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,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,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,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,packagecloud.io,packages.cloud.google.com,packages.microsoft.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" + GITHUB_SERVER_URL: ${{ github.server_url }} + GITHUB_API_URL: ${{ github.api_url }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1},\"missing_data\":{},\"missing_tool\":{}}" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs'); + await main(); + - name: Upload Safe Output Items Manifest + if: always() + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + with: + name: safe-output-items + path: /tmp/safe-output-items.jsonl + if-no-files-found: warn + diff --git a/.github/workflows/copilot-ci-feedback.md b/.github/workflows/copilot-ci-feedback.md new file mode 100644 index 0000000000..2884b12f3c --- /dev/null +++ b/.github/workflows/copilot-ci-feedback.md @@ -0,0 +1,115 @@ +--- +description: "Investigates CI failures on Copilot coding agent PRs and posts @copilot comments with specific errors so the agent can self-correct." +on: + check_suite: + types: [completed] + workflow_dispatch: + skip-bots: [github-actions, copilot] + +engine: copilot + +permissions: + contents: read + checks: read + actions: read + pull-requests: read + +safe-outputs: + add-comment: + pull-requests: true + +mcp-servers: + ado: + command: "npx" + args: ["-y", "@azure-devops/mcp", "IdentityDivision", "-d", "pipelines"] + allowed: ["*"] + +tools: + github: + toolsets: [pull_requests] + bash: ["curl", "jq"] +--- + +# Copilot CI Feedback + +When CI checks fail on a PR created by the Copilot coding agent (`copilot-swe-agent[bot]`), +investigate the failures and post a `@copilot` comment with specific, actionable error +messages so the coding agent can fix them. + +## When to Act + +Only act when ALL of these are true: +1. The `check_suite` completed with `failure` conclusion +2. The PR branch starts with `copilot/` +3. The PR author is `copilot-swe-agent[bot]` or `app/copilot-swe-agent` +4. No feedback comment was already posted in the last 15 minutes (avoid spam) + +If any condition is not met, exit without posting. + +## Investigation Process + +### Step 1: Find the PR + +Use the GitHub tools to find the open PR associated with the failing check suite's +head branch. + +### Step 2: Identify Failing Checks + +List all check runs on the PR. For each failed check: +- Note the check name and conclusion +- Note the `details_url` — this contains the ADO build URL for pipeline checks + +### Step 3: Get Error Details + +**For GitHub Actions checks** (changelog, validate-pr-ab-id, etc.): +- Read the check run annotations for error messages + +**For Azure DevOps Pipeline checks** (Common - Build & Test, Assemble consumers, etc.): +- Parse the `details_url` to extract the ADO `buildId` + (e.g., from `https://identitydivision.visualstudio.com/.../buildId=1602697` extract `1602697`) +- Use the ADO MCP server to query the build timeline and find failing tasks +- Fetch the log content for failing tasks +- Extract the actual error lines (look for patterns like `e: file:///...`, `error:`, `FAILURE:`) + +### Step 4: Categorize and Summarize + +Group errors into categories: +- **Compilation errors**: Type mismatches, unresolved references, missing imports +- **Missing changelog**: The `changelog.txt` or `changes.txt` wasn't updated +- **Lint/SpotBugs**: Static analysis findings +- **Test failures**: Unit or integration test failures +- **Consumer build failures**: Downstream repos (MSAL, Broker) can't compile against changes + +For each error, include: +- The exact file and line number +- The full error message +- A brief suggestion of what to fix (based on the error type) + +### Step 5: Post Comment + +Post a single comment on the PR with this format: + +``` +@copilot CI checks failed on this PR. Please fix the following issues and push an update. + +## Compilation Errors +- `AuthTabManager.kt:69` — Type mismatch: expected `ActivityResultLauncher`, got `ActivityResultLauncher`. Check the AndroidX Browser 1.9.0 API — `AuthTabIntent.registerActivityResultLauncher()` returns `ActivityResultLauncher`, not ``. + +## Missing Changelog +- Add an entry to `changelog.txt` describing your changes. Format: `- [MINOR] Description (#PR_NUMBER)` + +## Other +- [any other errors with specific file:line and messages] +``` + +## Important Rules + +- **Be specific**: Include exact file paths, line numbers, and error messages. The coding agent + cannot click links or browse ADO — it needs the error text inline. +- **Don't fix the code yourself**: Your job is to report errors, not create PRs. The coding + agent will fix them. +- **Don't post if recently posted**: Check the last 5 comments on the PR. If one contains + `@copilot` and `CI checks failed` from within the last 15 minutes, skip posting. +- **Don't post on non-Copilot PRs**: Only act on PRs from `copilot-swe-agent[bot]`. +- **Include ADO build links**: For reference, include the ADO build URL so humans can + also investigate if needed. From bbed761a61b07777fea3747b9192b5e53e5a12ae Mon Sep 17 00:00:00 2001 From: Shahzaib Date: Fri, 13 Mar 2026 10:56:37 -0700 Subject: [PATCH 2/7] Address review: add if guard, restrict ADO MCP tools, disable discussions --- .../workflows/copilot-ci-feedback.lock.yml | 23 +++++++++++++------ .github/workflows/copilot-ci-feedback.md | 12 +++++++++- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/.github/workflows/copilot-ci-feedback.lock.yml b/.github/workflows/copilot-ci-feedback.lock.yml index bee63c9e8d..59a7f4e4fe 100644 --- a/.github/workflows/copilot-ci-feedback.lock.yml +++ b/.github/workflows/copilot-ci-feedback.lock.yml @@ -23,7 +23,7 @@ # # Investigates CI failures on Copilot coding agent PRs and posts @copilot comments with specific errors so the agent can self-correct. # -# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"c61683ac0cc10d1d7d2de8afae8472638ec9c03064eaa364fb4df472a251f737","compiler_version":"v0.58.0","strict":true} +# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"eed88d61768626ea449698052912d3802c928fd907fd671fc5bb08c27fc848a9","compiler_version":"v0.58.0","strict":true} name: "Copilot CI Feedback" "on": @@ -45,7 +45,9 @@ run-name: "Copilot CI Feedback" jobs: activation: needs: pre_activation - if: needs.pre_activation.outputs.activated == 'true' + if: > + (needs.pre_activation.outputs.activated == 'true') && (github.event_name == 'workflow_dispatch' || (github.event.check_suite.conclusion == 'failure' && + startsWith(github.event.check_suite.head_branch, 'copilot/'))) runs-on: ubuntu-slim permissions: contents: read @@ -623,7 +625,10 @@ jobs: "pipelines" ], "tools": [ - "*" + "get_build_status", + "get_build_log", + "get_build_log_by_id", + "get_builds" ] }, "github": { @@ -663,7 +668,10 @@ jobs: id: agentic_execution # Copilot CLI tool arguments (sorted): # --allow-tool ado - # --allow-tool ado(*) + # --allow-tool ado(get_build_log) + # --allow-tool ado(get_build_log_by_id) + # --allow-tool ado(get_build_status) + # --allow-tool ado(get_builds) # --allow-tool github # --allow-tool safeoutputs # --allow-tool shell(cat) @@ -687,7 +695,7 @@ jobs: touch /tmp/gh-aw/agent-step-summary.md # shellcheck disable=SC1003 sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,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,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,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,packagecloud.io,packages.cloud.google.com,packages.microsoft.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" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.23.0 --skip-pull --enable-api-proxy \ - -- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool ado --allow-tool '\''ado(*)'\'' --allow-tool github --allow-tool safeoutputs --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(curl)'\'' --allow-tool '\''shell(date)'\'' --allow-tool '\''shell(echo)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(pwd)'\'' --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 --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log + -- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool ado --allow-tool '\''ado(get_build_log)'\'' --allow-tool '\''ado(get_build_log_by_id)'\'' --allow-tool '\''ado(get_build_status)'\'' --allow-tool '\''ado(get_builds)'\'' --allow-tool github --allow-tool safeoutputs --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(curl)'\'' --allow-tool '\''shell(date)'\'' --allow-tool '\''shell(echo)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(pwd)'\'' --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 --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log env: COPILOT_AGENT_RUNNER_TYPE: STANDALONE COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} @@ -977,7 +985,6 @@ jobs: runs-on: ubuntu-slim permissions: contents: read - discussions: write issues: write pull-requests: write concurrency: @@ -1073,6 +1080,9 @@ jobs: await main(); pre_activation: + if: > + github.event_name == 'workflow_dispatch' || (github.event.check_suite.conclusion == 'failure' && + startsWith(github.event.check_suite.head_branch, 'copilot/')) runs-on: ubuntu-slim outputs: activated: ${{ (steps.check_membership.outputs.is_team_member == 'true') && (steps.check_skip_bots.outputs.skip_bots_ok == 'true') }} @@ -1113,7 +1123,6 @@ jobs: runs-on: ubuntu-slim permissions: contents: read - discussions: write issues: write pull-requests: write timeout-minutes: 15 diff --git a/.github/workflows/copilot-ci-feedback.md b/.github/workflows/copilot-ci-feedback.md index 2884b12f3c..3b73e18f03 100644 --- a/.github/workflows/copilot-ci-feedback.md +++ b/.github/workflows/copilot-ci-feedback.md @@ -6,6 +6,11 @@ on: workflow_dispatch: skip-bots: [github-actions, copilot] +if: > + github.event_name == 'workflow_dispatch' || + (github.event.check_suite.conclusion == 'failure' && + startsWith(github.event.check_suite.head_branch, 'copilot/')) + engine: copilot permissions: @@ -17,12 +22,17 @@ permissions: safe-outputs: add-comment: pull-requests: true + discussions: false mcp-servers: ado: command: "npx" args: ["-y", "@azure-devops/mcp", "IdentityDivision", "-d", "pipelines"] - allowed: ["*"] + allowed: + - "get_build_status" + - "get_build_log" + - "get_build_log_by_id" + - "get_builds" tools: github: From 7446b1cd3603be7c15fd7904b3594e3ce92fab49 Mon Sep 17 00:00:00 2001 From: Shahzaib Date: Fri, 13 Mar 2026 11:07:18 -0700 Subject: [PATCH 3/7] Address review round 2: pin MCP version, remove copilot from skip-bots, fix changelog ref, consistent author --- .github/workflows/copilot-ci-feedback.lock.yml | 7 +++---- .github/workflows/copilot-ci-feedback.md | 8 ++++---- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/.github/workflows/copilot-ci-feedback.lock.yml b/.github/workflows/copilot-ci-feedback.lock.yml index 59a7f4e4fe..7395da95c9 100644 --- a/.github/workflows/copilot-ci-feedback.lock.yml +++ b/.github/workflows/copilot-ci-feedback.lock.yml @@ -23,7 +23,7 @@ # # Investigates CI failures on Copilot coding agent PRs and posts @copilot comments with specific errors so the agent can self-correct. # -# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"eed88d61768626ea449698052912d3802c928fd907fd671fc5bb08c27fc848a9","compiler_version":"v0.58.0","strict":true} +# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"deb5f57ad12407792807c1eda79fdb841399143fb819ba05f087b6fcf60e39f5","compiler_version":"v0.58.0","strict":true} name: "Copilot CI Feedback" "on": @@ -32,7 +32,6 @@ name: "Copilot CI Feedback" - completed # skip-bots: # Skip-bots processed as bot check in pre-activation job # - github-actions # Skip-bots processed as bot check in pre-activation job - # - copilot # Skip-bots processed as bot check in pre-activation job workflow_dispatch: permissions: {} @@ -619,7 +618,7 @@ jobs: "entrypointArgs": [ "npx", "-y", - "@azure-devops/mcp", + "@azure-devops/mcp@2.4.0", "IdentityDivision", "-d", "pipelines" @@ -1108,7 +1107,7 @@ jobs: id: check_skip_bots uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: - GH_AW_SKIP_BOTS: github-actions,copilot + GH_AW_SKIP_BOTS: github-actions GH_AW_WORKFLOW_NAME: "Copilot CI Feedback" with: script: | diff --git a/.github/workflows/copilot-ci-feedback.md b/.github/workflows/copilot-ci-feedback.md index 3b73e18f03..1a529109e6 100644 --- a/.github/workflows/copilot-ci-feedback.md +++ b/.github/workflows/copilot-ci-feedback.md @@ -4,7 +4,7 @@ on: check_suite: types: [completed] workflow_dispatch: - skip-bots: [github-actions, copilot] + skip-bots: [github-actions] if: > github.event_name == 'workflow_dispatch' || @@ -27,7 +27,7 @@ safe-outputs: mcp-servers: ado: command: "npx" - args: ["-y", "@azure-devops/mcp", "IdentityDivision", "-d", "pipelines"] + args: ["-y", "@azure-devops/mcp@2.4.0", "IdentityDivision", "-d", "pipelines"] allowed: - "get_build_status" - "get_build_log" @@ -51,7 +51,7 @@ messages so the coding agent can fix them. Only act when ALL of these are true: 1. The `check_suite` completed with `failure` conclusion 2. The PR branch starts with `copilot/` -3. The PR author is `copilot-swe-agent[bot]` or `app/copilot-swe-agent` +3. The PR author is `copilot-swe-agent[bot]` 4. No feedback comment was already posted in the last 15 minutes (avoid spam) If any condition is not met, exit without posting. @@ -85,7 +85,7 @@ List all check runs on the PR. For each failed check: Group errors into categories: - **Compilation errors**: Type mismatches, unresolved references, missing imports -- **Missing changelog**: The `changelog.txt` or `changes.txt` wasn't updated +- **Missing changelog**: The `changelog.txt` wasn't updated - **Lint/SpotBugs**: Static analysis findings - **Test failures**: Unit or integration test failures - **Consumer build failures**: Downstream repos (MSAL, Broker) can't compile against changes From f4e33a15b7e7134b8b57ac66ccaaa05315e86f60 Mon Sep 17 00:00:00 2001 From: Shahzaib Date: Fri, 13 Mar 2026 11:27:43 -0700 Subject: [PATCH 4/7] Address review round 3: workflow_dispatch inputs, explicit PR number, author verification --- .../workflows/copilot-ci-feedback.lock.yml | 10 ++++++- .github/workflows/copilot-ci-feedback.md | 26 +++++++++++++++---- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/.github/workflows/copilot-ci-feedback.lock.yml b/.github/workflows/copilot-ci-feedback.lock.yml index 7395da95c9..0209cfded9 100644 --- a/.github/workflows/copilot-ci-feedback.lock.yml +++ b/.github/workflows/copilot-ci-feedback.lock.yml @@ -23,7 +23,7 @@ # # Investigates CI failures on Copilot coding agent PRs and posts @copilot comments with specific errors so the agent can self-correct. # -# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"deb5f57ad12407792807c1eda79fdb841399143fb819ba05f087b6fcf60e39f5","compiler_version":"v0.58.0","strict":true} +# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"a5d82d5e7adc8f44bf4d9c6a5a286d85aa2743f50aa95d8b2eee79061ff38e6d","compiler_version":"v0.58.0","strict":true} name: "Copilot CI Feedback" "on": @@ -33,6 +33,10 @@ name: "Copilot CI Feedback" # skip-bots: # Skip-bots processed as bot check in pre-activation job # - github-actions # Skip-bots processed as bot check in pre-activation job workflow_dispatch: + inputs: + pr_number: + description: PR number to investigate (for manual testing) + required: false permissions: {} @@ -115,6 +119,7 @@ jobs: GH_AW_GITHUB_ACTOR: ${{ github.actor }} GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} + GH_AW_GITHUB_EVENT_INPUTS_PR_NUMBER: ${{ github.event.inputs.pr_number }} GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} @@ -174,6 +179,7 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_GITHUB_EVENT_INPUTS_PR_NUMBER: ${{ github.event.inputs.pr_number }} with: script: | const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); @@ -187,6 +193,7 @@ jobs: GH_AW_GITHUB_ACTOR: ${{ github.actor }} GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} + GH_AW_GITHUB_EVENT_INPUTS_PR_NUMBER: ${{ github.event.inputs.pr_number }} GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} @@ -207,6 +214,7 @@ jobs: GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR, GH_AW_GITHUB_EVENT_COMMENT_ID: process.env.GH_AW_GITHUB_EVENT_COMMENT_ID, GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: process.env.GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER, + GH_AW_GITHUB_EVENT_INPUTS_PR_NUMBER: process.env.GH_AW_GITHUB_EVENT_INPUTS_PR_NUMBER, GH_AW_GITHUB_EVENT_ISSUE_NUMBER: process.env.GH_AW_GITHUB_EVENT_ISSUE_NUMBER, GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: process.env.GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER, GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY, diff --git a/.github/workflows/copilot-ci-feedback.md b/.github/workflows/copilot-ci-feedback.md index 1a529109e6..90a184d6a5 100644 --- a/.github/workflows/copilot-ci-feedback.md +++ b/.github/workflows/copilot-ci-feedback.md @@ -4,6 +4,10 @@ on: check_suite: types: [completed] workflow_dispatch: + inputs: + pr_number: + description: "PR number to investigate (for manual testing)" + required: false skip-bots: [github-actions] if: > @@ -48,20 +52,28 @@ messages so the coding agent can fix them. ## When to Act -Only act when ALL of these are true: +**For `check_suite` trigger**: Only act when ALL of these are true: 1. The `check_suite` completed with `failure` conclusion 2. The PR branch starts with `copilot/` -3. The PR author is `copilot-swe-agent[bot]` +3. The PR author is `copilot-swe-agent[bot]` — verify this in Step 1 after finding the PR 4. No feedback comment was already posted in the last 15 minutes (avoid spam) If any condition is not met, exit without posting. +**For `workflow_dispatch` trigger**: Use the `pr_number` input to specify which PR to investigate. +If no `pr_number` is provided, exit without posting. + ## Investigation Process -### Step 1: Find the PR +### Step 1: Find the PR and Verify Author +**For `check_suite` events**: Use the GitHub tools to find the open PR associated with the failing check suite's -head branch. +head branch. Then verify the PR author is `copilot-swe-agent[bot]`. If the author is +anyone else, exit immediately — this workflow only acts on Copilot agent PRs. + +**For `workflow_dispatch` events**: +Use the `pr_number` input (${{ github.event.inputs.pr_number }}) to look up the PR directly. ### Step 2: Identify Failing Checks @@ -97,7 +109,11 @@ For each error, include: ### Step 5: Post Comment -Post a single comment on the PR with this format: +Post a single comment on the PR. **You MUST specify the PR number explicitly** using the +`item_number` parameter when calling the comment tool — the `check_suite` trigger does not +automatically provide PR context. + +Use the PR number resolved in Step 1. Format: ``` @copilot CI checks failed on this PR. Please fix the following issues and push an update. From 78edd64e1b974ef1ac8d4b15281eb6dac1661021 Mon Sep 17 00:00:00 2001 From: Shahzaib Date: Fri, 13 Mar 2026 11:36:12 -0700 Subject: [PATCH 5/7] Address review round 4: required pr_number, issues:read, env vars for injection safety, author pre-check step --- .../workflows/copilot-ci-feedback.lock.yml | 44 +++++++------------ .github/workflows/copilot-ci-feedback.md | 37 ++++++++++++++-- 2 files changed, 49 insertions(+), 32 deletions(-) diff --git a/.github/workflows/copilot-ci-feedback.lock.yml b/.github/workflows/copilot-ci-feedback.lock.yml index 0209cfded9..6a186621ea 100644 --- a/.github/workflows/copilot-ci-feedback.lock.yml +++ b/.github/workflows/copilot-ci-feedback.lock.yml @@ -23,7 +23,7 @@ # # Investigates CI failures on Copilot coding agent PRs and posts @copilot comments with specific errors so the agent can self-correct. # -# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"a5d82d5e7adc8f44bf4d9c6a5a286d85aa2743f50aa95d8b2eee79061ff38e6d","compiler_version":"v0.58.0","strict":true} +# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"333142823bdac43590f423e471ffdb5155aed166792dd4ee67faedbdd947f8b7","compiler_version":"v0.58.0","strict":true} name: "Copilot CI Feedback" "on": @@ -35,8 +35,8 @@ name: "Copilot CI Feedback" workflow_dispatch: inputs: pr_number: - description: PR number to investigate (for manual testing) - required: false + description: PR number to investigate (required for manual testing) + required: true permissions: {} @@ -49,7 +49,7 @@ jobs: activation: needs: pre_activation if: > - (needs.pre_activation.outputs.activated == 'true') && (github.event_name == 'workflow_dispatch' || (github.event.check_suite.conclusion == 'failure' && + (needs.pre_activation.outputs.activated == 'true') && ((github.event_name == 'workflow_dispatch' && github.event.inputs.pr_number != '') || (github.event.check_suite.conclusion == 'failure' && startsWith(github.event.check_suite.head_branch, 'copilot/'))) runs-on: ubuntu-slim permissions: @@ -248,6 +248,7 @@ jobs: actions: read checks: read contents: read + issues: read pull-requests: read concurrency: group: "gh-aw-copilot-${{ github.workflow }}" @@ -286,6 +287,15 @@ jobs: package-manager-cache: false - name: Create gh-aw temp directory run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh + - env: + CHECK_SUITE_BRANCH: ${{ github.event.check_suite.head_branch }} + EVENT_NAME: ${{ github.event_name }} + GH_TOKEN: ${{ github.token }} + INPUT_PR_NUMBER: ${{ github.event.inputs.pr_number }} + id: verify + name: Verify Copilot agent PR + run: "if [ \"$EVENT_NAME\" = \"workflow_dispatch\" ]; then\n PR_NUMBER=\"$INPUT_PR_NUMBER\"\nelse\n PR_NUMBER=$(gh pr list --head \"$CHECK_SUITE_BRANCH\" --state open --json number --jq '.[0].number' 2>/dev/null || echo \"\")\nfi\nif [ -z \"$PR_NUMBER\" ]; then\n echo \"No PR found. Skipping.\"\n echo \"activated=false\" >> $GITHUB_OUTPUT\n exit 0\nfi\nAUTHOR=$(gh pr view \"$PR_NUMBER\" --json author --jq '.author.login' 2>/dev/null || echo \"\")\nif [ \"$AUTHOR\" != \"app/copilot-swe-agent\" ] && [ \"$AUTHOR\" != \"copilot-swe-agent[bot]\" ]; then\n echo \"PR #$PR_NUMBER author is '$AUTHOR', not copilot-swe-agent. Skipping.\"\n echo \"activated=false\" >> $GITHUB_OUTPUT\n exit 0\nfi\necho \"PR #$PR_NUMBER is by Copilot agent. Proceeding.\"\necho \"activated=true\" >> $GITHUB_OUTPUT\necho \"pr_number=$PR_NUMBER\" >> $GITHUB_OUTPUT\n" + - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} @@ -674,35 +684,13 @@ jobs: - name: Execute GitHub Copilot CLI id: agentic_execution # Copilot CLI tool arguments (sorted): - # --allow-tool ado - # --allow-tool ado(get_build_log) - # --allow-tool ado(get_build_log_by_id) - # --allow-tool ado(get_build_status) - # --allow-tool ado(get_builds) - # --allow-tool github - # --allow-tool safeoutputs - # --allow-tool shell(cat) - # --allow-tool shell(curl) - # --allow-tool shell(date) - # --allow-tool shell(echo) - # --allow-tool shell(grep) - # --allow-tool shell(head) - # --allow-tool shell(jq) - # --allow-tool shell(ls) - # --allow-tool shell(pwd) - # --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: 20 run: | set -o pipefail touch /tmp/gh-aw/agent-step-summary.md # shellcheck disable=SC1003 sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,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,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,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,packagecloud.io,packages.cloud.google.com,packages.microsoft.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" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.23.0 --skip-pull --enable-api-proxy \ - -- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool ado --allow-tool '\''ado(get_build_log)'\'' --allow-tool '\''ado(get_build_log_by_id)'\'' --allow-tool '\''ado(get_build_status)'\'' --allow-tool '\''ado(get_builds)'\'' --allow-tool github --allow-tool safeoutputs --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(curl)'\'' --allow-tool '\''shell(date)'\'' --allow-tool '\''shell(echo)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(pwd)'\'' --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 --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log + -- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log env: COPILOT_AGENT_RUNNER_TYPE: STANDALONE COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} @@ -1088,7 +1076,7 @@ jobs: pre_activation: if: > - github.event_name == 'workflow_dispatch' || (github.event.check_suite.conclusion == 'failure' && + (github.event_name == 'workflow_dispatch' && github.event.inputs.pr_number != '') || (github.event.check_suite.conclusion == 'failure' && startsWith(github.event.check_suite.head_branch, 'copilot/')) runs-on: ubuntu-slim outputs: diff --git a/.github/workflows/copilot-ci-feedback.md b/.github/workflows/copilot-ci-feedback.md index 90a184d6a5..e7d233b76e 100644 --- a/.github/workflows/copilot-ci-feedback.md +++ b/.github/workflows/copilot-ci-feedback.md @@ -6,12 +6,12 @@ on: workflow_dispatch: inputs: pr_number: - description: "PR number to investigate (for manual testing)" - required: false + description: "PR number to investigate (required for manual testing)" + required: true skip-bots: [github-actions] if: > - github.event_name == 'workflow_dispatch' || + (github.event_name == 'workflow_dispatch' && github.event.inputs.pr_number != '') || (github.event.check_suite.conclusion == 'failure' && startsWith(github.event.check_suite.head_branch, 'copilot/')) @@ -21,6 +21,7 @@ permissions: contents: read checks: read actions: read + issues: read pull-requests: read safe-outputs: @@ -38,10 +39,38 @@ mcp-servers: - "get_build_log_by_id" - "get_builds" +steps: + - name: Verify Copilot agent PR + id: verify + env: + EVENT_NAME: ${{ github.event_name }} + INPUT_PR_NUMBER: ${{ github.event.inputs.pr_number }} + CHECK_SUITE_BRANCH: ${{ github.event.check_suite.head_branch }} + GH_TOKEN: ${{ github.token }} + run: | + if [ "$EVENT_NAME" = "workflow_dispatch" ]; then + PR_NUMBER="$INPUT_PR_NUMBER" + else + PR_NUMBER=$(gh pr list --head "$CHECK_SUITE_BRANCH" --state open --json number --jq '.[0].number' 2>/dev/null || echo "") + fi + if [ -z "$PR_NUMBER" ]; then + echo "No PR found. Skipping." + echo "activated=false" >> $GITHUB_OUTPUT + exit 0 + fi + AUTHOR=$(gh pr view "$PR_NUMBER" --json author --jq '.author.login' 2>/dev/null || echo "") + if [ "$AUTHOR" != "app/copilot-swe-agent" ] && [ "$AUTHOR" != "copilot-swe-agent[bot]" ]; then + echo "PR #$PR_NUMBER author is '$AUTHOR', not copilot-swe-agent. Skipping." + echo "activated=false" >> $GITHUB_OUTPUT + exit 0 + fi + echo "PR #$PR_NUMBER is by Copilot agent. Proceeding." + echo "activated=true" >> $GITHUB_OUTPUT + echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT + tools: github: toolsets: [pull_requests] - bash: ["curl", "jq"] --- # Copilot CI Feedback From b84bb067c25ec3dc1785622e6a8074d8778f99b3 Mon Sep 17 00:00:00 2001 From: Shahzaib Date: Fri, 13 Mar 2026 11:46:59 -0700 Subject: [PATCH 6/7] Address review round 5: exit 1 on non-Copilot PRs to short-circuit agent execution --- .github/workflows/copilot-ci-feedback.lock.yml | 4 ++-- .github/workflows/copilot-ci-feedback.md | 11 ++++------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/.github/workflows/copilot-ci-feedback.lock.yml b/.github/workflows/copilot-ci-feedback.lock.yml index 6a186621ea..07626b3422 100644 --- a/.github/workflows/copilot-ci-feedback.lock.yml +++ b/.github/workflows/copilot-ci-feedback.lock.yml @@ -23,7 +23,7 @@ # # Investigates CI failures on Copilot coding agent PRs and posts @copilot comments with specific errors so the agent can self-correct. # -# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"333142823bdac43590f423e471ffdb5155aed166792dd4ee67faedbdd947f8b7","compiler_version":"v0.58.0","strict":true} +# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"b6427f61d4390f0b8be52c5027f9d2c9eddd9e2df202daf1d783ec33e09ad955","compiler_version":"v0.58.0","strict":true} name: "Copilot CI Feedback" "on": @@ -294,7 +294,7 @@ jobs: INPUT_PR_NUMBER: ${{ github.event.inputs.pr_number }} id: verify name: Verify Copilot agent PR - run: "if [ \"$EVENT_NAME\" = \"workflow_dispatch\" ]; then\n PR_NUMBER=\"$INPUT_PR_NUMBER\"\nelse\n PR_NUMBER=$(gh pr list --head \"$CHECK_SUITE_BRANCH\" --state open --json number --jq '.[0].number' 2>/dev/null || echo \"\")\nfi\nif [ -z \"$PR_NUMBER\" ]; then\n echo \"No PR found. Skipping.\"\n echo \"activated=false\" >> $GITHUB_OUTPUT\n exit 0\nfi\nAUTHOR=$(gh pr view \"$PR_NUMBER\" --json author --jq '.author.login' 2>/dev/null || echo \"\")\nif [ \"$AUTHOR\" != \"app/copilot-swe-agent\" ] && [ \"$AUTHOR\" != \"copilot-swe-agent[bot]\" ]; then\n echo \"PR #$PR_NUMBER author is '$AUTHOR', not copilot-swe-agent. Skipping.\"\n echo \"activated=false\" >> $GITHUB_OUTPUT\n exit 0\nfi\necho \"PR #$PR_NUMBER is by Copilot agent. Proceeding.\"\necho \"activated=true\" >> $GITHUB_OUTPUT\necho \"pr_number=$PR_NUMBER\" >> $GITHUB_OUTPUT\n" + run: "if [ \"$EVENT_NAME\" = \"workflow_dispatch\" ]; then\n PR_NUMBER=\"$INPUT_PR_NUMBER\"\nelse\n PR_NUMBER=$(gh pr list --head \"$CHECK_SUITE_BRANCH\" --state open --json number --jq '.[0].number' 2>/dev/null || echo \"\")\nfi\nif [ -z \"$PR_NUMBER\" ]; then\n echo \"No PR found. Exiting.\"\n exit 1\nfi\nAUTHOR=$(gh pr view \"$PR_NUMBER\" --json author --jq '.author.login' 2>/dev/null || echo \"\")\nif [ \"$AUTHOR\" != \"app/copilot-swe-agent\" ] && [ \"$AUTHOR\" != \"copilot-swe-agent[bot]\" ]; then\n echo \"PR #$PR_NUMBER author is '$AUTHOR', not copilot-swe-agent. Exiting.\"\n exit 1\nfi\necho \"PR #$PR_NUMBER is by Copilot agent. Proceeding.\"\necho \"pr_number=$PR_NUMBER\" >> $GITHUB_OUTPUT\n" - name: Configure Git credentials env: diff --git a/.github/workflows/copilot-ci-feedback.md b/.github/workflows/copilot-ci-feedback.md index e7d233b76e..011df80e17 100644 --- a/.github/workflows/copilot-ci-feedback.md +++ b/.github/workflows/copilot-ci-feedback.md @@ -54,18 +54,15 @@ steps: PR_NUMBER=$(gh pr list --head "$CHECK_SUITE_BRANCH" --state open --json number --jq '.[0].number' 2>/dev/null || echo "") fi if [ -z "$PR_NUMBER" ]; then - echo "No PR found. Skipping." - echo "activated=false" >> $GITHUB_OUTPUT - exit 0 + echo "No PR found. Exiting." + exit 1 fi AUTHOR=$(gh pr view "$PR_NUMBER" --json author --jq '.author.login' 2>/dev/null || echo "") if [ "$AUTHOR" != "app/copilot-swe-agent" ] && [ "$AUTHOR" != "copilot-swe-agent[bot]" ]; then - echo "PR #$PR_NUMBER author is '$AUTHOR', not copilot-swe-agent. Skipping." - echo "activated=false" >> $GITHUB_OUTPUT - exit 0 + echo "PR #$PR_NUMBER author is '$AUTHOR', not copilot-swe-agent. Exiting." + exit 1 fi echo "PR #$PR_NUMBER is by Copilot agent. Proceeding." - echo "activated=true" >> $GITHUB_OUTPUT echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT tools: From 18452ab80485d4a6d0b66f841357488f2cf99bc3 Mon Sep 17 00:00:00 2001 From: Shahzaib Date: Fri, 13 Mar 2026 11:56:28 -0700 Subject: [PATCH 7/7] Address review round 6: fix jq null handling, consistent author check (copilot-swe-agent[bot] only) --- .github/workflows/copilot-ci-feedback.lock.yml | 4 ++-- .github/workflows/copilot-ci-feedback.md | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/copilot-ci-feedback.lock.yml b/.github/workflows/copilot-ci-feedback.lock.yml index 07626b3422..71da8f32f7 100644 --- a/.github/workflows/copilot-ci-feedback.lock.yml +++ b/.github/workflows/copilot-ci-feedback.lock.yml @@ -23,7 +23,7 @@ # # Investigates CI failures on Copilot coding agent PRs and posts @copilot comments with specific errors so the agent can self-correct. # -# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"b6427f61d4390f0b8be52c5027f9d2c9eddd9e2df202daf1d783ec33e09ad955","compiler_version":"v0.58.0","strict":true} +# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"e52e9b9b6eedd6ac9bbab945962371ac5a5ae397bd74756c281002dca086107b","compiler_version":"v0.58.0","strict":true} name: "Copilot CI Feedback" "on": @@ -294,7 +294,7 @@ jobs: INPUT_PR_NUMBER: ${{ github.event.inputs.pr_number }} id: verify name: Verify Copilot agent PR - run: "if [ \"$EVENT_NAME\" = \"workflow_dispatch\" ]; then\n PR_NUMBER=\"$INPUT_PR_NUMBER\"\nelse\n PR_NUMBER=$(gh pr list --head \"$CHECK_SUITE_BRANCH\" --state open --json number --jq '.[0].number' 2>/dev/null || echo \"\")\nfi\nif [ -z \"$PR_NUMBER\" ]; then\n echo \"No PR found. Exiting.\"\n exit 1\nfi\nAUTHOR=$(gh pr view \"$PR_NUMBER\" --json author --jq '.author.login' 2>/dev/null || echo \"\")\nif [ \"$AUTHOR\" != \"app/copilot-swe-agent\" ] && [ \"$AUTHOR\" != \"copilot-swe-agent[bot]\" ]; then\n echo \"PR #$PR_NUMBER author is '$AUTHOR', not copilot-swe-agent. Exiting.\"\n exit 1\nfi\necho \"PR #$PR_NUMBER is by Copilot agent. Proceeding.\"\necho \"pr_number=$PR_NUMBER\" >> $GITHUB_OUTPUT\n" + run: "if [ \"$EVENT_NAME\" = \"workflow_dispatch\" ]; then\n PR_NUMBER=\"$INPUT_PR_NUMBER\"\nelse\n PR_NUMBER=$(gh pr list --head \"$CHECK_SUITE_BRANCH\" --state open --json number --jq '.[0].number // empty' 2>/dev/null || echo \"\")\nfi\nif [ -z \"$PR_NUMBER\" ]; then\n echo \"No PR found. Exiting.\"\n exit 1\nfi\nAUTHOR=$(gh pr view \"$PR_NUMBER\" --json author --jq '.author.login' 2>/dev/null || echo \"\")\nif [ \"$AUTHOR\" != \"copilot-swe-agent[bot]\" ]; then\n echo \"PR #$PR_NUMBER author is '$AUTHOR', not copilot-swe-agent[bot]. Exiting.\"\n exit 1\nfi\necho \"PR #$PR_NUMBER is by Copilot agent. Proceeding.\"\necho \"pr_number=$PR_NUMBER\" >> $GITHUB_OUTPUT\n" - name: Configure Git credentials env: diff --git a/.github/workflows/copilot-ci-feedback.md b/.github/workflows/copilot-ci-feedback.md index 011df80e17..35021547d3 100644 --- a/.github/workflows/copilot-ci-feedback.md +++ b/.github/workflows/copilot-ci-feedback.md @@ -51,15 +51,15 @@ steps: if [ "$EVENT_NAME" = "workflow_dispatch" ]; then PR_NUMBER="$INPUT_PR_NUMBER" else - PR_NUMBER=$(gh pr list --head "$CHECK_SUITE_BRANCH" --state open --json number --jq '.[0].number' 2>/dev/null || echo "") + PR_NUMBER=$(gh pr list --head "$CHECK_SUITE_BRANCH" --state open --json number --jq '.[0].number // empty' 2>/dev/null || echo "") fi if [ -z "$PR_NUMBER" ]; then echo "No PR found. Exiting." exit 1 fi AUTHOR=$(gh pr view "$PR_NUMBER" --json author --jq '.author.login' 2>/dev/null || echo "") - if [ "$AUTHOR" != "app/copilot-swe-agent" ] && [ "$AUTHOR" != "copilot-swe-agent[bot]" ]; then - echo "PR #$PR_NUMBER author is '$AUTHOR', not copilot-swe-agent. Exiting." + if [ "$AUTHOR" != "copilot-swe-agent[bot]" ]; then + echo "PR #$PR_NUMBER author is '$AUTHOR', not copilot-swe-agent[bot]. Exiting." exit 1 fi echo "PR #$PR_NUMBER is by Copilot agent. Proceeding."