# # ___ _ _ # / _ \ | | (_) # | |_| | __ _ ___ _ __ | |_ _ ___ # | _ |/ _` |/ _ \ '_ \| __| |/ __| # | | | | (_| | __/ | | | |_| | (__ # \_| |_/\__, |\___|_| |_|\__|_|\___| # __/ | # _ _ |___/ # | | | | / _| | # | | | | ___ _ __ _ __| |_| | _____ ____ # | |/\| |/ _ \ '__| |/ /| _| |/ _ \ \ /\ / / ___| # \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ # \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ # # This file was automatically generated by gh-aw (v0.50.7). 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/ # # Automated code reviewer for pull requests. Analyzes code changes for bugs, # security issues, performance problems, Go best practices, and Kubernetes # controller patterns. Creates review comments with specific feedback and # pushes minor fixes (formatting, linting) directly. Assigns the maintainer # only when human judgment is genuinely needed. # # gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"18ec815df5d5897d21d7ba19dae442639f83c3aef1984fc3de7b79b51ecff78c","compiler_version":"v0.50.7"} name: "Automated Code Reviewer" "on": pull_request: types: - opened - synchronize permissions: {} concurrency: group: "gh-aw-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}" cancel-in-progress: true run-name: "Automated Code Reviewer" jobs: activation: needs: pre_activation if: > (needs.pre_activation.outputs.activated == 'true') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.id == github.repository_id)) runs-on: ubuntu-slim permissions: contents: read outputs: body: ${{ steps.sanitized.outputs.body }} comment_id: "" comment_repo: "" secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} text: ${{ steps.sanitized.outputs.text }} title: ${{ steps.sanitized.outputs.title }} steps: - name: Setup Scripts uses: github/gh-aw/actions/setup@9cbca3cd9be433a23a38e4da332635097fd40251 # v0.50.7 with: destination: /opt/gh-aw/actions - 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: Validate context variables 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/validate_context_variables.cjs'); await main(); - name: Checkout .github and .agents folders uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: sparse-checkout: | .github .agents fetch-depth: 1 persist-credentials: false - name: Check workflow file timestamps uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: GH_AW_WORKFLOW_FILE: "code-review.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: Compute current body text id: sanitized 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/compute_text.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, create_pull_request_review_comment, submit_pull_request_review, push_to_pull_request_branch, missing_tool, missing_data, noop GH_AW_PROMPT_EOF cat "/opt/gh-aw/prompts/safe_outputs_push_to_pr_branch.md" cat << 'GH_AW_PROMPT_EOF' 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/code-review.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 GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} 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 prompt artifact if: success() uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 with: name: prompt path: /tmp/gh-aw/aw-prompts/prompt.txt retention-days: 1 agent: needs: activation runs-on: ubuntu-latest permissions: read-all 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: codereview 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 }} model: ${{ steps.generate_aw_info.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@9cbca3cd9be433a23a38e4da332635097fd40251 # v0.50.7 with: destination: /opt/gh-aw/actions - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: 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: Generate agentic run info id: generate_aw_info uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 with: script: | const fs = require('fs'); const awInfo = { engine_id: "copilot", engine_name: "GitHub Copilot CLI", model: process.env.GH_AW_MODEL_AGENT_COPILOT || "", version: "", agent_version: "0.0.419", cli_version: "v0.50.7", workflow_name: "Automated Code Reviewer", experimental: false, supports_tools_allowlist: true, run_id: context.runId, run_number: context.runNumber, run_attempt: process.env.GITHUB_RUN_ATTEMPT, repository: context.repo.owner + '/' + context.repo.repo, ref: context.ref, sha: context.sha, actor: context.actor, event_name: context.eventName, staged: false, allowed_domains: ["defaults"], firewall_enabled: true, awf_version: "v0.23.0", awmg_version: "v0.1.6", steps: { firewall: "squid" }, created_at: new Date().toISOString() }; // Write to /tmp/gh-aw directory to avoid inclusion in PR const tmpPath = '/tmp/gh-aw/aw_info.json'; fs.writeFileSync(tmpPath, JSON.stringify(awInfo, null, 2)); console.log('Generated aw_info.json at:', tmpPath); console.log(JSON.stringify(awInfo, null, 2)); // Set model as output for reuse in other steps/jobs core.setOutput('model', awInfo.model); - name: Install GitHub Copilot CLI run: /opt/gh-aw/actions/install_copilot_cli.sh 0.0.419 - name: Install awf binary run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.23.0 - name: Validate lockdown mode requirements id: validate-lockdown-requirements uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: GITHUB_MCP_LOCKDOWN_EXPLICIT: "true" 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 validateLockdownRequirements = require('/opt/gh-aw/actions/validate_lockdown_requirements.cjs'); validateLockdownRequirements(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.6 ghcr.io/github/github-mcp-server:v0.31.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},"create_pull_request_review_comment":{"max":10},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_to_pull_request_branch":{"max":0},"submit_pull_request_review":{"max":1}} GH_AW_SAFE_OUTPUTS_CONFIG_EOF 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" }, "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). 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 comment will be silently discarded.", "type": "number" } }, "required": [ "body" ], "type": "object" }, "name": "add_comment" }, { "description": "Create a review comment on a specific line of code in a pull request. Use this for inline code review feedback, suggestions, or questions about specific code changes. For general PR comments not tied to specific lines, use add_comment instead. CONSTRAINTS: Maximum 10 review comment(s) can be created. Comments will be on the RIGHT side of the diff.", "inputSchema": { "additionalProperties": false, "properties": { "body": { "description": "Review comment content in Markdown. Provide specific, actionable feedback about the code at this location.", "type": "string" }, "line": { "description": "Line number for the comment. For single-line comments, this is the target line. For multi-line comments, this is the ending line.", "type": [ "number", "string" ] }, "path": { "description": "File path relative to the repository root (e.g., 'src/auth/login.js'). Must be a file that was changed in the PR.", "type": "string" }, "side": { "description": "Side of the diff to comment on: RIGHT for the new version (additions), LEFT for the old version (deletions). Defaults to RIGHT.", "enum": [ "LEFT", "RIGHT" ], "type": "string" }, "start_line": { "description": "Starting line number for multi-line comments. When set, the comment spans from start_line to line. Omit for single-line comments.", "type": [ "number", "string" ] } }, "required": [ "path", "line", "body" ], "type": "object" }, "name": "create_pull_request_review_comment" }, { "description": "Submit a pull request review with a status decision. All create_pull_request_review_comment outputs are automatically collected and included as inline comments in this review. Use APPROVE to approve the PR, REQUEST_CHANGES to request changes, or COMMENT for general feedback without a decision. If you don't call this tool, review comments are still submitted as a COMMENT review. CONSTRAINTS: Maximum 1 review(s) can be submitted.", "inputSchema": { "additionalProperties": false, "properties": { "body": { "description": "Overall review summary in Markdown. Provide a high-level assessment of the changes. Required for REQUEST_CHANGES; optional for APPROVE and COMMENT.", "type": "string" }, "event": { "description": "Review decision: APPROVE to approve the pull request, REQUEST_CHANGES to formally request changes before merging, or COMMENT for general feedback without a formal decision. Defaults to COMMENT when omitted.", "enum": [ "APPROVE", "REQUEST_CHANGES", "COMMENT" ], "type": "string" } }, "type": "object" }, "name": "submit_pull_request_review" }, { "description": "Push committed changes to a pull request's branch. Use this to add follow-up commits to an existing PR, such as addressing review feedback or fixing issues. Changes must be committed locally before calling this tool.", "inputSchema": { "additionalProperties": false, "properties": { "branch": { "description": "Branch name to push changes from. If omitted, uses the current working branch. Only specify if you need to push from a different branch.", "type": "string" }, "message": { "description": "Commit message describing the changes. Follow repository commit message conventions (e.g., conventional commits).", "type": "string" }, "pull_request_number": { "description": "Pull request number to push changes to. This is the numeric ID from the GitHub URL (e.g., 654 in github.com/owner/repo/pull/654). Required when the workflow target is '*' (any PR).", "type": [ "number", "string" ] } }, "required": [ "message" ], "type": "object" }, "name": "push_to_pull_request_branch" }, { "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" }, "reason": { "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).", "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": { "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" } }, "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" }, "reason": { "description": "Explanation of why this data is needed to complete the task (max 256 characters).", "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 } } }, "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" }, "repo": { "type": "string", "maxLength": 256 }, "side": { "type": "string", "enum": [ "LEFT", "RIGHT" ] }, "start_line": { "optionalPositiveInteger": true } }, "customValidation": "startLineLessOrEqualLine" }, "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 } } }, "push_to_pull_request_branch": { "defaultMax": 1, "fields": { "branch": { "required": true, "type": "string", "sanitize": true, "maxLength": 256 }, "message": { "required": true, "type": "string", "sanitize": true, "maxLength": 65000 }, "pull_request_number": { "issueOrPRNumber": true } } }, "submit_pull_request_review": { "defaultMax": 1, "fields": { "body": { "type": "string", "sanitize": true, "maxLength": 65000 }, "event": { "type": "string", "enum": [ "APPROVE", "REQUEST_CHANGES", "COMMENT" ] } } } } 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_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 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 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.6' mkdir -p /home/runner/.copilot cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", "env": { "GITHUB_LOCKDOWN_MODE": "1", "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}", "GITHUB_READ_ONLY": "1", "GITHUB_TOOLSETS": "pull_requests,repos" } }, "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: Generate workflow overview uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 with: script: | const { generateWorkflowOverview } = require('/opt/gh-aw/actions/generate_workflow_overview.cjs'); await generateWorkflowOverview(core); - name: Download prompt artifact uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6 with: name: prompt path: /tmp/gh-aw/aw-prompts - 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): timeout-minutes: 15 run: | set -o pipefail # 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-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_COPILOT:+ --model "$GH_AW_MODEL_AGENT_COPILOT"}' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log env: COPILOT_AGENT_RUNNER_TYPE: STANDALONE COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} GH_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json GH_AW_MODEL_AGENT_COPILOT: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || '' }} GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} GITHUB_API_URL: ${{ github.api_url }} 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: ${{ env.GITHUB_STEP_SUMMARY }} GITHUB_WORKSPACE: ${{ github.workspace }} XDG_CONFIG_HOME: /home/runner - 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: Upload Safe Outputs if: always() uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 with: name: safe-output path: ${{ env.GH_AW_SAFE_OUTPUTS }} if-no-files-found: warn - 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: Upload sanitized agent output if: always() && env.GH_AW_AGENT_OUTPUT uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 with: name: agent-output path: ${{ env.GH_AW_AGENT_OUTPUT }} if-no-files-found: warn - name: Upload engine output files uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 with: name: agent_outputs path: | /tmp/gh-aw/sandbox/agent/logs/ /tmp/gh-aw/redacted-urls.log if-no-files-found: ignore - 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@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 with: name: agent-artifacts path: | /tmp/gh-aw/aw-prompts/prompt.txt /tmp/gh-aw/aw_info.json /tmp/gh-aw/mcp-logs/ /tmp/gh-aw/sandbox/firewall/logs/ /tmp/gh-aw/agent-stdio.log /tmp/gh-aw/agent/ /tmp/gh-aw/aw-*.patch 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: "Automated Code Reviewer" WORKFLOW_DESCRIPTION: "Automated code reviewer for pull requests. Analyzes code changes for bugs,\nsecurity issues, performance problems, Go best practices, and Kubernetes\ncontroller patterns. Creates review comments with specific feedback and\npushes minor fixes (formatting, linting) directly. Assigns the maintainer\nonly when human judgment is genuinely needed." 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 # 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)"${GH_AW_MODEL_DETECTION_COPILOT:+ --model "$GH_AW_MODEL_DETECTION_COPILOT"}' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log env: COPILOT_AGENT_RUNNER_TYPE: STANDALONE COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} GH_AW_MODEL_DETECTION_COPILOT: ${{ vars.GH_AW_MODEL_DETECTION_COPILOT || '' }} GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GITHUB_API_URL: ${{ github.api_url }} GITHUB_HEAD_REF: ${{ github.head_ref }} GITHUB_REF_NAME: ${{ github.ref_name }} GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }} GITHUB_WORKSPACE: ${{ github.workspace }} 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@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 with: name: threat-detection.log 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: write discussions: write issues: write pull-requests: write 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@9cbca3cd9be433a23a38e4da332635097fd40251 # v0.50.7 with: destination: /opt/gh-aw/actions - name: Download agent output artifact continue-on-error: true uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6 with: name: agent-output path: /tmp/gh-aw/safeoutputs/ - name: Setup agent output environment variable run: | mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/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: "Automated Code Reviewer" 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: "Automated Code Reviewer" 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: "Automated Code Reviewer" 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: "code-review" GH_AW_SECRET_VERIFICATION_RESULT: ${{ needs.activation.outputs.secret_verification_result }} GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }} GH_AW_CODE_PUSH_FAILURE_ERRORS: ${{ needs.safe_outputs.outputs.code_push_failure_errors }} GH_AW_CODE_PUSH_FAILURE_COUNT: ${{ needs.safe_outputs.outputs.code_push_failure_count }} GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e Reviewed by [{workflow_name}]({run_url})\",\"runStarted\":\"[{workflow_name}]({run_url}) is reviewing this pull request...\",\"runSuccess\":\"[{workflow_name}]({run_url}) has completed the review.\",\"runFailure\":\"[{workflow_name}]({run_url}) encountered an error ({status}).\"}" GH_AW_GROUP_REPORTS: "false" 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: "Automated Code Reviewer" 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: if: (github.event_name != 'pull_request') || (github.event.pull_request.head.repo.id == github.repository_id) runs-on: ubuntu-slim outputs: activated: ${{ steps.check_membership.outputs.is_team_member == 'true' }} matched_command: '' steps: - name: Setup Scripts uses: github/gh-aw/actions/setup@9cbca3cd9be433a23a38e4da332635097fd40251 # v0.50.7 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(); safe_outputs: needs: - activation - agent if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (needs.agent.outputs.detection_success == 'true') runs-on: ubuntu-slim permissions: contents: write discussions: write issues: write pull-requests: write timeout-minutes: 15 env: GH_AW_ENGINE_ID: "copilot" GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e Reviewed by [{workflow_name}]({run_url})\",\"runStarted\":\"[{workflow_name}]({run_url}) is reviewing this pull request...\",\"runSuccess\":\"[{workflow_name}]({run_url}) has completed the review.\",\"runFailure\":\"[{workflow_name}]({run_url}) encountered an error ({status}).\"}" GH_AW_WORKFLOW_ID: "code-review" GH_AW_WORKFLOW_NAME: "Automated Code Reviewer" 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 }} 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@9cbca3cd9be433a23a38e4da332635097fd40251 # v0.50.7 with: destination: /opt/gh-aw/actions - name: Download agent output artifact continue-on-error: true uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6 with: name: agent-output path: /tmp/gh-aw/safeoutputs/ - name: Setup agent output environment variable run: | mkdir -p /tmp/gh-aw/safeoutputs/ find "/tmp/gh-aw/safeoutputs/" -type f -print echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - name: Download patch artifact continue-on-error: true uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6 with: name: agent-artifacts path: /tmp/gh-aw/ - name: Checkout repository if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'push_to_pull_request_branch')) uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: ${{ github.base_ref || github.event.pull_request.base.ref || github.ref_name || github.event.repository.default_branch }} token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} persist-credentials: false fetch-depth: 1 - name: Configure Git credentials if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'push_to_pull_request_branch')) env: REPO_NAME: ${{ github.repository }} SERVER_URL: ${{ github.server_url }} GIT_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} 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:${GIT_TOKEN}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" echo "Git configured with standard GitHub Actions identity" - 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},\"create_pull_request_review_comment\":{\"max\":10,\"side\":\"RIGHT\"},\"missing_data\":{},\"missing_tool\":{},\"push_to_pull_request_branch\":{\"if_no_changes\":\"warn\",\"max_patch_size\":1024},\"submit_pull_request_review\":{\"max\":1}}" GH_AW_CI_TRIGGER_TOKEN: ${{ secrets.GH_AW_CI_TRIGGER_TOKEN }} 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@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 with: name: safe-output-items path: /tmp/safe-output-items.jsonl if-no-files-found: warn