Repository navigation
PR Review Panel #1295
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"116cedbaf93b8f734c3d202c22ad3ab9918b9f5b44b5fcbffd0daac2487527e0","body_hash":"ffb5f8ecd2d97ce31c8b16daa40d6a86e6c8682847803ff54ee627da2fe2e623","compiler_version":"v0.89.15","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.83"}} | ||
|
Check warning on line 1 in .github/workflows/pr-review-panel.lock.yml
|
||
| # gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_DEFAULT_OTLP_ENDPOINT","GH_AW_DEFAULT_OTLP_HEADERS","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_PLUGINS_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"3d3c42e5aac5ba805825da76410c181273ba90b1","version":"v7.0.1"},{"repo":"actions/create-github-app-token","sha":"bcd2ba49218906704ab6c1aa796996da409d3eb1","version":"v3.2.0"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"820762786026740c76f36085b0efc47a31fe5020","version":"v7.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"045beb2d14bda8d0c1f2e83b41527f63770d2855","version":"v0.89.15"},{"repo":"microsoft/apm-action","sha":"d723bb64ed70c135bbaf87d126b721dd2dae0439","version":"v1.10.0"},{"repo":"ruby/setup-ruby","sha":"984c0c890880bbf811283d6f09c4607c62d210a4","version":"v1.323.0"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.28.16","digest":"sha256:57a3e27388a6d7d32719088581e52567727fa0bf2f0d477bf565b0c4baa12a3f","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.28.16@sha256:57a3e27388a6d7d32719088581e52567727fa0bf2f0d477bf565b0c4baa12a3f"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.28.16","digest":"sha256:cd400948638ffe1b87ec319abf73fa29b6ac9881b015da58bba971c4cc13a400","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.28.16@sha256:cd400948638ffe1b87ec319abf73fa29b6ac9881b015da58bba971c4cc13a400"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.28.16","digest":"sha256:452197f2e241b2cda8eb0b5674960aa8ca544a64dc242e24f8263c3df6452919","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.28.16@sha256:452197f2e241b2cda8eb0b5674960aa8ca544a64dc242e24f8263c3df6452919"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.21","digest":"sha256:f26f665840660581510746ed84f5fc706f4ca8c48833757dfb4d72d86587201e","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.21@sha256:f26f665840660581510746ed84f5fc706f4ca8c48833757dfb4d72d86587201e"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:0daa8971fa4732b647150cb6524a6b0804b68d5d24f6f58b5dd1af23bd63fb23","pinned_image":"ghcr.io/github/gh-aw-node@sha256:0daa8971fa4732b647150cb6524a6b0804b68d5d24f6f58b5dd1af23bd63fb23"},{"image":"ghcr.io/github/github-mcp-server:v1.12.1","digest":"sha256:0ba840c46a237879c8300e7fddb0b6347f20e029ccb9cbe2ce4a943daa1ff560","pinned_image":"ghcr.io/github/github-mcp-server:v1.12.1@sha256:0ba840c46a237879c8300e7fddb0b6347f20e029ccb9cbe2ce4a943daa1ff560"}],"has_pull_request_target":true,"mcp_servers":[{"name":"github","tools":["get_commit","get_file_contents","get_latest_release","get_me","get_pull_request","get_pull_request_comments","get_pull_request_diff","get_pull_request_files","get_pull_request_review_comments","get_pull_request_reviews","get_pull_request_status","get_release_by_tag","get_tag","issue_read","list_branches","list_commits","list_issue_types","list_issues","list_pull_requests","list_releases","list_starred_repositories","list_tags","pull_request_read","search_code","search_issues","search_pull_requests","search_repositories"]},{"name":"safeoutputs","tools":["add_comment","missing_data","missing_tool","noop","remove_labels"]}]} | ||
| # This file was automatically generated by gh-aw (v0.89.15). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md | ||
| # | ||
| # ___ _ _ | ||
| # / _ \ | | (_) | ||
| # | |_| | __ _ ___ _ __ | |_ _ ___ | ||
| # | _ |/ _` |/ _ \ '_ \| __| |/ __| | ||
| # | | | | (_| | __/ | | | |_| | (__ | ||
| # \_| |_/\__, |\___|_| |_|\__|_|\___| | ||
| # __/ | | ||
| # _ _ |___/ | ||
| # | | | | / _| | | ||
| # | | | | ___ _ __ _ __| |_| | _____ ____ | ||
| # | |/\| |/ _ \ '__| |/ /| _| |/ _ \ \ /\ / / ___| | ||
| # \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ | ||
| # \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ | ||
| # | ||
| # | ||
| # To update this file, edit the corresponding .md file and run: | ||
| # gh aw compile | ||
| # Not all edits will cause changes to this file. | ||
| # | ||
| # For more information: https://github.github.com/gh-aw/introduction/overview/ | ||
| # | ||
| # Multi-persona expert panel review of labelled PRs, posting a single synthesized advisory recommendation comment. | ||
| # | ||
| # Resolved workflow manifest: | ||
| # Imports: | ||
| # - shared/apm.md | ||
| # | ||
| # Secrets used: | ||
| # - COPILOT_GITHUB_TOKEN | ||
| # - GH_AW_DEFAULT_OTLP_ENDPOINT | ||
| # - GH_AW_DEFAULT_OTLP_HEADERS | ||
| # - GH_AW_GITHUB_MCP_SERVER_TOKEN | ||
| # - GH_AW_GITHUB_TOKEN | ||
| # - GH_AW_PLUGINS_TOKEN | ||
| # - GITHUB_TOKEN | ||
| # | ||
| # Custom actions used: | ||
| # - actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | ||
| # - actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 | ||
| # - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | ||
| # - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| # - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 (source v9) | ||
| # - actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 | ||
| # - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | ||
| # - github/gh-aw-actions/setup@045beb2d14bda8d0c1f2e83b41527f63770d2855 # v0.89.15 | ||
| # - microsoft/apm-action@d723bb64ed70c135bbaf87d126b721dd2dae0439 # v1.10.0 | ||
| # - ruby/setup-ruby@984c0c890880bbf811283d6f09c4607c62d210a4 # v1.323.0 | ||
| # | ||
| # Container images used: | ||
| # - ghcr.io/github/gh-aw-firewall/agent:0.28.16@sha256:57a3e27388a6d7d32719088581e52567727fa0bf2f0d477bf565b0c4baa12a3f | ||
| # - ghcr.io/github/gh-aw-firewall/api-proxy:0.28.16@sha256:cd400948638ffe1b87ec319abf73fa29b6ac9881b015da58bba971c4cc13a400 | ||
| # - ghcr.io/github/gh-aw-firewall/squid:0.28.16@sha256:452197f2e241b2cda8eb0b5674960aa8ca544a64dc242e24f8263c3df6452919 | ||
| # - ghcr.io/github/gh-aw-mcpg:v0.4.21@sha256:f26f665840660581510746ed84f5fc706f4ca8c48833757dfb4d72d86587201e | ||
| # - ghcr.io/github/gh-aw-node@sha256:0daa8971fa4732b647150cb6524a6b0804b68d5d24f6f58b5dd1af23bd63fb23 | ||
| # - ghcr.io/github/github-mcp-server:v1.12.1@sha256:0ba840c46a237879c8300e7fddb0b6347f20e029ccb9cbe2ce4a943daa1ff560 | ||
| name: "PR Review Panel" | ||
| on: | ||
| pull_request_target: | ||
| types: | ||
| - labeled | ||
| # report-blocked-version: false # Report-blocked-version processed as activation-stage notification toggle | ||
| # roles: # Roles processed as role check in pre-activation job | ||
| # - admin # Roles processed as role check in pre-activation job | ||
| # - maintainer # Roles processed as role check in pre-activation job | ||
| # - write # Roles processed as role check in pre-activation job | ||
| workflow_dispatch: | ||
| inputs: | ||
| aw_context: | ||
| default: "" | ||
| description: "Agent caller context (used internally by Agentic Workflows)." | ||
| required: false | ||
| type: string | ||
| pr_number: | ||
| description: Pull request number to review (works for fork PRs) | ||
| required: true | ||
| type: string | ||
| permissions: {} | ||
| concurrency: | ||
| group: "gh-aw-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref || github.run_id }}" | ||
| cancel-in-progress: true | ||
| run-name: "PR Review Panel" | ||
| env: | ||
| OTEL_EXPORTER_OTLP_ENDPOINT: ${{ secrets.GH_AW_DEFAULT_OTLP_ENDPOINT || vars.GH_AW_DEFAULT_OTLP_ENDPOINT }} | ||
| OTEL_SERVICE_NAME: gh-aw.pr-review-panel | ||
| OTEL_RESOURCE_ATTRIBUTES: 'gh-aw.workflow.name=PR%20Review%20Panel,gh-aw.repository=${{ github.repository }},gh-aw.run.id=${{ github.run_id }},github.run_id=${{ github.run_id }},gh-aw.engine.id=copilot' | ||
| OTEL_EXPORTER_OTLP_HEADERS: ${{ secrets.GH_AW_DEFAULT_OTLP_HEADERS }} | ||
| GH_AW_OTLP_ENDPOINTS: '[{"url":"${{ secrets.GH_AW_DEFAULT_OTLP_ENDPOINT || vars.GH_AW_DEFAULT_OTLP_ENDPOINT }}","headers":"${{ secrets.GH_AW_DEFAULT_OTLP_HEADERS }}"}]' | ||
| GH_AW_OTLP_IF_MISSING: ignore | ||
| jobs: | ||
| activation: | ||
| needs: pre_activation | ||
| if: > | ||
| needs.pre_activation.outputs.activated == 'true' && (github.event_name == 'workflow_dispatch' || github.event.label.name == 'panel-review') | ||
| runs-on: ubuntu-slim | ||
| permissions: | ||
| actions: read | ||
| contents: read | ||
| env: | ||
| GH_AW_MAX_DAILY_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_DAILY_AI_CREDITS || '5000' }} | ||
| GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} | ||
| outputs: | ||
| body: ${{ steps.sanitized.outputs.body }} | ||
| comment_id: "" | ||
| comment_repo: "" | ||
| daily_ai_credits_exceeded: ${{ steps.daily-effective-workflow-guardrail.outputs.daily_ai_credits_exceeded == 'true' }} | ||
| daily_ai_credits_guardrail_error: ${{ steps.daily-effective-workflow-guardrail.outputs.daily_ai_credits_guardrail_error || '' }} | ||
| daily_ai_credits_guardrail_status: ${{ steps.daily-effective-workflow-guardrail.outputs.daily_ai_credits_guardrail_status || '' }} | ||
| daily_ai_credits_threshold: ${{ steps.daily-effective-workflow-guardrail.outputs.daily_ai_credits_threshold || '' }} | ||
| daily_ai_credits_total_effective_tokens: ${{ steps.daily-effective-workflow-guardrail.outputs.daily_ai_credits_total_effective_tokens || '' }} | ||
| engine_id: ${{ steps.generate_aw_info.outputs.engine_id }} | ||
| lockdown_check_failed: ${{ steps.generate_aw_info.outputs.lockdown_check_failed == 'true' }} | ||
| model: ${{ steps.generate_aw_info.outputs.model }} | ||
| oauth_token_check_failed: ${{ steps.check-oauth-tokens.outputs.oauth_token_check_failed == 'true' }} | ||
| secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} | ||
| setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }} | ||
| setup-span-id: ${{ steps.setup.outputs.span-id }} | ||
| setup-trace-id: ${{ steps.setup.outputs.trace-id }} | ||
| stale_lock_file_failed: ${{ steps.check-lock-file.outputs.stale_lock_file_failed == 'true' }} | ||
| text: ${{ steps.sanitized.outputs.text }} | ||
| title: ${{ steps.sanitized.outputs.title }} | ||
| steps: | ||
| - name: Setup Scripts | ||
| id: setup | ||
| uses: github/gh-aw-actions/setup@045beb2d14bda8d0c1f2e83b41527f63770d2855 # v0.89.15 | ||
| with: | ||
| destination: ${{ runner.temp }}/gh-aw/actions | ||
| job-name: ${{ github.job }} | ||
| trace-id: ${{ needs.pre_activation.outputs.setup-trace-id }} | ||
| parent-span-id: ${{ needs.pre_activation.outputs.setup-parent-span-id || needs.pre_activation.outputs.setup-span-id }} | ||
| safe-output-artifact-client: ${{ env.GH_AW_MAX_DAILY_AI_CREDITS != '' }} | ||
| env: | ||
| GH_AW_SETUP_WORKFLOW_NAME: "PR Review Panel" | ||
| GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/pr-review-panel.lock.yml@${{ github.ref }} | ||
| GH_AW_INFO_VERSION: "1.0.83" | ||
| GH_AW_INFO_AWF_VERSION: "v0.28.16" | ||
| GH_AW_INFO_ENGINE_ID: "copilot" | ||
| - name: Mask OTLP telemetry headers | ||
| run: bash "${RUNNER_TEMP}/gh-aw/actions/mask_otlp_headers.sh" | ||
| - name: Generate agentic run info | ||
| id: generate_aw_info | ||
| env: | ||
| GH_AW_INFO_ENGINE_ID: "copilot" | ||
| GH_AW_INFO_ENGINE_NAME: "GitHub Copilot CLI" | ||
| GH_AW_INFO_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || vars.GH_AW_DEFAULT_MODEL_COPILOT || 'auto' }} | ||
| GH_AW_INFO_VERSION: "1.0.83" | ||
| GH_AW_INFO_AGENT_VERSION: "1.0.83" | ||
| GH_AW_INFO_CLI_VERSION: "v0.89.15" | ||
| GH_AW_INFO_WORKFLOW_NAME: "PR Review Panel" | ||
| GH_AW_INFO_EXPERIMENTAL: "false" | ||
| GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" | ||
| GH_AW_INFO_STAGED: "false" | ||
| GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","github"]' | ||
| GH_AW_INFO_FIREWALL_ENABLED: "true" | ||
| GH_AW_INFO_AWF_VERSION: "v0.28.16" | ||
| GH_AW_INFO_AWMG_VERSION: "" | ||
| GH_AW_INFO_FIREWALL_TYPE: "squid" | ||
| GH_AW_INFO_AGENT_RUNTIME: "" | ||
| GH_AW_COMPILED_STRICT: "true" | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| with: | ||
| script: | | ||
| const path = require('path'); | ||
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | ||
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require(path.join(actionsDir, 'generate_aw_info.cjs')); | ||
| await main(core, context); | ||
| - name: Restore daily AIC scan observations | ||
| id: restore-daily-aic-cache-fallback | ||
| if: ${{ env.GH_AW_MAX_DAILY_AI_CREDITS != '' }} | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| env: | ||
| GH_AW_HAS_SLASH_COMMAND: "false" | ||
| GH_AW_HAS_LABEL_COMMAND: "false" | ||
| with: | ||
| github-token: ${{ secrets.GITHUB_TOKEN }} | ||
| script: | | ||
| const path = require('path'); | ||
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | ||
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require(path.join(actionsDir, 'restore_aic_scan_cache.cjs')); | ||
| await main(); | ||
| - name: Check daily workflow token guardrail | ||
| id: daily-effective-workflow-guardrail | ||
| if: ${{ env.GH_AW_MAX_DAILY_AI_CREDITS != '' }} | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| env: | ||
| GH_AW_WORKFLOW_NAME: "PR Review Panel" | ||
| GH_AW_WORKFLOW_ID: "pr-review-panel" | ||
| GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | ||
| GH_AW_WORKFLOW_DISPATCH_AW_CONTEXT: ${{ github.event.inputs.aw_context || '' }} | ||
| GH_AW_HAS_SLASH_COMMAND: "false" | ||
| GH_AW_HAS_LABEL_COMMAND: "false" | ||
| GH_AW_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| GH_AW_MAX_DAILY_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_DAILY_AI_CREDITS || '5000' }} | ||
| with: | ||
| github-token: ${{ secrets.GITHUB_TOKEN }} | ||
| script: | | ||
| const path = require('path'); | ||
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | ||
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require(path.join(actionsDir, 'check_daily_aic_workflow_guardrail.cjs')); | ||
| await main(); | ||
| - name: Publish daily AIC scan observations | ||
| if: always() && env.GH_AW_MAX_DAILY_AI_CREDITS != '' | ||
| continue-on-error: true | ||
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | ||
| with: | ||
| name: aic-usage-scan-v2 | ||
| path: /tmp/gh-aw/agentic-workflow-usage-scan-v2.jsonl | ||
| overwrite: true | ||
| if-no-files-found: ignore | ||
| retention-days: 3 | ||
| - name: Validate COPILOT_GITHUB_TOKEN secret | ||
| id: validate-secret | ||
| run: bash "${RUNNER_TEMP}/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: Check for OAuth tokens | ||
| id: check-oauth-tokens | ||
| run: bash "${RUNNER_TEMP}/gh-aw/actions/check_oauth_tokens.sh" | ||
| env: | ||
| COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} | ||
| GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} | ||
| GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} | ||
| - name: Checkout .github and .agents folders | ||
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | ||
| with: | ||
| persist-credentials: false | ||
| sparse-checkout: | | ||
| .github | ||
| .agents | ||
| .claude | ||
| .codex | ||
| .gemini | ||
| .pi | ||
| sparse-checkout-cone-mode: true | ||
| fetch-depth: 1 | ||
| - name: Save agent config folders for base branch restoration | ||
| env: | ||
| GH_AW_AGENT_FOLDERS: ".agents .github" | ||
| GH_AW_AGENT_FILES: "AGENTS.md" | ||
| run: | | ||
| bash "${RUNNER_TEMP}/gh-aw/actions/save_base_github_folders.sh" | ||
| - name: Check workflow lock file | ||
| id: check-lock-file | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| env: | ||
| GH_AW_WORKFLOW_FILE: "pr-review-panel.lock.yml" | ||
| GH_AW_CONTEXT_WORKFLOW_REF: "${{ github.workflow_ref }}" | ||
| with: | ||
| script: | | ||
| const path = require('path'); | ||
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | ||
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require(path.join(actionsDir, 'check_workflow_timestamp_api.cjs')); | ||
| await main(); | ||
| - name: Check compile-agentic version | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| env: | ||
| GH_AW_COMPILED_VERSION: "v0.89.15" | ||
| GH_AW_BLOCKED_VERSION_REPORT_AS_ISSUE: "false" | ||
| GH_AW_WORKFLOW_NAME: "PR Review Panel" | ||
| with: | ||
| script: | | ||
| const path = require('path'); | ||
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | ||
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require(path.join(actionsDir, 'check_version_updates.cjs')); | ||
| await main(); | ||
| - name: Compute current body text | ||
| id: sanitized | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| env: | ||
| GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.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,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.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,patch-diff.githubusercontent.com,patchdiff.githubusercontent.com,ppa.launchpad.net,raw.githubusercontent.com,s.symcb.com,s.symcd.com,security.ubuntu.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com" | ||
| with: | ||
| script: | | ||
| const path = require('path'); | ||
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | ||
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require(path.join(actionsDir, 'compute_text.cjs')); | ||
| await main(); | ||
| - name: Log runtime features | ||
| if: ${{ contains(toJSON(vars), '"GH_AW_RUNTIME_FEATURES":') }} | ||
| run: bash "${RUNNER_TEMP}/gh-aw/actions/log_runtime_features_summary.sh" | ||
| - name: Create prompt with built-in context | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| env: | ||
| GH_AW_ACTIONS_DIR: ${{ runner.temp }}/gh-aw/actions | ||
| GH_AW_PROMPT: ${{ runner.temp }}/gh-aw/aw-prompts/prompt.txt | ||
| GH_AW_SAFE_OUTPUTS: ${{ runner.temp }}/gh-aw/safeoutputs/outputs.jsonl | ||
| GH_AW_PROMPT_CONFIG: "{\"items\":[{\"content_env\":\"GH_AW_PROMPT_CONTENT_0000\"},{\"file\":\"xpia.md\"},{\"file\":\"temp_folder_prompt.md\"},{\"file\":\"markdown.md\"},{\"file\":\"safe_outputs_prompt.md\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0001\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0002\"},{\"file\":\"mcp_cli_tools_with_safeoutputs_prompt.md\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0003\"},{\"file\":\"github_mcp_tools_with_safeoutputs_prompt.md\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0004\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0005\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0006\"}]}" | ||
| GH_AW_EXPR_1A3A194A: ${{ github.event.discussion.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'discussion' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} | ||
| GH_AW_EXPR_463A214A: ${{ github.event.pull_request.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'pull_request' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} | ||
| GH_AW_EXPR_802A9F6A: ${{ github.event.issue.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'issue' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} | ||
| GH_AW_EXPR_A0E5D436: ${{ github.event.pull_request.number || inputs.pr_number }} | ||
| GH_AW_EXPR_FF1D34CE: ${{ github.event.comment.id || fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').comment_id }} | ||
| GH_AW_GITHUB_ACTOR: ${{ github.actor }} | ||
| GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} | ||
| GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} | ||
| GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} | ||
| GH_AW_INPUTS_PR_NUMBER: ${{ inputs.pr_number }} | ||
| GH_AW_PROMPT_CONTENT_0000: "<system>\n" | ||
| GH_AW_PROMPT_CONTENT_0001: "<safe-output-tools>\nTools: add_comment(max:2), remove_labels(max:3), missing_tool, missing_data, noop\n" | ||
| GH_AW_PROMPT_CONTENT_0002: "</safe-output-tools>\n" | ||
| GH_AW_PROMPT_CONTENT_0003: "<github-context>\nThe following GitHub context information is available for this workflow:\n{{#if github.actor}}\n- **actor**: __GH_AW_GITHUB_ACTOR__\n{{/if}}\n{{#if github.repository}}\n- **repository**: __GH_AW_GITHUB_REPOSITORY__\n{{/if}}\n{{#if github.workspace}}\n- **workspace**: __GH_AW_GITHUB_WORKSPACE__\n{{/if}}\n{{#if github.event.issue.number || (github.aw.context.item_type == 'issue' && github.aw.context.item_number)}}\n- **issue-number**: #__GH_AW_EXPR_802A9F6A__\n{{/if}}\n{{#if github.event.discussion.number || (github.aw.context.item_type == 'discussion' && github.aw.context.item_number)}}\n- **discussion-number**: #__GH_AW_EXPR_1A3A194A__\n{{/if}}\n{{#if github.event.pull_request.number || (github.aw.context.item_type == 'pull_request' && github.aw.context.item_number)}}\n- **pull-request-number**: #__GH_AW_EXPR_463A214A__\n{{/if}}\n{{#if github.event.comment.id || github.aw.context.comment_id}}\n- **comment-id**: __GH_AW_EXPR_FF1D34CE__\n{{/if}}\n{{#if github.run_id}}\n- **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__\n{{/if}}\n- **checkouts**: The following repositories have been checked out and are available in the workspace:\n - repo `__GH_AW_GITHUB_REPOSITORY__` → `$GITHUB_WORKSPACE` (cwd) [shallow clone, fetch-depth=1 (default)]\n - **Note**: If a branch you need is not in the list above and is not listed as an additional fetched ref, it has NOT been checked out. For private repositories you cannot fetch it. If the branch is required and not available, exit with an error and ask the user to add it to the `fetch:` option of the `checkout:` configuration (e.g., `fetch: [\"refs/pulls/open/*\"]` for all open PR refs, or `fetch: [\"main\", \"feature/my-branch\"]` for specific branches).\n - **Warning: No git credentials are available to the agent.** Credentials are\n intentionally removed after the checkout step for security. This means any git\n operation that needs to authenticate to the remote will fail. In private repositories, that includes:\n - `git fetch`, `git pull`, `git clone`, and `git push` (direct push, not via safe-output tools)\n - Checking out or switching to a remote branch that is not already fetched\n - Deepening a shallow clone (`git fetch --unshallow`)\n - On-demand blob fetches in partial/blobless clones (operations on files not in the initial checkout)\n Do NOT attempt to configure credentials, run `git credential fill`, or modify `.gitconfig` —\n authentication will not succeed. If you encounter credential prompts or authentication errors,\n stop immediately and report the limitation rather than spending turns trying to work around it.\n</github-context>\n\n" | ||
| GH_AW_PROMPT_CONTENT_0004: "</system>\n" | ||
| GH_AW_PROMPT_CONTENT_0005: "\n" | ||
| GH_AW_PROMPT_CONTENT_0006: "{{#runtime-import .github/workflows/pr-review-panel.md}}\n" | ||
| with: | ||
| script: | | ||
| const { setupGlobals } = require(process.env.GH_AW_ACTIONS_DIR + '/setup_globals.cjs'); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require(process.env.GH_AW_ACTIONS_DIR + '/create_prompt.cjs'); | ||
| await main(core); | ||
| - name: Interpolate variables and render templates | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| env: | ||
| GH_AW_PROMPT: ${{ runner.temp }}/gh-aw/aw-prompts/prompt.txt | ||
| GH_AW_ENGINE_ID: "copilot" | ||
| GH_AW_EXPR_A0E5D436: ${{ github.event.pull_request.number || inputs.pr_number }} | ||
| GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} | ||
| GH_AW_INPUTS_PR_NUMBER: ${{ inputs.pr_number }} | ||
| with: | ||
| script: | | ||
| const path = require('path'); | ||
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | ||
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require(path.join(actionsDir, 'interpolate_prompt.cjs')); | ||
| await main(); | ||
| - name: Substitute placeholders | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| env: | ||
| GH_AW_PROMPT: ${{ runner.temp }}/gh-aw/aw-prompts/prompt.txt | ||
| GH_AW_EXPR_1A3A194A: ${{ github.event.discussion.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'discussion' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} | ||
| GH_AW_EXPR_463A214A: ${{ github.event.pull_request.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'pull_request' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} | ||
| GH_AW_EXPR_802A9F6A: ${{ github.event.issue.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'issue' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} | ||
| GH_AW_EXPR_A0E5D436: ${{ github.event.pull_request.number || inputs.pr_number }} | ||
| GH_AW_EXPR_FF1D34CE: ${{ github.event.comment.id || fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').comment_id }} | ||
| GH_AW_GITHUB_ACTOR: ${{ github.actor }} | ||
| GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} | ||
| GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} | ||
| GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} | ||
| GH_AW_INPUTS_PR_NUMBER: ${{ inputs.pr_number }} | ||
| GH_AW_MCP_CLI_SERVERS_LIST: "- `github` — run `github --help` to see available tools\n- `safeoutputs` — run `safeoutputs --help` to see available tools" | ||
| GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }} | ||
| with: | ||
| script: | | ||
| const path = require('path'); | ||
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | ||
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const substitutePlaceholders = require(path.join(actionsDir, 'substitute_placeholders.cjs')); | ||
| // Call the substitution function | ||
| return await substitutePlaceholders({ | ||
| file: process.env.GH_AW_PROMPT, | ||
| substitutions: { | ||
| GH_AW_EXPR_1A3A194A: process.env.GH_AW_EXPR_1A3A194A, | ||
| GH_AW_EXPR_463A214A: process.env.GH_AW_EXPR_463A214A, | ||
| GH_AW_EXPR_802A9F6A: process.env.GH_AW_EXPR_802A9F6A, | ||
| GH_AW_EXPR_A0E5D436: process.env.GH_AW_EXPR_A0E5D436, | ||
| GH_AW_EXPR_FF1D34CE: process.env.GH_AW_EXPR_FF1D34CE, | ||
| GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR, | ||
| GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY, | ||
| GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID, | ||
| GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE, | ||
| GH_AW_INPUTS_PR_NUMBER: process.env.GH_AW_INPUTS_PR_NUMBER, | ||
| GH_AW_MCP_CLI_SERVERS_LIST: process.env.GH_AW_MCP_CLI_SERVERS_LIST, | ||
| GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED | ||
| } | ||
| }); | ||
| - name: Validate prompt placeholders | ||
| env: | ||
| GH_AW_PROMPT: ${{ runner.temp }}/gh-aw/aw-prompts/prompt.txt | ||
| run: | | ||
| bash "${RUNNER_TEMP}/gh-aw/actions/validate_prompt_placeholders.sh" | ||
| - name: Print prompt | ||
| env: | ||
| GH_AW_PROMPT: ${{ runner.temp }}/gh-aw/aw-prompts/prompt.txt | ||
| run: | | ||
| bash "${RUNNER_TEMP}/gh-aw/actions/print_prompt_summary.sh" | ||
| - name: Upload info artifact | ||
| if: success() || failure() | ||
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | ||
| with: | ||
| name: info | ||
| path: /tmp/gh-aw/aw_info.json | ||
| if-no-files-found: ignore | ||
| - name: Stage prompt files for artifact upload | ||
| run: | | ||
| mkdir -p /tmp/gh-aw/aw-prompts | ||
| cp -a "${RUNNER_TEMP}/gh-aw/aw-prompts/." /tmp/gh-aw/aw-prompts/ | ||
| - name: Upload activation artifact | ||
| if: success() || failure() | ||
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | ||
| with: | ||
| name: activation | ||
| include-hidden-files: true | ||
| path: | | ||
| /tmp/gh-aw/aw_info.json | ||
| /tmp/gh-aw/models.json | ||
| /tmp/gh-aw/aw-prompts/prompt.txt | ||
| /tmp/gh-aw/aw-prompts/prompt-template.txt | ||
| /tmp/gh-aw/aw-prompts/prompt-import-tree.json | ||
| /tmp/gh-aw/github_rate_limits.jsonl | ||
| /tmp/gh-aw/base | ||
| /tmp/gh-aw/.github/agents | ||
| /tmp/gh-aw/.github/skills | ||
| if-no-files-found: ignore | ||
| retention-days: 1 | ||
| agent: | ||
| needs: | ||
| - activation | ||
| - apm | ||
| - apm-prep | ||
| if: needs.activation.outputs.daily_ai_credits_exceeded != 'true' | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: read | ||
| issues: read | ||
| pull-requests: read | ||
| timeout-minutes: 60 | ||
| 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_PR_HEAD_BASE_BRANCH: "" | ||
| GH_AW_PR_HEAD_BASE_PR_NUMBER: "" | ||
| GH_AW_PR_HEAD_BASE_REF: "" | ||
| GH_AW_PR_HEAD_BASE_REPO: "" | ||
| GH_AW_PR_HEAD_BASE_SHA: "" | ||
| GH_AW_PR_HEAD_REPO: "" | ||
| GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} | ||
| GH_AW_WORKFLOW_ID_SANITIZED: prreviewpanel | ||
| outputs: | ||
| agentic_engine_timeout: ${{ steps.detect-agent-errors.outputs.agentic_engine_timeout || 'false' }} | ||
| ai_credits_rate_limit_error: ${{ steps.parse-mcp-gateway.outputs.ai_credits_rate_limit_error || 'false' }} | ||
| aic: ${{ steps.parse-token-usage.outputs.aic }} | ||
| ambient_context: ${{ steps.parse-token-usage.outputs.ambient_context }} | ||
| effective_tokens: ${{ steps.parse-mcp-gateway.outputs.effective_tokens }} | ||
| has_patch: ${{ steps.collect_output.outputs.has_patch }} | ||
| http_400_response_error: ${{ steps.detect-agent-errors.outputs.http_400_response_error || 'false' }} | ||
| inference_access_error: ${{ steps.detect-agent-errors.outputs.inference_access_error || 'false' }} | ||
| invocation_cap_exceeded: ${{ steps.detect-agent-errors.outputs.invocation_cap_exceeded || 'false' }} | ||
| max_cache_misses_exceeded: ${{ steps.detect-agent-errors.outputs.max_cache_misses_exceeded || 'false' }} | ||
| mcp_policy_error: ${{ steps.detect-agent-errors.outputs.mcp_policy_error || 'false' }} | ||
| missing_model_pricing_error: ${{ steps.detect-agent-errors.outputs.missing_model_pricing_error || 'false' }} | ||
| missing_model_pricing_model_name: ${{ steps.detect-agent-errors.outputs.missing_model_pricing_model_name || '' }} | ||
| model: ${{ needs.activation.outputs.model }} | ||
| model_not_supported_error: ${{ steps.detect-agent-errors.outputs.model_not_supported_error || 'false' }} | ||
| output: ${{ steps.collect_output.outputs.output }} | ||
| output_types: ${{ steps.collect_output.outputs.output_types }} | ||
| setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }} | ||
| setup-span-id: ${{ steps.setup.outputs.span-id }} | ||
| setup-trace-id: ${{ steps.setup.outputs.trace-id }} | ||
| shell_expansion_guard_rejected: ${{ steps.detect-agent-errors.outputs.shell_expansion_guard_rejected || 'false' }} | ||
| unknown_model_ai_credits: ${{ steps.parse-mcp-gateway.outputs.unknown_model_ai_credits || 'false' }} | ||
| steps: | ||
| - name: Setup Scripts | ||
| id: setup | ||
| uses: github/gh-aw-actions/setup@045beb2d14bda8d0c1f2e83b41527f63770d2855 # v0.89.15 | ||
| with: | ||
| destination: ${{ runner.temp }}/gh-aw/actions | ||
| job-name: ${{ github.job }} | ||
| trace-id: ${{ needs.activation.outputs.setup-trace-id }} | ||
| parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} | ||
| env: | ||
| GH_AW_SETUP_WORKFLOW_NAME: "PR Review Panel" | ||
| GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/pr-review-panel.lock.yml@${{ github.ref }} | ||
| GH_AW_INFO_VERSION: "1.0.83" | ||
| GH_AW_INFO_AWF_VERSION: "v0.28.16" | ||
| GH_AW_INFO_ENGINE_ID: "copilot" | ||
| - name: Set runtime paths | ||
| id: set-runtime-paths | ||
| env: | ||
| GH_AW_RUNNER_TOOL_CACHE: ${{ runner.tool_cache }} | ||
| run: | # zizmor: ignore[github-env] - runner.tool_cache is set by GitHub Actions, not user input. | ||
| if [ -z "${RUNNER_TOOL_CACHE:-}" ]; then | ||
| echo "RUNNER_TOOL_CACHE=${GH_AW_RUNNER_TOOL_CACHE}" >> "$GITHUB_ENV" | ||
| fi | ||
| { | ||
| echo "GH_AW_SAFE_OUTPUTS=${RUNNER_TEMP}/gh-aw/safeoutputs/outputs.jsonl" | ||
| echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" | ||
| echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/tools.json" | ||
| } >> "$GITHUB_OUTPUT" | ||
| - name: Mask OTLP telemetry headers | ||
| run: bash "${RUNNER_TEMP}/gh-aw/actions/mask_otlp_headers.sh" | ||
| - name: Check OTLP telemetry configuration | ||
| run: bash "${RUNNER_TEMP}/gh-aw/actions/check_otlp_default_credentials.sh" | ||
| - name: Checkout repository | ||
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | ||
| with: | ||
| persist-credentials: false | ||
| ref: ${{ github.event.pull_request.base.sha }} | ||
| - name: Initialize agent execution evidence | ||
| run: | | ||
| mkdir -p "/tmp/gh-aw" | ||
| evidence_tmp="/tmp/gh-aw/agent_execution.json.tmp" | ||
| printf '{"version":1,"component":"agent","run_id":%s,"run_attempt":%s,"state":"not_started"}\n' "$GITHUB_RUN_ID" "$GITHUB_RUN_ATTEMPT" > "$evidence_tmp" | ||
| mv "$evidence_tmp" "/tmp/gh-aw/agent_execution.json" | ||
| - name: Create gh-aw temp directory | ||
| run: bash "${RUNNER_TEMP}/gh-aw/actions/create_gh_aw_tmp_dir.sh" | ||
| - name: Configure gh CLI for GitHub Enterprise | ||
| run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_gh_for_ghe.sh" | ||
| env: | ||
| GH_TOKEN: ${{ github.token }} | ||
| - name: Download activation artifact | ||
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | ||
| with: | ||
| name: activation | ||
| path: /tmp/gh-aw | ||
| - name: Download APM bundle artifacts (all groups) | ||
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | ||
| with: | ||
| merge-multiple: false | ||
| path: /tmp/gh-aw/apm-bundles | ||
| pattern: ${{ needs.activation.outputs.artifact_prefix }}apm-* | ||
| - env: | ||
| ARTIFACT_PREFIX: ${{ needs.activation.outputs.artifact_prefix }} | ||
| EXPECTED_MATRIX: ${{ needs.apm-prep.outputs.matrix }} | ||
| name: Normalise bundle layout (single-artifact flatten workaround) | ||
| run: "set -euo pipefail\n# actions/download-artifact (>=v5) flattens contents directly into `path/`\n# whenever exactly one artifact matches the pattern, ignoring\n# `merge-multiple: false`. Re-shape into the per-group subdir layout so\n# downstream validation sees a stable structure regardless of matrix size.\n# Upstream reference:\n# https://github.com/actions/download-artifact/blob/v8.0.1/src/download-artifact.ts\n# (see the `isSingleArtifactDownload || mergeMultiple || artifacts.length === 1`\n# branch). Remove this step once download-artifact stops flattening or\n# exposes an opt-out.\nexpected_count=$(echo \"$EXPECTED_MATRIX\" | jq '.group // [] | length')\nif [ \"$expected_count\" -eq 1 ]; then\n group_id=$(echo \"$EXPECTED_MATRIX\" | jq -r '.group[0].id')\n # Defence-in-depth: group_id is interpolated into a shell path. apm-prep\n # produces a sanitised id today, but enforce a strict allowlist here so\n # any future schema drift cannot smuggle traversal sequences.\n if ! printf '%s' \"$group_id\" | grep -Eq '^[A-Za-z0-9_-]+$'; then\n echo \"::error::unsafe group_id '$group_id' (must match ^[A-Za-z0-9_-]+$)\"\n exit 1\n fi\n group_dir=\"/tmp/gh-aw/apm-bundles/${ARTIFACT_PREFIX}apm-${group_id}\"\n if [ ! -d \"$group_dir\" ]; then\n mkdir -p \"$group_dir\"\n find /tmp/gh-aw/apm-bundles -mindepth 1 -maxdepth 1 ! -path \"$group_dir\" -exec mv {} \"$group_dir/\" \\;\n fi\nfi\n" | ||
| - env: | ||
| ARTIFACT_PREFIX: ${{ needs.activation.outputs.artifact_prefix }} | ||
| EXPECTED_MATRIX: ${{ needs.apm-prep.outputs.matrix }} | ||
| name: Validate downloaded bundles match matrix manifest | ||
| run: "set -euo pipefail\nexpected=$(echo \"$EXPECTED_MATRIX\" | jq -r --arg prefix \"$ARTIFACT_PREFIX\" '.group | map($prefix + \"apm-\" + .id) | sort | .[]')\nactual=$(ls /tmp/gh-aw/apm-bundles | sort)\nmissing=$(comm -23 <(echo \"$expected\") <(echo \"$actual\") || true)\nunexpected=$(comm -13 <(echo \"$expected\") <(echo \"$actual\") || true)\nif [ -n \"$missing\" ]; then\n echo \"::error::missing APM bundles (group did not pack successfully): $missing\"\n exit 1\nfi\nif [ -n \"$unexpected\" ]; then\n echo \"::error::unexpected artifact in apm bundle download (collision attack?): $unexpected\"\n exit 1\nfi\n" | ||
| - id: bundles | ||
| name: Build bundle list | ||
| run: "set -euo pipefail\nmapfile -t list < <(find /tmp/gh-aw/apm-bundles \\( -name '*.tar.gz' -o -name '*.zip' \\) | sort)\n[ ${#list[@]} -gt 0 ] || { echo '::error::no apm bundles found'; exit 1; }\nprintf '%s\\n' \"${list[@]}\" > /tmp/gh-aw/apm-bundle-list.txt\n" | ||
| - name: Restore APM packages (all bundles) | ||
| uses: microsoft/apm-action@d723bb64ed70c135bbaf87d126b721dd2dae0439 # v1.10.0 | ||
| with: | ||
| apm-version: 0.28.0 | ||
| bundles-file: /tmp/gh-aw/apm-bundle-list.txt | ||
| - name: Setup Ruby | ||
| uses: ruby/setup-ruby@984c0c890880bbf811283d6f09c4607c62d210a4 # v1.323.0 | ||
| with: | ||
| ruby-version: ruby-3.3 | ||
| - name: Configure Git credentials | ||
| env: | ||
| GITHUB_REPOSITORY: ${{ github.repository }} | ||
| GITHUB_SERVER_URL: ${{ github.server_url }} | ||
| GITHUB_TOKEN: ${{ github.token }} | ||
| run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_git_credentials.sh" | ||
| - name: Install GitHub Copilot CLI | ||
| run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" | ||
| env: | ||
| GH_HOST: github.com | ||
| GH_AW_COMPILED_VERSION: v0.89.15 | ||
| - name: Install AWF binary | ||
| run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.28.16 --rootless | ||
| - name: Determine automatic lockdown mode for GitHub MCP Server | ||
| id: determine-automatic-lockdown | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 (source v9) | ||
| env: | ||
| GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} | ||
| GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} | ||
| GH_AW_GITHUB_MIN_INTEGRITY: 'unapproved' | ||
| GH_AW_GITHUB_REPOS: '["microsoft/apm"]' | ||
| with: | ||
| script: | | ||
| const path = require('path'); | ||
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | ||
| const determineAutomaticLockdown = require(path.join(actionsDir, 'determine_automatic_lockdown.cjs')); | ||
| await determineAutomaticLockdown(github, context, core); | ||
| - name: Parse integrity filter lists | ||
| id: parse-guard-vars | ||
| env: | ||
| GH_AW_BLOCKED_USERS_VAR: ${{ vars.GH_AW_GITHUB_BLOCKED_USERS || '' }} | ||
| GH_AW_TRUSTED_USERS_VAR: ${{ vars.GH_AW_GITHUB_TRUSTED_USERS || '' }} | ||
| GH_AW_APPROVAL_LABELS_VAR: ${{ vars.GH_AW_GITHUB_APPROVAL_LABELS || '' }} | ||
| run: bash "${RUNNER_TEMP}/gh-aw/actions/parse_guard_list.sh" | ||
| - name: Restore inline sub-agents from activation artifact | ||
| env: | ||
| GH_AW_SUB_AGENT_DIR: ".github/agents" | ||
| GH_AW_SUB_AGENT_EXT: ".agent.md" | ||
| run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_inline_sub_agents.sh" | ||
| - name: Restore inline skills from activation artifact | ||
| env: | ||
| GH_AW_SKILL_DIR: ".github/skills" | ||
| run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_inline_skills.sh" | ||
| - name: Download container images | ||
| run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.28.16@sha256:57a3e27388a6d7d32719088581e52567727fa0bf2f0d477bf565b0c4baa12a3f ghcr.io/github/gh-aw-firewall/api-proxy:0.28.16@sha256:cd400948638ffe1b87ec319abf73fa29b6ac9881b015da58bba971c4cc13a400 ghcr.io/github/gh-aw-firewall/squid:0.28.16@sha256:452197f2e241b2cda8eb0b5674960aa8ca544a64dc242e24f8263c3df6452919 ghcr.io/github/gh-aw-mcpg:v0.4.21@sha256:f26f665840660581510746ed84f5fc706f4ca8c48833757dfb4d72d86587201e ghcr.io/github/gh-aw-node@sha256:0daa8971fa4732b647150cb6524a6b0804b68d5d24f6f58b5dd1af23bd63fb23 ghcr.io/github/github-mcp-server:v1.12.1@sha256:0ba840c46a237879c8300e7fddb0b6347f20e029ccb9cbe2ce4a943daa1ff560 | ||
| - name: Prepare Safe Outputs Directories | ||
| run: | | ||
| mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs" | ||
| mkdir -p /tmp/gh-aw/safeoutputs | ||
| mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs | ||
| - name: Generate Safe Outputs Config | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| env: | ||
| GH_AW_FILE_ROOT: "${{ runner.temp }}/gh-aw" | ||
| GH_AW_FILE_CONFIG: "{\"files\":[{\"path\":\"safeoutputs/config.json\",\"content_env\":\"GH_AW_SAFE_OUTPUTS_CONFIG\"}]}" | ||
| GH_AW_SAFE_OUTPUTS_CONFIG: "{\"add_comment\":{\"max\":2},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"remove_labels\":{\"allowed\":[\"panel-review\",\"panel-approved\",\"panel-rejected\"],\"max\":3},\"report_incomplete\":{}}" | ||
| with: | ||
| script: | | ||
| const path = require('path'); | ||
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | ||
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require(path.join(actionsDir, 'create_files.cjs')); | ||
| await main(); | ||
| - name: Generate Safe Outputs Tools | ||
| env: | ||
| GH_AW_TOOLS_META_JSON: | | ||
| { | ||
| "description_suffixes": { | ||
| "add_comment": " CONSTRAINTS: Maximum 2 comment(s) can be added. Supports reply_to_id for discussion threading.", | ||
| "remove_labels": " CONSTRAINTS: Maximum 3 label(s) can be removed. Only these labels can be removed: [panel-review panel-approved panel-rejected]." | ||
| }, | ||
| "repo_params": {}, | ||
| "dynamic_tools": [] | ||
| } | ||
| GH_AW_VALIDATION_JSON: | | ||
| { | ||
| "add_comment": { | ||
| "defaultMax": 1, | ||
| "fields": { | ||
| "body": { | ||
| "required": true, | ||
| "type": "string", | ||
| "sanitize": true, | ||
| "maxLength": 65000 | ||
| }, | ||
| "comment_id": { | ||
| "optionalPositiveInteger": true | ||
| }, | ||
| "item_number": { | ||
| "issueOrPRNumber": true | ||
| }, | ||
| "pr": { | ||
| "issueOrPRNumber": true | ||
| }, | ||
| "pr_number": { | ||
| "issueOrPRNumber": true | ||
| }, | ||
| "reply_to_id": { | ||
| "type": "string", | ||
| "maxLength": 256 | ||
| }, | ||
| "repo": { | ||
| "type": "string", | ||
| "maxLength": 256 | ||
| }, | ||
| "target": { | ||
| "type": "string", | ||
| "enum": [ | ||
| "status" | ||
| ] | ||
| }, | ||
| "temporary_id": { | ||
| "type": "string", | ||
| "pattern": "^#?aw_[A-Za-z0-9_]{3,12}$" | ||
| } | ||
| } | ||
| }, | ||
| "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 | ||
| } | ||
| } | ||
| }, | ||
| "remove_labels": { | ||
| "defaultMax": 5, | ||
| "fields": { | ||
| "item_number": { | ||
| "issueNumberOrTemporaryId": true | ||
| }, | ||
| "labels": { | ||
| "required": true, | ||
| "type": "array" | ||
| }, | ||
| "repo": { | ||
| "type": "string", | ||
| "maxLength": 256 | ||
| } | ||
| } | ||
| }, | ||
| "report_incomplete": { | ||
| "defaultMax": 5, | ||
| "fields": { | ||
| "details": { | ||
| "type": "string", | ||
| "sanitize": true, | ||
| "maxLength": 65000 | ||
| }, | ||
| "reason": { | ||
| "required": true, | ||
| "type": "string", | ||
| "sanitize": true, | ||
| "maxLength": 1024 | ||
| } | ||
| } | ||
| } | ||
| } | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| with: | ||
| script: | | ||
| const path = require('path'); | ||
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | ||
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require(path.join(actionsDir, 'generate_safe_outputs_tools.cjs')); | ||
| await main(); | ||
| - name: Start MCP Gateway | ||
| id: start-mcp-gateway | ||
| env: | ||
| GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST: ${{ vars.GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST || 'true' }} | ||
| GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} | ||
| GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS_CONFIG_PATH }} | ||
| GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS_TOOLS_PATH }} | ||
| GH_AW_SINK_VISIBILITY: ${{ steps.determine-automatic-lockdown.outputs.visibility }} | ||
| GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | ||
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| run: | | ||
| set -eo pipefail | ||
| mkdir -p "${RUNNER_TEMP}/gh-aw/mcp-config" | ||
| if [ -n "${GITHUB_EVENT_PATH:-}" ] && [ -r "${GITHUB_EVENT_PATH}" ]; then | ||
| GH_AW_SAFEOUTPUTS_EVENT_PATH="${RUNNER_TEMP}/gh-aw/safeoutputs/github_event.json" | ||
| cp "${GITHUB_EVENT_PATH}" "${GH_AW_SAFEOUTPUTS_EVENT_PATH}" | ||
| export GITHUB_EVENT_PATH="${GH_AW_SAFEOUTPUTS_EVENT_PATH}" | ||
| fi | ||
| # Export gateway environment variables for MCP config and gateway script | ||
| export MCP_GATEWAY_PORT="8080" | ||
| export MCP_GATEWAY_DOMAIN="awmg-mcpg" | ||
| export MCP_GATEWAY_HOST_DOMAIN="localhost" | ||
| MCP_GATEWAY_AGENT_ID=$(openssl rand -base64 45 | tr -d '/+=') | ||
| echo "::add-mask::${MCP_GATEWAY_AGENT_ID}" | ||
| export MCP_GATEWAY_AGENT_ID | ||
| export MCP_GATEWAY_PAYLOAD_DIR="/tmp/gh-aw/mcp-payloads" | ||
| mkdir -p "${MCP_GATEWAY_PAYLOAD_DIR}" | ||
| export MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD="524288" | ||
| export MCP_GATEWAY_ALLOWED_MOUNT_ROOTS="${GITHUB_WORKSPACE}:rw,${RUNNER_TEMP}/gh-aw:ro,${RUNNER_TEMP}/gh-aw/safeoutputs:rw,/opt:ro,/tmp:rw,/usr/bin/gh:ro" | ||
| export GH_AW_PR_HEAD_BASE_BRANCH="${GH_AW_PR_HEAD_BASE_BRANCH:-}" | ||
| export GH_AW_PR_HEAD_BASE_SHA="${GH_AW_PR_HEAD_BASE_SHA:-}" | ||
| export GH_AW_PR_HEAD_BASE_REPO="${GH_AW_PR_HEAD_BASE_REPO:-}" | ||
| export GH_AW_PR_HEAD_BASE_PR_NUMBER="${GH_AW_PR_HEAD_BASE_PR_NUMBER:-}" | ||
| export GH_AW_PR_HEAD_BASE_REF="${GH_AW_PR_HEAD_BASE_REF:-}" | ||
| export GH_AW_PR_HEAD_REPO="${GH_AW_PR_HEAD_REPO:-}" | ||
| export DEBUG="*" | ||
| export GH_AW_ENGINE="copilot" | ||
| MCP_GATEWAY_UID=$(id -u 2>/dev/null || echo '0') | ||
| MCP_GATEWAY_GID=$(id -g 2>/dev/null || echo '0') | ||
| source "${RUNNER_TEMP}/gh-aw/actions/resolve_docker_socket_gid.sh" | ||
| export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network bridge -p 127.0.0.1:'"${MCP_GATEWAY_PORT}"':'"${MCP_GATEWAY_PORT}"' --name awmg-mcpg --add-host host.docker.internal:host-gateway --user '"${MCP_GATEWAY_UID}"':'"${MCP_GATEWAY_GID}"' --group-add '"${DOCKER_SOCK_GID}"' -v '"${DOCKER_SOCK_PATH}"':/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_AGENT_ID -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DOCKER_HOST=unix:///var/run/docker.sock -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_PR_HEAD_BASE_BRANCH -e GH_AW_PR_HEAD_BASE_SHA -e GH_AW_PR_HEAD_BASE_REPO -e GH_AW_PR_HEAD_BASE_PR_NUMBER -e GH_AW_PR_HEAD_BASE_REF -e GH_AW_PR_HEAD_REPO -e GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GH_AW_SINK_VISIBILITY -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e RUNNER_TEMP -e RUNNER_TOOL_CACHE -e MCP_GATEWAY_ALLOWED_MOUNT_ROOTS -e GITHUB_AW_OTEL_TRACE_ID -e GITHUB_AW_OTEL_PARENT_SPAN_ID -e OTEL_EXPORTER_OTLP_HEADERS -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw -v '"${RUNNER_TEMP}"'/gh-aw/safeoutputs:'"${RUNNER_TEMP}"'/gh-aw/safeoutputs:rw ghcr.io/github/gh-aw-mcpg:v0.4.21' | ||
| mkdir -p "$HOME/.copilot" | ||
| GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) | ||
| cat << GH_AW_MCP_CONFIG_fed8c914a555c02b_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" | ||
| { | ||
| "mcpServers": { | ||
| "github": { | ||
| "type": "stdio", | ||
| "container": "ghcr.io/github/github-mcp-server:v1.12.1", | ||
| "env": { | ||
| "GITHUB_FEATURES": "fields_param", | ||
| "GITHUB_HOST": "${GITHUB_SERVER_URL}", | ||
| "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_MCP_SERVER_TOKEN}", | ||
| "GITHUB_READ_ONLY": "1", | ||
| "GITHUB_TOOLSETS": "context,repos,issues,pull_requests" | ||
| }, | ||
| "guard-policies": { | ||
| "allow-only": { | ||
| "approval-labels": ${{ steps.parse-guard-vars.outputs.approval_labels }}, | ||
| "blocked-users": ${{ steps.parse-guard-vars.outputs.blocked_users }}, | ||
| "min-integrity": "unapproved", | ||
| "repos": [ | ||
| "microsoft/apm" | ||
| ], | ||
| "trusted-users": ${{ steps.parse-guard-vars.outputs.trusted_users }} | ||
| } | ||
| } | ||
| }, | ||
| "safeoutputs": { | ||
| "type": "stdio", | ||
| "container": "ghcr.io/github/gh-aw-node", | ||
| "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw", "${RUNNER_TEMP}/gh-aw/safeoutputs:${RUNNER_TEMP}/gh-aw/safeoutputs:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], | ||
| "args": ["-w", "\${GITHUB_WORKSPACE}"], | ||
| "entrypoint": "sh", | ||
| "entrypointArgs": ["-c", "sh ${RUNNER_TEMP}/gh-aw/safeoutputs/start_safe_outputs_mcp.sh"], | ||
| "env": { | ||
| "DEBUG": "*", | ||
| "DEFAULT_BRANCH": "\${DEFAULT_BRANCH}", | ||
| "GH_AW_ASSETS_ALLOWED_EXTS": "\${GH_AW_ASSETS_ALLOWED_EXTS}", | ||
| "GH_AW_ASSETS_BRANCH": "\${GH_AW_ASSETS_BRANCH}", | ||
| "GH_AW_ASSETS_MAX_SIZE_KB": "\${GH_AW_ASSETS_MAX_SIZE_KB}", | ||
| "GH_AW_MCP_LOG_DIR": "\${GH_AW_MCP_LOG_DIR}", | ||
| "GH_AW_SAFE_OUTPUTS": "\${GH_AW_SAFE_OUTPUTS}", | ||
| "GH_AW_SAFE_OUTPUTS_CONFIG_PATH": "\${GH_AW_SAFE_OUTPUTS_CONFIG_PATH}", | ||
| "GH_AW_SAFE_OUTPUTS_TOOLS_PATH": "\${GH_AW_SAFE_OUTPUTS_TOOLS_PATH}", | ||
| "GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST": "\${GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST}", | ||
| "GH_AW_PR_HEAD_BASE_BRANCH": "\${GH_AW_PR_HEAD_BASE_BRANCH}", | ||
| "GH_AW_PR_HEAD_BASE_SHA": "\${GH_AW_PR_HEAD_BASE_SHA}", | ||
| "GH_AW_PR_HEAD_BASE_REPO": "\${GH_AW_PR_HEAD_BASE_REPO}", | ||
| "GH_AW_PR_HEAD_BASE_PR_NUMBER": "\${GH_AW_PR_HEAD_BASE_PR_NUMBER}", | ||
| "GH_AW_PR_HEAD_BASE_REF": "\${GH_AW_PR_HEAD_BASE_REF}", | ||
| "GH_AW_PR_HEAD_REPO": "\${GH_AW_PR_HEAD_REPO}", | ||
| "GITHUB_EVENT_NAME": "\${GITHUB_EVENT_NAME}", | ||
| "GITHUB_EVENT_PATH": "\${GITHUB_EVENT_PATH}", | ||
| "GITHUB_REPOSITORY": "\${GITHUB_REPOSITORY}", | ||
| "GITHUB_SHA": "\${GITHUB_SHA}", | ||
| "GITHUB_TOKEN": "\${GITHUB_TOKEN}", | ||
| "GITHUB_WORKSPACE": "\${GITHUB_WORKSPACE}", | ||
| "RUNNER_TEMP": "\${RUNNER_TEMP}" | ||
| }, | ||
| "guard-policies": { | ||
| "write-sink": { | ||
| "accept": [ | ||
| "private:microsoft/apm" | ||
| ], | ||
| "sink-visibility": "${GH_AW_SINK_VISIBILITY}" | ||
| } | ||
| } | ||
| } | ||
| }, | ||
| "gateway": { | ||
| "port": $MCP_GATEWAY_PORT, | ||
| "domain": "${MCP_GATEWAY_DOMAIN}", | ||
| "agentId": "${MCP_GATEWAY_AGENT_ID}", | ||
| "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}", | ||
| "startupTimeout": 120, | ||
| "opentelemetry": { | ||
| "endpoint": "${OTEL_EXPORTER_OTLP_ENDPOINT}", | ||
| "traceId": "${GITHUB_AW_OTEL_TRACE_ID}", | ||
| "spanId": "${GITHUB_AW_OTEL_PARENT_SPAN_ID}" | ||
| } | ||
| } | ||
| } | ||
| GH_AW_MCP_CONFIG_fed8c914a555c02b_EOF | ||
| - name: Mount MCP servers as CLIs | ||
| id: mount-mcp-clis | ||
| continue-on-error: true | ||
| env: | ||
| MCP_GATEWAY_AGENT_ID: ${{ steps.start-mcp-gateway.outputs.gateway-agent-id }} | ||
| MCP_GATEWAY_DOMAIN: ${{ steps.start-mcp-gateway.outputs.gateway-domain }} | ||
| MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }} | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| with: | ||
| script: | | ||
| const path = require('path'); | ||
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | ||
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | ||
| setupGlobals(core, github, context, exec, io); | ||
| const { main } = require(path.join(actionsDir, 'mount_mcp_as_cli.cjs')); | ||
| await main(); | ||
| - name: Clean credentials | ||
| continue-on-error: true | ||
| run: bash "${RUNNER_TEMP}/gh-aw/actions/clean_git_credentials.sh" | ||
| - name: Audit pre-agent workspace | ||
| id: pre_agent_audit | ||
| continue-on-error: true | ||
| run: bash "${RUNNER_TEMP}/gh-aw/actions/audit_pre_agent_workspace.sh" | ||
| - name: Execute GitHub Copilot CLI | ||
| id: agentic_execution | ||
| # Copilot CLI tool arguments (sorted): | ||
| timeout-minutes: 30 | ||
| run: | | ||
| set -o pipefail | ||
| printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt | ||
| trap 'gh_aw_exit_code=$?; mkdir -p /tmp/gh-aw >/dev/null 2>&1 || true; printf "%s" "$gh_aw_exit_code" > /tmp/gh-aw/agent_execution_exit_code.txt || true; rm -f "$HOME/.copilot/settings.json"; if [ "$gh_aw_exit_code" -ne 0 ]; then echo "::error::Agent execution exited with code $gh_aw_exit_code"; fi' EXIT | ||
| mkdir -p "$HOME/.copilot" | ||
| printf '%s' '{"builtInAgents":{"rubberDuck":false}}' > "$HOME/.copilot/settings.json" | ||
| export XDG_CONFIG_HOME="$HOME" | ||
| export GH_AW_MCP_CONFIG="$HOME/.copilot/mcp-config.json" | ||
| GH_AW_COPILOT_SRC="$(command -v copilot 2>/dev/null || true)" | ||
| if [ -z "$GH_AW_COPILOT_SRC" ] || [ ! -x "$GH_AW_COPILOT_SRC" ]; then | ||
| echo "GitHub Copilot CLI executable not found on PATH after installation" >&2 | ||
| exit 127 | ||
| fi | ||
| GH_AW_COPILOT_BIN="${RUNNER_TEMP}/gh-aw/bin/copilot" | ||
| mkdir -p "${RUNNER_TEMP}/gh-aw/bin" | ||
| if [ "$GH_AW_COPILOT_SRC" != "$GH_AW_COPILOT_BIN" ]; then | ||
| cp "$GH_AW_COPILOT_SRC" "$GH_AW_COPILOT_BIN" | ||
| fi | ||
| chmod 755 "$GH_AW_COPILOT_BIN" | ||
| touch /tmp/gh-aw/agent-step-summary.md | ||
| GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true) | ||
| export GH_AW_NODE_BIN | ||
| export COPILOT_API_KEY="$COPILOT_DUMMY_BYOK" | ||
| (umask 177 && touch /tmp/gh-aw/agent-stdio.log) | ||
| GH_AW_MAX_AI_CREDITS="${GH_AW_MAX_AI_CREDITS:-1000}" | ||
| if [[ ! "$GH_AW_MAX_AI_CREDITS" =~ ^[0-9]+$ ]]; then | ||
| GH_AW_MAX_AI_CREDITS="1000" | ||
| fi | ||
| printf '%s\n' "{\"\$schema\":\"https://github.com/github/gh-aw-firewall/releases/download/v0.28.16/awf-config.schema.json\",\"network\":{\"allowDomains\":[\"*.githubusercontent.com\",\"api.snapcraft.io\",\"archive.ubuntu.com\",\"azure.archive.ubuntu.com\",\"codeload.github.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\",\"docs.github.com\",\"github-cloud.githubusercontent.com\",\"github-cloud.s3.amazonaws.com\",\"github.blog\",\"github.com\",\"github.githubassets.com\",\"json-schema.org\",\"json.schemastore.org\",\"keyserver.ubuntu.com\",\"lfs.github.com\",\"objects.githubusercontent.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\",\"patch-diff.githubusercontent.com\",\"patchdiff.githubusercontent.com\",\"ppa.launchpad.net\",\"raw.githubusercontent.com\",\"s.symcb.com\",\"s.symcd.com\",\"security.ubuntu.com\",\"ts-crl.ws.symantec.com\",\"ts-ocsp.ws.symantec.com\",\"www.googleapis.com\"],\"isolation\":true,\"topologyAttach\":[\"awmg-mcpg\"]},\"apiProxy\":{\"enabled\":true,\"enableTokenSteering\":true,\"maxRuns\":500,\"maxAiCredits\":${GH_AW_MAX_AI_CREDITS},\"maxCacheMisses\":5,\"models\":{\"agent\":[\"sonnet-6x\",\"gpt-6\",\"gpt-5.4\",\"gpt-5.5\",\"gpt-5.6\",\"gpt-5.3\",\"gemini-pro\",\"any\"],\"antigravity\":[\"copilot/antigravity*\",\"google/antigravity*\",\"gemini/antigravity*\"],\"any\":[\"copilot/*\",\"anthropic/*\",\"openai/*\",\"google/*\",\"gemini/*\"],\"auto\":[\"copilot/auto\",\"large\"],\"claude\":[\"agent\"],\"codex\":[\"agent\"],\"coding\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\",\"gpt-5-codex\",\"kimi\"],\"computer-use\":[\"copilot/*computer-use*\",\"google/*computer-use*\",\"gemini/*computer-use*\",\"openai/*computer-use*\"],\"copilot\":[\"agent\"],\"deep-research\":[\"copilot/deep-research*\",\"copilot/o3-deep-research*\",\"copilot/o4-mini-deep-research*\",\"google/deep-research*\",\"gemini/deep-research*\",\"openai/o3-deep-research*\",\"openai/o4-mini-deep-research*\"],\"detection\":[\"small\"],\"evals\":[\"small\"],\"fable\":[\"copilot/*fable*\",\"anthropic/*fable*\"],\"gemini\":[\"agent\"],\"gemini-3-flash\":[\"copilot/gemini-3*flash*\",\"google/gemini-3*flash*\",\"gemini/gemini-3*flash*\"],\"gemini-3-pro\":[\"copilot/gemini-3*pro*\",\"google/gemini-3*pro*\",\"google/nano-banana*\",\"gemini/gemini-3*pro*\"],\"gemini-3.1-flash\":[\"copilot/gemini-3.1*flash*\",\"google/gemini-3.1*flash*\",\"gemini/gemini-3.1*flash*\"],\"gemini-3.1-pro\":[\"copilot/gemini-3.1*pro*\",\"google/gemini-3.1*pro*\",\"gemini/gemini-3.1*pro*\"],\"gemini-3.5-flash\":[\"copilot/gemini-3.5*flash*\",\"google/gemini-3.5*flash*\",\"gemini/gemini-3.5*flash*\"],\"gemini-3.6-flash\":[\"copilot/gemini-3.6*flash*\",\"google/gemini-3.6*flash*\",\"gemini/gemini-3.6*flash*\"],\"gemini-3.7-flash\":[\"copilot/gemini-3.7*flash*\",\"google/gemini-3.7*flash*\",\"gemini/gemini-3.7*flash*\"],\"gemini-flash\":[\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"],\"gemini-flash-lite\":[\"copilot/gemini-*flash*lite*\",\"google/gemini-*flash*lite*\",\"gemini/gemini-*flash*lite*\"],\"gemini-omni\":[\"copilot/gemini-omni*\",\"google/gemini-omni*\",\"gemini/gemini-omni*\"],\"gemini-pro\":[\"copilot/gemini-*pro*\",\"google/gemini-*pro*\",\"gemini/gemini-*pro*\"],\"gemma\":[\"copilot/gemma*\",\"google/gemma*\",\"gemini/gemma*\"],\"gpt-5\":[\"copilot/gpt-5*\",\"openai/gpt-5*\"],\"gpt-5-codex\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\"],\"gpt-5-mini\":[\"copilot/gpt-5*mini*\",\"openai/gpt-5*mini*\"],\"gpt-5-nano\":[\"copilot/gpt-5*nano*\",\"openai/gpt-5*nano*\"],\"gpt-5-pro\":[\"copilot/gpt-5*pro*\",\"openai/gpt-5*pro*\"],\"gpt-5.1\":[\"copilot/gpt-5.1*\",\"openai/gpt-5.1*\"],\"gpt-5.2\":[\"copilot/gpt-5.2*\",\"openai/gpt-5.2*\"],\"gpt-5.3\":[\"copilot/gpt-5.3*\",\"openai/gpt-5.3*\"],\"gpt-5.4\":[\"copilot/gpt-5.4*\",\"openai/gpt-5.4*\"],\"gpt-5.5\":[\"copilot/gpt-5.5*\",\"openai/gpt-5.5*\"],\"gpt-5.6\":[\"copilot/gpt-5.6*\",\"openai/gpt-5.6*\"],\"gpt-6\":[\"copilot/gpt-6*\",\"openai/gpt-6*\"],\"grok\":[\"copilot/*grok*\",\"openai/*grok*\"],\"haiku\":[\"copilot/*haiku*\",\"anthropic/*haiku*\"],\"image-generation\":[\"copilot/gpt-image*\",\"openai/gpt-image*\",\"openai/chatgpt-image*\",\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"google/imagen*\"],\"kimi\":[\"copilot/kimi*\",\"openai/kimi*\"],\"kiwi\":[\"copilot/kiwi*\",\"openai/kiwi*\"],\"large\":[\"sonnet\",\"gpt-6\",\"gpt-5-pro\",\"gpt-5\",\"gemini-pro\"],\"lyria\":[\"google/lyria*\",\"gemini/lyria*\",\"copilot/lyria*\"],\"mai-code\":[\"copilot/MAI-Code*\",\"copilot/mai-code*\",\"openai/MAI-Code*\"],\"mai-code-1-flash-picker\":[\"copilot/MAI-Code-1-Flash-picker*\",\"copilot/mai-code-1-flash-picker*\",\"openai/MAI-Code-1-Flash-picker*\"],\"mini\":[\"haiku\",\"gpt-5-mini\",\"gpt-5-nano\",\"gemini-flash-lite\"],\"nano-banana\":[\"copilot/nano-banana*\",\"google/nano-banana*\",\"gemini/nano-banana*\"],\"opus\":[\"copilot/*opus*\",\"anthropic/*opus*\"],\"opusplan\":[\"opus?effort=high\"],\"raptor-mini\":[\"copilot/raptor*\",\"openai/raptor*\"],\"reasoning\":[\"copilot/o1*\",\"copilot/o3*\",\"copilot/o4*\",\"openai/o1*\",\"openai/o3*\",\"openai/o4*\"],\"robotics\":[\"copilot/*robotics*\",\"google/*robotics*\",\"gemini/*robotics*\"],\"small\":[\"mini\"],\"small-agent\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash\"],\"sonnet\":[\"copilot/*sonnet*\",\"anthropic/*sonnet*\"],\"sonnet-6x\":[\"copilot/*sonnet-4.5*\",\"copilot/*sonnet-4.6*\",\"copilot/*sonnet-5*\",\"copilot/*sonnet-4-5-*\",\"anthropic/*sonnet-4-5-*\",\"copilot/*sonnet-4-6*\",\"anthropic/*sonnet-4-6*\",\"anthropic/*sonnet-5*\"],\"summarization\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash-lite\",\"mini\"],\"veo\":[\"google/veo*\",\"gemini/veo*\"],\"vision\":[\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"]}},\"container\":{\"imageTag\":\"0.28.16,squid=sha256:452197f2e241b2cda8eb0b5674960aa8ca544a64dc242e24f8263c3df6452919,agent=sha256:57a3e27388a6d7d32719088581e52567727fa0bf2f0d477bf565b0c4baa12a3f,api-proxy=sha256:cd400948638ffe1b87ec319abf73fa29b6ac9881b015da58bba971c4cc13a400,cli-proxy=sha256:3ed2226be062870df95bda2cc9d9f81b4a03be68b43fe036e510b61d787738a9\"},\"logging\":{\"proxyLogsDir\":\"/tmp/gh-aw/sandbox/firewall/logs\",\"auditDir\":\"/tmp/gh-aw/sandbox/firewall/audit\"}}" > "${RUNNER_TEMP}/gh-aw/awf-config.json" | ||
| cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json | ||
| export GH_AW_MODELS_JSON_PATH="/tmp/gh-aw/models.json" | ||
| GH_AW_DOCKER_HOST="" | ||
| if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then | ||
| GH_AW_DOCKER_HOST="${DOCKER_HOST}" | ||
| fi | ||
| if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then | ||
| GH_AW_CHROOT_BINARIES_SOURCE_PATH="${RUNNER_TEMP}/gh-aw" GH_AW_CHROOT_IDENTITY_HOME="${RUNNER_TEMP}/gh-aw/home" node "${RUNNER_TEMP}/gh-aw/actions/patch_awf_chroot_config.cjs" | ||
| fi | ||
| GH_AW_TOOL_CACHE_MOUNT="" | ||
| GH_AW_TOOL_CACHE="${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}" | ||
| if [ -d "$GH_AW_TOOL_CACHE" ]; then | ||
| if [[ "$GH_AW_TOOL_CACHE" != /opt/* ]]; then | ||
| GH_AW_TOOL_CACHE_MOUNT="$GH_AW_TOOL_CACHE:$GH_AW_TOOL_CACHE:ro" | ||
| fi | ||
| fi | ||
| # shellcheck disable=SC1003,SC2016,SC2086 | ||
| mkdir -p "/tmp/gh-aw" | ||
| evidence_tmp="/tmp/gh-aw/agent_execution.json.tmp" | ||
| printf '{"version":1,"component":"agent","run_id":%s,"run_attempt":%s,"state":"started"}\n' "$GITHUB_RUN_ID" "$GITHUB_RUN_ATTEMPT" > "$evidence_tmp" | ||
| mv "$evidence_tmp" "/tmp/gh-aw/agent_execution.json" | ||
| GH_AW_AWF_ENGINE_NAME=copilot \ | ||
| GH_AW_AWF_HARNESS_MARKER='[copilot-harness]' \ | ||
| GH_AW_AWF_LOG_FILE=/tmp/gh-aw/agent-stdio.log \ | ||
| GH_AW_AWF_ATTEMPT_LOG_NAME=copilot \ | ||
| bash "${RUNNER_TEMP}/gh-aw/actions/run_awf_with_startup_retries.sh" -- \ | ||
| awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_TOOL_CACHE_MOUNT:+--mount "$GH_AW_TOOL_CACHE_MOUNT"} ${GH_AW_DOCKER_HOST:+--docker-host "$GH_AW_DOCKER_HOST"} --env-all --exclude-env ACTIONS_ID_TOKEN_REQUEST_TOKEN --exclude-env ACTIONS_ID_TOKEN_REQUEST_URL --exclude-env COPILOT_GITHUB_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_AGENT_ID --mount /tmp/gh-aw:/tmp/gh-aw:rw --log-level info --skip-pull \ | ||
| -- /bin/bash -c 'set +o histexpand; export PATH="${RUNNER_TEMP}/gh-aw/mcp-cli/bin:$PATH" && : "${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}"; GH_AW_TOOL_CACHE="$RUNNER_TOOL_CACHE"; GH_AW_TOOL_BINS="$(find "$GH_AW_TOOL_CACHE" -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')"; GH_AW_TOOL_BINS="${GH_AW_TOOL_BINS%:}"; export PATH="$PATH${GH_AW_TOOL_BINS:+:}$GH_AW_TOOL_BINS"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true; [ -n "$ERLANG_HOME" ] && export PATH="$ERLANG_HOME/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; GH_AW_NPM_GLOBAL_ROOT="$(npm root -g 2>/dev/null || true)"; if [ -n "$GH_AW_NPM_GLOBAL_ROOT" ]; then export NODE_PATH="${GH_AW_NPM_GLOBAL_ROOT}${NODE_PATH:+:${NODE_PATH}}"; fi; "$GH_AW_NODE_EXEC" "${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs" "${RUNNER_TEMP}/gh-aw/bin/copilot" --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --allow-all-paths --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' | ||
| env: | ||
| AWF_REFLECT_ENABLED: 1 | ||
| COPILOT_AGENT_RUNNER_TYPE: STANDALONE | ||
| COPILOT_DUMMY_BYOK: dummy-byok-key-for-offline-mode | ||
| COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} | ||
| COPILOT_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || vars.GH_AW_DEFAULT_MODEL_COPILOT || 'auto' }} | ||
| GH_AW_LLM_PROVIDER: github | ||
| GH_AW_MAX_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_AI_CREDITS || '1000' }} | ||
| GH_AW_MAX_TURNS: ${{ vars.GH_AW_DEFAULT_MAX_TURNS || '' }} | ||
| GH_AW_PHASE: agent | ||
| GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt | ||
| GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} | ||
| GH_AW_TIMEOUT_MINUTES: 30 | ||
| GH_AW_VERSION: v0.89.15 | ||
| GITHUB_API_URL: ${{ github.api_url }} | ||
| GITHUB_AW: true | ||
| GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows | ||
| GITHUB_HEAD_REF: ${{ github.head_ref }} | ||
| GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | ||
| GITHUB_REF_NAME: ${{ github.ref_name }} | ||
| GITHUB_SERVER_URL: ${{ github.server_url }} | ||
| GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md | ||
| GITHUB_WORKSPACE: ${{ github.workspace }} | ||
| GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com | ||
| GIT_AUTHOR_NAME: github-actions[bot] | ||
| GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com | ||
| GIT_COMMITTER_NAME: github-actions[bot] | ||
| RUNNER_TEMP: ${{ runner.temp }} | ||
| TRACEPARENT: ${{ env.GITHUB_AW_OTEL_TRACE_ID != '' && env.GITHUB_AW_OTEL_PARENT_SPAN_ID != '' && format('00-{0}-{1}-01', env.GITHUB_AW_OTEL_TRACE_ID, env.GITHUB_AW_OTEL_PARENT_SPAN_ID) || '' }} | ||
| - name: Detect agent errors | ||
| if: always() | ||
| id: detect-agent-errors | ||
| continue-on-error: true | ||
| env: | ||
| GH_AW_AGENTIC_EXECUTION_OUTCOME: ${{ steps.agentic_execution.outcome }} | ||
| GH_AW_ENGINE_STEP_TIMEOUT_MINUTES: 30 | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| with: | ||
| script: | | ||
| const path = require('path'); | ||
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | ||
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require(path.join(actionsDir, 'detect_agent_errors.cjs')); | ||
| await main(); | ||
| - name: Configure Git credentials | ||
| env: | ||
| GITHUB_REPOSITORY: ${{ github.repository }} | ||
| GITHUB_SERVER_URL: ${{ github.server_url }} | ||
| GITHUB_TOKEN: ${{ github.token }} | ||
| run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_git_credentials.sh" | ||
| - name: Copy Copilot session state files to logs | ||
| if: always() | ||
| continue-on-error: true | ||
| run: bash "${RUNNER_TEMP}/gh-aw/actions/copy_copilot_session_state.sh" | ||
| - name: Stop MCP Gateway | ||
| if: always() | ||
| continue-on-error: true | ||
| env: | ||
| MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }} | ||
| MCP_GATEWAY_AGENT_ID: ${{ steps.start-mcp-gateway.outputs.gateway-agent-id }} | ||
| GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }} | ||
| run: | | ||
| bash "${RUNNER_TEMP}/gh-aw/actions/stop_mcp_gateway.sh" "$GATEWAY_PID" | ||
| - name: Redact secrets in logs | ||
| if: always() | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| with: | ||
| script: | | ||
| const path = require('path'); | ||
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | ||
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require(path.join(actionsDir, '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 "${RUNNER_TEMP}/gh-aw/actions/append_agent_step_summary.sh" | ||
| - name: Copy Safe Outputs | ||
| if: always() | ||
| env: | ||
| GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} | ||
| run: | | ||
| mkdir -p /tmp/gh-aw | ||
| cp "$GH_AW_SAFE_OUTPUTS" /tmp/gh-aw/safeoutputs.jsonl 2>/dev/null || true | ||
| - name: Ingest agent output | ||
| id: collect_output | ||
| if: always() | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| env: | ||
| GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} | ||
| GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.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,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.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,patch-diff.githubusercontent.com,patchdiff.githubusercontent.com,ppa.launchpad.net,raw.githubusercontent.com,s.symcb.com,s.symcd.com,security.ubuntu.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com" | ||
| GITHUB_SERVER_URL: ${{ github.server_url }} | ||
| GITHUB_API_URL: ${{ github.api_url }} | ||
| with: | ||
| script: | | ||
| const path = require('path'); | ||
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | ||
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require(path.join(actionsDir, 'collect_ndjson_output.cjs')); | ||
| await main(); | ||
| - name: Parse agent logs for step summary | ||
| if: always() | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| env: | ||
| GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/ | ||
| GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} | ||
| with: | ||
| script: | | ||
| const path = require('path'); | ||
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | ||
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require(path.join(actionsDir, 'parse_copilot_log.cjs')); | ||
| await main(); | ||
| - name: Parse MCP Gateway logs for step summary | ||
| if: always() | ||
| id: parse-mcp-gateway | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| with: | ||
| script: | | ||
| const path = require('path'); | ||
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | ||
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require(path.join(actionsDir, 'parse_mcp_gateway_log.cjs')); | ||
| await main(); | ||
| - name: Print firewall logs | ||
| if: always() | ||
| continue-on-error: true | ||
| env: | ||
| AWF_LOGS_DIR: /tmp/gh-aw/sandbox/firewall/logs | ||
| run: bash "${RUNNER_TEMP}/gh-aw/actions/print_firewall_logs.sh" --rootless | ||
| - name: Parse token usage for step summary | ||
| if: always() | ||
| id: parse-token-usage | ||
| continue-on-error: true | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| with: | ||
| script: | | ||
| const path = require('path'); | ||
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | ||
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require(path.join(actionsDir, 'parse_token_usage.cjs')); | ||
| await main(); | ||
| - name: Print AWF reflect summary | ||
| if: always() | ||
| continue-on-error: true | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| with: | ||
| script: | | ||
| const path = require('path'); | ||
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | ||
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require(path.join(actionsDir, 'awf_reflect_summary.cjs')); | ||
| await main(); | ||
| - name: Generate observability summary | ||
| if: always() | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| with: | ||
| script: | | ||
| const path = require('path'); | ||
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | ||
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require(path.join(actionsDir, 'generate_observability_summary.cjs')); | ||
| await main(core); | ||
| - name: Write agent output placeholder if missing | ||
| if: always() | ||
| run: | | ||
| if [ ! -f /tmp/gh-aw/agent_output.json ]; then | ||
| echo '{"items":[]}' > /tmp/gh-aw/agent_output.json | ||
| fi | ||
| # Small dedicated copy of the agent output so safe-output processing | ||
| # survives a failed or timed-out upload of the larger agent artifact | ||
| - name: Upload agent output fallback artifact | ||
| if: always() | ||
| continue-on-error: true | ||
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | ||
| with: | ||
| name: agent-output-fallback | ||
| path: | | ||
| /tmp/gh-aw/agent_output.json | ||
| /tmp/gh-aw/safeoutputs.jsonl | ||
| if-no-files-found: ignore | ||
| - name: Upload agent artifacts | ||
| if: always() | ||
| continue-on-error: true | ||
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | ||
| with: | ||
| name: agent | ||
| path: | | ||
| /tmp/gh-aw/aw-prompts/prompt.txt | ||
| /tmp/gh-aw/agent_execution.json | ||
| /tmp/gh-aw/sandbox/agent/logs/ | ||
| /tmp/gh-aw/redacted-urls.log | ||
| /tmp/gh-aw/mcp-logs/ | ||
| /tmp/gh-aw/proxy-logs/ | ||
| !/tmp/gh-aw/proxy-logs/proxy-tls/ | ||
| /tmp/gh-aw/agent_usage.json | ||
| /tmp/gh-aw/agent_usage.jsonl | ||
| /tmp/gh-aw/agent-stdio.log | ||
| /tmp/gh-aw/pre-agent-audit.txt | ||
| /tmp/gh-aw/github_rate_limits.jsonl | ||
| /tmp/gh-aw/otel.jsonl | ||
| /tmp/gh-aw/otlp-export-errors.jsonl | ||
| /tmp/gh-aw/safeoutputs.jsonl | ||
| /tmp/gh-aw/agent_output.json | ||
| /tmp/gh-aw/aw-*.patch | ||
| /tmp/gh-aw/aw-*.bundle | ||
| /tmp/gh-aw/awf-config.json | ||
| /tmp/gh-aw/sandbox/firewall/logs/ | ||
| /tmp/gh-aw/sandbox/firewall/audit/ | ||
| /tmp/gh-aw/sandbox/firewall/awf-reflect.json | ||
| if-no-files-found: ignore | ||
| apm: | ||
| needs: | ||
| - activation | ||
| - apm-prep | ||
| runs-on: ubuntu-slim | ||
| strategy: | ||
| fail-fast: false | ||
| matrix: ${{ fromJSON(needs.apm-prep.outputs.matrix) }} | ||
| permissions: | ||
| contents: read | ||
| env: | ||
| AW_APM_APPS: ${{ github.aw.import-inputs.apps }} | ||
| AW_APM_LEGACY_APP_ID: ${{ github.aw.import-inputs.app-id }} | ||
| AW_APM_LEGACY_PRIVATE_KEY: ${{ github.aw.import-inputs.private-key }} | ||
| steps: | ||
| - name: Configure GH_HOST for enterprise compatibility | ||
| id: ghes-host-config | ||
| shell: bash | ||
| run: | # zizmor: ignore[github-env] - GITHUB_SERVER_URL is set by GitHub Actions, not user input. | ||
| # Derive GH_HOST from GITHUB_SERVER_URL so the gh CLI targets the correct | ||
| # GitHub instance (GHES/GHEC). On github.com this is a harmless no-op. | ||
| GH_HOST="${GITHUB_SERVER_URL#https://}" | ||
| GH_HOST="${GH_HOST#http://}" | ||
| echo "GH_HOST=${GH_HOST}" >> "$GITHUB_ENV" | ||
| - name: Resolve credentials for this matrix row | ||
| if: ${{ matrix.group.has-app == 'true' }} | ||
| run: | | ||
| # SECURITY: never `set -x` or `echo "$pk"` in this step. ::add-mask:: | ||
| # registers the PEM as a single multi-line substring; the masker will | ||
| # not match individual PEM lines printed in isolation, so any future | ||
| # debug echo of $pk line-by-line would leak the key body in clear text. | ||
| set -euo pipefail | ||
| case "$ROW_KIND" in | ||
| legacy) | ||
| app_id="${AW_APM_LEGACY_APP_ID:-}" | ||
| pk="${AW_APM_LEGACY_PRIVATE_KEY:-}" | ||
| ;; | ||
| apps) | ||
| apps_json="${AW_APM_APPS:-[]}" | ||
| app_id=$(printf '%s' "$apps_json" | jq -r --argjson i "$ROW_INDEX" '.[$i]["app-id"] // ""') | ||
| pk=$(printf '%s' "$apps_json" | jq -r --argjson i "$ROW_INDEX" '.[$i]["private-key"] // ""') | ||
| ;; | ||
| *) | ||
| echo "::error::unexpected apm matrix kind '$ROW_KIND' for row with has-app=true" | ||
| exit 1 | ||
| ;; | ||
| esac | ||
| if [ -z "$app_id" ] || [ -z "$pk" ]; then | ||
| echo "::error::missing app-id or private-key for apm row kind=$ROW_KIND index=$ROW_INDEX" | ||
| exit 1 | ||
| fi | ||
| # Normalise trailing newline. Bash $(jq ...) strips ALL trailing | ||
| # newlines from PEMs read out of the apps[] JSON, while a direct | ||
| # env-var assignment preserves them. Stripping any tail and adding | ||
| # exactly one makes the legacy and apps paths produce byte-identical | ||
| # ROW_PRIVATE_KEY values so downstream tolerance is irrelevant. | ||
| pk="${pk%$'\n'}" | ||
| # Defence in depth: the PK is already masked because it came from | ||
| # a secrets-context reference at compile time (gh-aw substitutes | ||
| # the configured private-key secret into AW_APM_*), but | ||
| # registering it again here makes the contract explicit and | ||
| # survives any future gh-aw template churn that might lose the | ||
| # secret tag. NOTE: do not write GitHub Actions expression syntax | ||
| # (dollar-doublecurly ... doublecurly) inside this comment. | ||
| # gh-aw v0.76+ harvests such tokens out of bash run-block bodies | ||
| # (even inside `#` comments) and hoists them into the step env, | ||
| # which fails workflow load when the inner expression resolves | ||
| # to a sequence (e.g. wildcard secrets-context references). | ||
| echo "::add-mask::$pk" | ||
| # Use a random heredoc delimiter to eliminate any chance of a PEM | ||
| # line collision terminating the value early. The official docs | ||
| # explicitly warn against fixed delimiters for arbitrary multi-line | ||
| # values: https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#multiline-strings | ||
| delim="APMPK_$(openssl rand -hex 16)" | ||
| { | ||
| echo "ROW_APP_ID=$app_id" | ||
| printf 'ROW_PRIVATE_KEY<<%s\n' "$delim" | ||
| printf '%s\n' "$pk" | ||
| printf '%s\n' "$delim" | ||
| } >> "$GITHUB_ENV" | ||
| env: | ||
| ROW_INDEX: ${{ matrix.group.index }} | ||
| ROW_KIND: ${{ matrix.group.kind }} | ||
| - name: Mint installation token | ||
| id: token | ||
| if: ${{ matrix.group.has-app == 'true' }} | ||
| uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 | ||
| with: | ||
| app-id: ${{ env.ROW_APP_ID }} | ||
| owner: ${{ matrix.group.owner != '' && matrix.group.owner || github.repository_owner }} | ||
| private-key: ${{ env.ROW_PRIVATE_KEY }} | ||
| repositories: ${{ matrix.group.repositories }} | ||
| - name: Select APM package token | ||
| id: package-token | ||
| run: | | ||
| set -euo pipefail | ||
| case "$ROW_TOKEN_SOURCE" in | ||
| github-token) | ||
| selected=${BUILTIN_TOKEN:-} | ||
| tier=github-token | ||
| ;; | ||
| app) | ||
| selected=${APP_TOKEN:-} | ||
| tier=app | ||
| ;; | ||
| cascade) | ||
| selected=${CASCADE_TOKEN:-} | ||
| if [ "${HAS_PLUGINS_TOKEN:-false}" = "true" ]; then | ||
| tier=cascade:GH_AW_PLUGINS_TOKEN | ||
| elif [ "${HAS_GH_AW_TOKEN:-false}" = "true" ]; then | ||
| tier=cascade:GH_AW_GITHUB_TOKEN | ||
| else | ||
| tier=cascade:GITHUB_TOKEN | ||
| fi | ||
| ;; | ||
| *) | ||
| echo "::error::unexpected APM token source '$ROW_TOKEN_SOURCE'" | ||
| exit 1 | ||
| ;; | ||
| esac | ||
| if [ -z "$(printf '%s' "$selected" | tr -d '[:space:]')" ]; then | ||
| case "$ROW_TOKEN_SOURCE" in | ||
| cascade) | ||
| echo "::error::selected APM token source 'cascade' is unavailable; configure GH_AW_PLUGINS_TOKEN or GH_AW_GITHUB_TOKEN, or ensure the built-in GITHUB_TOKEN is enabled" | ||
| ;; | ||
| github-token) | ||
| echo "::error::selected APM token source 'github-token' is unavailable; ensure GitHub Actions can issue the built-in GITHUB_TOKEN with contents: read" | ||
| ;; | ||
| app) | ||
| echo "::error::selected APM token source 'app' is unavailable; verify the App ID, private key, installation owner, and repository scope" | ||
| ;; | ||
| esac | ||
| exit 1 | ||
| fi | ||
| case "$selected" in | ||
| *$'\n'*|*$'\r'*) | ||
| echo "::error::selected APM token must be a single line" | ||
| exit 1 | ||
| ;; | ||
| esac | ||
| echo "::notice::APM package token source: $tier" | ||
| echo "::add-mask::$selected" | ||
| printf 'token=%s\n' "$selected" >> "$GITHUB_OUTPUT" | ||
| env: | ||
| APP_TOKEN: ${{ matrix.group.token-source == 'app' && steps.token.outputs.token || '' }} | ||
| BUILTIN_TOKEN: ${{ matrix.group.token-source == 'github-token' && github.token || '' }} | ||
| CASCADE_TOKEN: ${{ matrix.group.token-source == 'cascade' && secrets.GH_AW_PLUGINS_TOKEN || matrix.group.token-source == 'cascade' && secrets.GH_AW_GITHUB_TOKEN || matrix.group.token-source == 'cascade' && secrets.GITHUB_TOKEN || '' }} | ||
| HAS_GH_AW_TOKEN: ${{ matrix.group.token-source == 'cascade' && secrets.GH_AW_GITHUB_TOKEN != '' }} | ||
| HAS_PLUGINS_TOKEN: ${{ matrix.group.token-source == 'cascade' && secrets.GH_AW_PLUGINS_TOKEN != '' }} | ||
| ROW_TOKEN_SOURCE: ${{ matrix.group.token-source }} | ||
| - name: Render package list | ||
| id: list | ||
| run: | | ||
| DEPS=$(echo "$AW_PKG" | jq -r '.[] | "- " + .') | ||
| { | ||
| echo "deps<<APMDEPS" | ||
| printf '%s\n' "$DEPS" | ||
| echo "APMDEPS" | ||
| } >> "$GITHUB_OUTPUT" | ||
| env: | ||
| AW_PKG: ${{ toJSON(matrix.group.packages) }} | ||
| - name: Pack APM packages | ||
| id: pack | ||
| uses: microsoft/apm-action@d723bb64ed70c135bbaf87d126b721dd2dae0439 # v1.10.0 | ||
| env: | ||
| GH_TOKEN: ${{ steps.package-token.outputs.token }} | ||
| GITHUB_APM_PAT: ${{ steps.package-token.outputs.token }} | ||
| GITHUB_TOKEN: ${{ steps.package-token.outputs.token }} | ||
| with: | ||
| apm-version: 0.28.0 | ||
| archive: "true" | ||
| dependencies: ${{ steps.list.outputs.deps }} | ||
| isolated: "true" | ||
| pack: "true" | ||
| target: copilot | ||
| working-directory: /tmp/gh-aw/apm-workspace | ||
| - name: Upload APM bundle artifact | ||
| if: success() | ||
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | ||
| with: | ||
| name: ${{ needs.activation.outputs.artifact_prefix }}apm-${{ matrix.group.id }} | ||
| path: ${{ steps.pack.outputs.bundle-path }} | ||
| retention-days: "1" | ||
| apm-prep: | ||
| needs: activation | ||
| runs-on: ubuntu-slim | ||
| permissions: {} | ||
| outputs: | ||
| matrix: ${{ steps.compute.outputs.matrix }} | ||
| steps: | ||
| - name: Configure GH_HOST for enterprise compatibility | ||
| id: ghes-host-config | ||
| shell: bash | ||
| run: | # zizmor: ignore[github-env] - GITHUB_SERVER_URL is set by GitHub Actions, not user input. | ||
| # Derive GH_HOST from GITHUB_SERVER_URL so the gh CLI targets the correct | ||
| # GitHub instance (GHES/GHEC). On github.com this is a harmless no-op. | ||
| GH_HOST="${GITHUB_SERVER_URL#https://}" | ||
| GH_HOST="${GH_HOST#http://}" | ||
| echo "GH_HOST=${GH_HOST}" >> "$GITHUB_ENV" | ||
| - name: Validate APM target | ||
| run: | | ||
| set -euo pipefail | ||
| raw_target=${AW_APM_TARGET:-} | ||
| if [ -z "$(printf '%s' "$raw_target" | tr -d '[:space:]')" ]; then | ||
| echo "::error::shared/apm.md requires a non-empty target under the import's with: block (for example, target: copilot); match the workflow engine" | ||
| exit 1 | ||
| fi | ||
| IFS=',' read -ra requested_targets <<< "$raw_target" | ||
| for requested in "${requested_targets[@]}"; do | ||
| normalized=$(printf '%s' "$requested" | tr -d '[:space:]' | tr 'A-Z' 'a-z') | ||
| if [ "$normalized" = "all" ]; then | ||
| echo "::error::target 'all' degrades to auto-detection in the isolated apm.yml; set a concrete target matching the workflow engine" | ||
| exit 1 | ||
| fi | ||
| done | ||
| env: | ||
| AW_APM_TARGET: copilot | ||
| - name: Validate APM token source | ||
| run: | | ||
| set -euo pipefail | ||
| token_source=${AW_APM_TOKEN_SOURCE:-cascade} | ||
| case "$token_source" in | ||
| cascade|github-token) ;; | ||
| *) | ||
| echo "::error::invalid token-source '$token_source'; expected cascade or github-token" | ||
| exit 1 | ||
| ;; | ||
| esac | ||
| env: | ||
| AW_APM_TOKEN_SOURCE: cascade | ||
| - name: Compute APM credential-group matrix | ||
| id: compute | ||
| run: | | ||
| set -euo pipefail | ||
| packages_json=${AW_APM_PACKAGES:-null} | ||
| apps_json=${AW_APM_APPS:-null} | ||
| legacy_id=${AW_APM_LEGACY_APP_ID:-} | ||
| token_source=${AW_APM_TOKEN_SOURCE:-cascade} | ||
| # gh-aw substitutes `["microsoft/apm#main"]` at | ||
| # compile time using Go's default slice formatter, which emits | ||
| # `[a b c]` (space-separated, no quotes) instead of valid JSON. | ||
| # That breaks `jq --argjson` below. Repair string-array inputs | ||
| # in place; leave already-valid JSON untouched. apps[] (objects) | ||
| # is not repairable this way -- consumers must use the legacy | ||
| # single-app inputs until upstream gh-aw exposes a JSON-encoding | ||
| # helper for import-inputs. | ||
| repair_string_array() { | ||
| local raw="$1" | ||
| if [ -z "$raw" ] || [ "$raw" = "null" ]; then | ||
| echo "$raw"; return | ||
| fi | ||
| if printf '%s' "$raw" | jq -e 'type=="array"' >/dev/null 2>&1; then | ||
| echo "$raw"; return | ||
| fi | ||
| python3 -c 'import json, re, sys; s=sys.argv[1].strip(); s=s[1:-1] if s.startswith("[") and s.endswith("]") else s; print(json.dumps([t for t in re.split(r"[\s,]+", s) if t]))' "$raw" | ||
| } | ||
| packages_json=$(repair_string_array "$packages_json") | ||
| groups=$(jq -nc \ | ||
| --argjson packages "$packages_json" \ | ||
| --argjson apps "$apps_json" \ | ||
| --arg legacy_id "$legacy_id" \ | ||
| --arg legacy_owner "${AW_APM_LEGACY_OWNER:-}" \ | ||
| --arg legacy_repos "${AW_APM_LEGACY_REPOS:-}" \ | ||
| --arg token_source "$token_source" \ | ||
| 'def slug(s): s | gsub("[^a-zA-Z0-9-]"; "-") | ascii_downcase | .[0:32]; | ||
| def with_id(g): | ||
| g + (if (g.id // "") == "" then {id: ("auto-" + slug(g.owner // "default"))} else {} end); | ||
| [ | ||
| (if (($packages // []) | length) > 0 and $legacy_id == "" | ||
| then [{id:"default",kind:"default",index:0,owner:"",repositories:"",packages:$packages,("has-app"):"false",("token-source"):$token_source}] | ||
| else [] end), | ||
| (if $legacy_id != "" | ||
| then [with_id({id:"legacy",kind:"legacy",index:0,owner:$legacy_owner,repositories:$legacy_repos,packages:($packages // []),("has-app"):"true",("token-source"):"app"})] | ||
| else [] end), | ||
| (($apps // []) | to_entries | map( | ||
| with_id({ | ||
| id: (.value.id // ""), | ||
| kind: "apps", | ||
| index: .key, | ||
| owner: (.value.owner // ""), | ||
| repositories: (.value.repositories // ""), | ||
| packages: (.value.packages // []), | ||
| ("has-app"): "true", | ||
| ("token-source"): "app" | ||
| }))) | ||
| ] | add // []') | ||
| count=$(echo "$groups" | jq 'length') | ||
| if [ "$count" = "0" ]; then | ||
| echo "::error::shared/apm.md import provided no packages. Add packages: <list>, single-app inputs (app-id + private-key), or apps: <list> in the with: block." | ||
| exit 1 | ||
| fi | ||
| dups=$(echo "$groups" | jq -r '[.[].id] | group_by(.) | map(select(length > 1) | first) | join(", ")') | ||
| if [ -n "$dups" ]; then | ||
| echo "::error::duplicate apm group ids after auto-derivation: $dups. Set apps[].id explicitly when two entries share the same owner." | ||
| exit 1 | ||
| fi | ||
| while IFS= read -r id; do | ||
| if ! echo "$id" | grep -Eq '^[a-z0-9-]{1,32}$'; then | ||
| echo "::error::invalid apm group id: '$id' (lowercase alphanumeric and dashes, 1-32 chars). Set apps[].id explicitly." | ||
| exit 1 | ||
| fi | ||
| done < <(echo "$groups" | jq -r '.[].id') | ||
| # Emit only id + package-count to logs to keep notice output tight. | ||
| # The matrix itself is non-secret under the env-relay design, but a | ||
| # condensed summary remains easier to scan in the run UI. | ||
| { | ||
| echo "matrix={\"group\":$groups}" | ||
| } >> "$GITHUB_OUTPUT" | ||
| printf "::notice::APM matrix: %d credential group(s)\n" "$count" | ||
| echo "$groups" | jq -r '.[] | " - " + .id + " (" + (.packages | length | tostring) + " package(s))"' | ||
| env: | ||
| AW_APM_APPS: ${{ github.aw.import-inputs.apps }} | ||
| AW_APM_LEGACY_APP_ID: ${{ github.aw.import-inputs.app-id }} | ||
| AW_APM_LEGACY_OWNER: ${{ github.aw.import-inputs.owner }} | ||
| AW_APM_LEGACY_REPOS: ${{ github.aw.import-inputs.repositories }} | ||
| AW_APM_PACKAGES: "[\"microsoft/apm#main\"]" | ||
| AW_APM_TOKEN_SOURCE: cascade | ||
| conclusion: | ||
| needs: | ||
| - activation | ||
| - agent | ||
| - apm | ||
| - apm-prep | ||
| - detection | ||
| - safe_outputs | ||
| if: > | ||
| always() && (needs.agent.result != 'skipped' || needs.activation.outputs.lockdown_check_failed == 'true' || | ||
| needs.activation.outputs.oauth_token_check_failed == 'true' || needs.activation.outputs.stale_lock_file_failed == 'true' || | ||
| needs.activation.outputs.secret_verification_result == 'failed' || needs.activation.outputs.daily_ai_credits_exceeded == 'true' || | ||
| needs.activation.outputs.daily_ai_credits_guardrail_status == 'structural_error' || needs.activation.outputs.daily_ai_credits_guardrail_status == 'transient_error') | ||
| runs-on: ubuntu-slim | ||
| permissions: | ||
| actions: read | ||
| issues: write | ||
| pull-requests: write | ||
| concurrency: | ||
| group: "gh-aw-conclusion-pr-review-panel" | ||
| cancel-in-progress: false | ||
| queue: max | ||
| env: | ||
| GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} | ||
| outputs: | ||
| incomplete_count: ${{ steps.report_incomplete.outputs.incomplete_count }} | ||
| noop_message: ${{ steps.noop.outputs.noop_message }} | ||
| tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} | ||
| total_count: ${{ steps.missing_tool.outputs.total_count }} | ||
| steps: | ||
| - name: Setup Scripts | ||
| id: setup | ||
| uses: github/gh-aw-actions/setup@045beb2d14bda8d0c1f2e83b41527f63770d2855 # v0.89.15 | ||
| with: | ||
| destination: ${{ runner.temp }}/gh-aw/actions | ||
| job-name: ${{ github.job }} | ||
| trace-id: ${{ needs.activation.outputs.setup-trace-id }} | ||
| parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} | ||
| env: | ||
| GH_AW_SETUP_WORKFLOW_NAME: "PR Review Panel" | ||
| GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/pr-review-panel.lock.yml@${{ github.ref }} | ||
| GH_AW_INFO_VERSION: "1.0.83" | ||
| GH_AW_INFO_AWF_VERSION: "v0.28.16" | ||
| GH_AW_INFO_ENGINE_ID: "copilot" | ||
| - name: Download agent output artifact | ||
| id: download-agent-output | ||
| continue-on-error: true | ||
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | ||
| with: | ||
| pattern: "{agent,agent-output-fallback}" | ||
| merge-multiple: true | ||
| path: /tmp/gh-aw/ | ||
| - name: Setup agent output environment variable | ||
| id: setup-agent-output-env | ||
| if: steps.download-agent-output.outcome == 'success' | ||
| run: | | ||
| mkdir -p /tmp/gh-aw/ | ||
| find "/tmp/gh-aw/" -type f -print | ||
| if [ -f "/tmp/gh-aw/agent_output.json" ]; then | ||
| echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT" | ||
| fi | ||
| - name: Download detection artifact | ||
| id: download-detection-artifact | ||
| continue-on-error: true | ||
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | ||
| with: | ||
| name: detection | ||
| path: /tmp/gh-aw/threat-detection/ | ||
| - name: Download Safe Outputs Items Manifest | ||
| id: download-safe-outputs-manifest | ||
| if: always() | ||
| continue-on-error: true | ||
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | ||
| with: | ||
| pattern: safe-outputs-items | ||
| merge-multiple: true | ||
| path: /tmp/gh-aw/ | ||
| - name: Collect usage artifact files | ||
| if: always() | ||
| continue-on-error: true | ||
| run: bash "${RUNNER_TEMP}/gh-aw/actions/collect_usage_artifact_files.sh" | ||
| - name: Upload usage artifact | ||
| if: always() | ||
| continue-on-error: true | ||
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | ||
| with: | ||
| name: usage | ||
| path: | | ||
| /tmp/gh-aw/usage/aw_info.json | ||
| /tmp/gh-aw/usage/aw-info.jsonl | ||
| /tmp/gh-aw/usage/agent_usage.json | ||
| /tmp/gh-aw/usage/agent_usage.jsonl | ||
| /tmp/gh-aw/usage/detection_usage.jsonl | ||
| /tmp/gh-aw/usage/evals.jsonl | ||
| /tmp/gh-aw/usage/graders/grader_manifest.json | ||
| /tmp/gh-aw/usage/graders/grader_results.json | ||
| /tmp/gh-aw/usage/github_rate_limits.jsonl | ||
| /tmp/gh-aw/usage/agent/token_usage.jsonl | ||
| /tmp/gh-aw/usage/agent/execution.json | ||
| /tmp/gh-aw/usage/detection/token_usage.jsonl | ||
| /tmp/gh-aw/usage/detection/execution.json | ||
| /tmp/gh-aw/usage/evals/token_usage.jsonl | ||
| /tmp/gh-aw/usage/evals/execution.json | ||
| /tmp/gh-aw/usage/activity/summary.json | ||
| if-no-files-found: ignore | ||
| - name: Process no-op messages | ||
| id: noop | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| env: | ||
| GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} | ||
| GH_AW_NOOP_MAX: "1" | ||
| GH_AW_WORKFLOW_NAME: "PR Review Panel" | ||
| GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/pr-review-panel.md" | ||
| GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | ||
| GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} | ||
| GH_AW_NOOP_REPORT_AS_ISSUE: "false" | ||
| GH_AW_AIC: ${{ needs.agent.outputs.aic }} | ||
| GH_AW_THREAT_DETECTION_AIC: ${{ needs.detection.outputs.aic }} | ||
| GH_AW_AMBIENT_CONTEXT: ${{ needs.agent.outputs.ambient_context }} | ||
| GH_AW_WORKFLOW_ID: "pr-review-panel" | ||
| with: | ||
| github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | ||
| script: | | ||
| const path = require('path'); | ||
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | ||
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require(path.join(actionsDir, 'handle_noop_message.cjs')); | ||
| await main(); | ||
| - name: Log detection run | ||
| id: detection_runs | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| env: | ||
| GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} | ||
| GH_AW_WORKFLOW_NAME: "PR Review Panel" | ||
| GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/pr-review-panel.md" | ||
| GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | ||
| GH_AW_DETECTION_CONCLUSION: ${{ needs.detection.outputs.detection_conclusion }} | ||
| GH_AW_DETECTION_REASON: ${{ needs.detection.outputs.detection_reason }} | ||
| with: | ||
| github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | ||
| script: | | ||
| const path = require('path'); | ||
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | ||
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require(path.join(actionsDir, 'handle_detection_runs.cjs')); | ||
| await main(); | ||
| - name: Record missing tool | ||
| id: missing_tool | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| env: | ||
| GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} | ||
| GH_AW_MISSING_TOOL_CREATE_ISSUE: "true" | ||
| GH_AW_WORKFLOW_NAME: "PR Review Panel" | ||
| GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/pr-review-panel.md" | ||
| with: | ||
| github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | ||
| script: | | ||
| const path = require('path'); | ||
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | ||
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require(path.join(actionsDir, 'missing_tool.cjs')); | ||
| await main(); | ||
| - name: Record incomplete | ||
| id: report_incomplete | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| env: | ||
| GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} | ||
| GH_AW_REPORT_INCOMPLETE_CREATE_ISSUE: "true" | ||
| GH_AW_WORKFLOW_NAME: "PR Review Panel" | ||
| GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/pr-review-panel.md" | ||
| with: | ||
| github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | ||
| script: | | ||
| const path = require('path'); | ||
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | ||
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require(path.join(actionsDir, 'report_incomplete_handler.cjs')); | ||
| await main(); | ||
| - name: Handle agent failure | ||
| id: handle_agent_failure | ||
| if: always() | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| env: | ||
| GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} | ||
| GH_AW_WORKFLOW_NAME: "PR Review Panel" | ||
| GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/pr-review-panel.md" | ||
| GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | ||
| GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} | ||
| GH_AW_WORKFLOW_ID: "pr-review-panel" | ||
| GH_AW_ACTION_FAILURE_ISSUE_EXPIRES_HOURS: "168" | ||
| GH_AW_ENGINE_ID: "copilot" | ||
| GH_AW_SECRET_VERIFICATION_RESULT: ${{ needs.activation.outputs.secret_verification_result }} | ||
| GH_AW_ENGINE_SECRET_FAILURE_MESSAGE: "**Alternative**: If your organization has a Copilot subscription, you can avoid the need for a personal access token by adding a top-level `permissions` block to your workflow file. This enables Copilot inference through the org using the built-in GitHub Actions token.\n\n```yaml\npermissions:\n copilot-requests: write\n```\n\nSee: https://github.github.com/gh-aw/reference/engines/#github-copilot-default" | ||
| GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens || '' }} | ||
| GH_AW_AI_CREDITS_RATE_LIMIT_ERROR: ${{ needs.agent.outputs.ai_credits_rate_limit_error || 'false' }} | ||
| GH_AW_UNKNOWN_MODEL_AI_CREDITS: ${{ needs.agent.outputs.unknown_model_ai_credits || 'false' }} | ||
| GH_AW_AIC: ${{ needs.agent.outputs.aic }} | ||
| GH_AW_THREAT_DETECTION_AIC: ${{ needs.detection.outputs.aic }} | ||
| GH_AW_MAX_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_AI_CREDITS || '1000' }} | ||
| GH_AW_INFERENCE_ACCESS_ERROR: ${{ needs.agent.outputs.inference_access_error }} | ||
| GH_AW_MCP_POLICY_ERROR: ${{ needs.agent.outputs.mcp_policy_error }} | ||
| GH_AW_AGENTIC_ENGINE_TIMEOUT: ${{ needs.agent.outputs.agentic_engine_timeout }} | ||
| GH_AW_MODEL_NOT_SUPPORTED_ERROR: ${{ needs.agent.outputs.model_not_supported_error }} | ||
| GH_AW_HTTP_400_RESPONSE_ERROR: ${{ needs.agent.outputs.http_400_response_error }} | ||
| GH_AW_MAX_CACHE_MISSES_EXCEEDED: ${{ needs.agent.outputs.max_cache_misses_exceeded }} | ||
| GH_AW_MISSING_MODEL_PRICING_ERROR: ${{ needs.agent.outputs.missing_model_pricing_error }} | ||
| GH_AW_MISSING_MODEL_PRICING_MODEL_NAME: ${{ needs.agent.outputs.missing_model_pricing_model_name }} | ||
| GH_AW_SHELL_EXPANSION_GUARD_REJECTED: ${{ needs.agent.outputs.shell_expansion_guard_rejected }} | ||
| GH_AW_ENGINE_API_HOSTS: "api.enterprise.githubcopilot.com,api.githubcopilot.com,api.business.githubcopilot.com,api.individual.githubcopilot.com" | ||
| GH_AW_LOCKDOWN_CHECK_FAILED: ${{ needs.activation.outputs.lockdown_check_failed }} | ||
| GH_AW_OAUTH_TOKEN_CHECK_FAILED: ${{ needs.activation.outputs.oauth_token_check_failed }} | ||
| GH_AW_STALE_LOCK_FILE_FAILED: ${{ needs.activation.outputs.stale_lock_file_failed }} | ||
| GH_AW_DAILY_AI_CREDITS_EXCEEDED: ${{ needs.activation.outputs.daily_ai_credits_exceeded }} | ||
| GH_AW_DAILY_AI_CREDITS_GUARDRAIL_STATUS: ${{ needs.activation.outputs.daily_ai_credits_guardrail_status }} | ||
| GH_AW_DAILY_AI_CREDITS_GUARDRAIL_ERROR: ${{ needs.activation.outputs.daily_ai_credits_guardrail_error }} | ||
| GH_AW_DAILY_AI_CREDITS_TOTAL_EFFECTIVE_TOKENS: ${{ needs.activation.outputs.daily_ai_credits_total_effective_tokens }} | ||
| GH_AW_DAILY_AI_CREDITS_THRESHOLD: ${{ needs.activation.outputs.daily_ai_credits_threshold }} | ||
| GH_AW_GROUP_REPORTS: "false" | ||
| GH_AW_FAILURE_REPORT_AS_ISSUE: "true" | ||
| GH_AW_MISSING_TOOL_REPORT_AS_FAILURE: "true" | ||
| GH_AW_MISSING_DATA_REPORT_AS_FAILURE: "true" | ||
| GH_AW_TIMEOUT_MINUTES: "30" | ||
| with: | ||
| github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | ||
| script: | | ||
| const path = require('path'); | ||
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | ||
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require(path.join(actionsDir, 'handle_agent_failure.cjs')); | ||
| await main(); | ||
| - name: Report failed jobs | ||
| id: report_failed_jobs | ||
| if: always() | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| env: | ||
| GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} | ||
| GH_AW_WORKFLOW_NAME: "PR Review Panel" | ||
| GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/pr-review-panel.md" | ||
| GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | ||
| GH_AW_REPORT_FAILED_JOBS: "true" | ||
| with: | ||
| github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | ||
| script: | | ||
| const path = require('path'); | ||
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | ||
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require(path.join(actionsDir, 'report_failed_jobs.cjs')); | ||
| await main(); | ||
| detection: | ||
| needs: | ||
| - activation | ||
| - agent | ||
| if: always() && needs.agent.result != 'skipped' | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: read | ||
| timeout-minutes: 10 | ||
| env: | ||
| GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} | ||
| outputs: | ||
| aic: ${{ steps.parse_detection_token_usage.outputs.aic }} | ||
| detection_conclusion: ${{ steps.detection_conclusion.outputs.conclusion }} | ||
| detection_reason: ${{ steps.detection_conclusion.outputs.reason }} | ||
| detection_success: ${{ steps.detection_conclusion.outputs.success }} | ||
| steps: | ||
| - name: Setup Scripts | ||
| id: setup | ||
| uses: github/gh-aw-actions/setup@045beb2d14bda8d0c1f2e83b41527f63770d2855 # v0.89.15 | ||
| with: | ||
| destination: ${{ runner.temp }}/gh-aw/actions | ||
| job-name: ${{ github.job }} | ||
| trace-id: ${{ needs.activation.outputs.setup-trace-id }} | ||
| parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} | ||
| env: | ||
| GH_AW_SETUP_WORKFLOW_NAME: "PR Review Panel" | ||
| GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/pr-review-panel.lock.yml@${{ github.ref }} | ||
| GH_AW_INFO_VERSION: "1.0.83" | ||
| GH_AW_INFO_AWF_VERSION: "v0.28.16" | ||
| GH_AW_INFO_ENGINE_ID: "copilot" | ||
| - name: Download activation artifact | ||
| continue-on-error: true | ||
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | ||
| with: | ||
| name: activation | ||
| path: /tmp/gh-aw | ||
| - name: Download agent output artifact | ||
| id: download-agent-output | ||
| continue-on-error: true | ||
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | ||
| with: | ||
| pattern: "{agent,agent-output-fallback}" | ||
| merge-multiple: true | ||
| path: /tmp/gh-aw/ | ||
| - name: Setup agent output environment variable | ||
| id: setup-agent-output-env | ||
| if: steps.download-agent-output.outcome == 'success' | ||
| run: | | ||
| mkdir -p /tmp/gh-aw/ | ||
| find "/tmp/gh-aw/" -type f -print | ||
| if [ -f "/tmp/gh-aw/agent_output.json" ]; then | ||
| echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT" | ||
| fi | ||
| - name: Checkout repository for patch context | ||
| if: needs.agent.outputs.has_patch == 'true' | ||
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | ||
| with: | ||
| persist-credentials: false | ||
| # --- Threat Detection --- | ||
| - name: Initialize detection execution evidence | ||
| run: | | ||
| mkdir -p "/tmp/gh-aw/threat-detection" | ||
| evidence_tmp="/tmp/gh-aw/threat-detection/execution.json.tmp" | ||
| printf '{"version":1,"component":"detection","run_id":%s,"run_attempt":%s,"state":"not_started"}\n' "$GITHUB_RUN_ID" "$GITHUB_RUN_ATTEMPT" > "$evidence_tmp" | ||
| mv "$evidence_tmp" "/tmp/gh-aw/threat-detection/execution.json" | ||
| - name: Clear inherited Copilot session state | ||
| run: rm -rf /tmp/gh-aw/sandbox/agent/logs/copilot-session-state | ||
| - name: Clean stale firewall files from agent artifact | ||
| run: | | ||
| rm -rf /tmp/gh-aw/sandbox/firewall/logs | ||
| rm -rf /tmp/gh-aw/sandbox/firewall/audit | ||
| - name: Download container images | ||
| run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.28.16@sha256:57a3e27388a6d7d32719088581e52567727fa0bf2f0d477bf565b0c4baa12a3f ghcr.io/github/gh-aw-firewall/api-proxy:0.28.16@sha256:cd400948638ffe1b87ec319abf73fa29b6ac9881b015da58bba971c4cc13a400 ghcr.io/github/gh-aw-firewall/squid:0.28.16@sha256:452197f2e241b2cda8eb0b5674960aa8ca544a64dc242e24f8263c3df6452919 | ||
| - name: Check if detection needed | ||
| id: detection_guard | ||
| if: always() | ||
| env: | ||
| OUTPUT_TYPES: ${{ needs.agent.outputs.output_types }} | ||
| HAS_PATCH: ${{ needs.agent.outputs.has_patch }} | ||
| run: | | ||
| if [[ -n "$OUTPUT_TYPES" || "$HAS_PATCH" == "true" ]]; then | ||
| echo "run_detection=true" >> "$GITHUB_OUTPUT" | ||
| echo "Detection will run: output_types=$OUTPUT_TYPES, has_patch=$HAS_PATCH" | ||
| else | ||
| echo "run_detection=false" >> "$GITHUB_OUTPUT" | ||
| echo "Detection skipped: no agent outputs or patches to analyze" | ||
| fi | ||
| - name: Clear MCP Config for detection | ||
| if: always() && steps.detection_guard.outputs.run_detection == 'true' | ||
| run: | | ||
| rm -f "${RUNNER_TEMP}/gh-aw/mcp-config/mcp-servers.json" | ||
| rm -f "$HOME/.copilot/mcp-config.json" | ||
| rm -f "$GITHUB_WORKSPACE/.gemini/settings.json" | ||
| - name: Prepare threat detection files | ||
| if: always() && steps.detection_guard.outputs.run_detection == 'true' | ||
| run: | | ||
| bash "${RUNNER_TEMP}/gh-aw/actions/prepare_threat_detection_files.sh" | ||
| - name: Setup threat detection | ||
| if: always() && steps.detection_guard.outputs.run_detection == 'true' | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| env: | ||
| WORKFLOW_NAME: "PR Review Panel" | ||
| WORKFLOW_DESCRIPTION: "Multi-persona expert panel review of labelled PRs, posting a single synthesized advisory recommendation comment." | ||
| HAS_PATCH: ${{ needs.agent.outputs.has_patch }} | ||
| GH_AW_DETECTION_CONTINUE_ON_ERROR: "true" | ||
| GH_AW_DETECTION_SKIP_PROMPT_SUMMARY: "true" | ||
| with: | ||
| script: | | ||
| const path = require('path'); | ||
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | ||
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require(path.join(actionsDir, '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: Install AWF binary | ||
| run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.28.16 --rootless | ||
| - name: Setup Node.js | ||
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 | ||
| with: | ||
| node-version: '24' | ||
| package-manager-cache: false | ||
| - name: Install GitHub Copilot CLI | ||
| run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" | ||
| env: | ||
| GH_HOST: github.com | ||
| GH_AW_COMPILED_VERSION: v0.89.15 | ||
| - name: Install threat-detect binary | ||
| if: always() && steps.detection_guard.outputs.run_detection == 'true' | ||
| continue-on-error: true | ||
| run: | | ||
| bash "${RUNNER_TEMP}/gh-aw/actions/install_threat_detect_binary.sh" v0.5.1 | ||
| - name: Execute threat detection with AWF | ||
| id: detection_agentic_execution | ||
| if: always() && steps.detection_guard.outputs.run_detection == 'true' | ||
| continue-on-error: true | ||
| timeout-minutes: 10 | ||
| env: | ||
| AWF_REFLECT_ENABLED: 1 | ||
| COPILOT_AGENT_RUNNER_TYPE: STANDALONE | ||
| COPILOT_DUMMY_BYOK: dummy-byok-key-for-offline-mode | ||
| COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} | ||
| COPILOT_MODEL: detection | ||
| GH_AW_HARNESS_MAX_RETRIES: 0 | ||
| GH_AW_LLM_PROVIDER: github | ||
| GH_AW_MAX_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_DETECTION_MAX_AI_CREDITS || '400' }} | ||
| GH_AW_MAX_TURNS: ${{ vars.GH_AW_DEFAULT_MAX_TURNS || '' }} | ||
| GH_AW_PHASE: detection | ||
| GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt | ||
| GH_AW_TIMEOUT_MINUTES: 10 | ||
| GH_AW_VERSION: v0.89.15 | ||
| GITHUB_API_URL: ${{ github.api_url }} | ||
| GITHUB_AW: true | ||
| GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows | ||
| GITHUB_HEAD_REF: ${{ github.head_ref }} | ||
| GITHUB_REF_NAME: ${{ github.ref_name }} | ||
| GITHUB_SERVER_URL: ${{ github.server_url }} | ||
| GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md | ||
| GITHUB_WORKSPACE: ${{ github.workspace }} | ||
| GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com | ||
| GIT_AUTHOR_NAME: github-actions[bot] | ||
| GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com | ||
| GIT_COMMITTER_NAME: github-actions[bot] | ||
| RUNNER_TEMP: ${{ runner.temp }} | ||
| TRACEPARENT: ${{ env.GITHUB_AW_OTEL_TRACE_ID != '' && env.GITHUB_AW_OTEL_PARENT_SPAN_ID != '' && format('00-{0}-{1}-01', env.GITHUB_AW_OTEL_TRACE_ID, env.GITHUB_AW_OTEL_PARENT_SPAN_ID) || '' }} | ||
| WORKFLOW_NAME: "PR Review Panel" | ||
| WORKFLOW_DESCRIPTION: "Multi-persona expert panel review of labelled PRs, posting a single synthesized advisory recommendation comment." | ||
| HAS_PATCH: ${{ needs.agent.outputs.has_patch }} | ||
| GH_AW_DETECTION_CONTINUE_ON_ERROR: "true" | ||
| run: | | ||
| mkdir -p "/tmp/gh-aw/threat-detection" | ||
| evidence_tmp="/tmp/gh-aw/threat-detection/execution.json.tmp" | ||
| printf '{"version":1,"component":"detection","run_id":%s,"run_attempt":%s,"state":"started"}\n' "$GITHUB_RUN_ID" "$GITHUB_RUN_ATTEMPT" > "$evidence_tmp" | ||
| mv "$evidence_tmp" "/tmp/gh-aw/threat-detection/execution.json" | ||
| set -o pipefail | ||
| printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt | ||
| GH_AW_COPILOT_SRC="$(command -v copilot 2>/dev/null || true)" | ||
| if [ -z "$GH_AW_COPILOT_SRC" ] || [ ! -x "$GH_AW_COPILOT_SRC" ]; then | ||
| echo "GitHub Copilot CLI executable not found on PATH after installation" >&2 | ||
| exit 127 | ||
| fi | ||
| GH_AW_COPILOT_BIN="${RUNNER_TEMP}/gh-aw/bin/copilot" | ||
| mkdir -p "${RUNNER_TEMP}/gh-aw/bin" | ||
| if [ "$GH_AW_COPILOT_SRC" != "$GH_AW_COPILOT_BIN" ]; then | ||
| cp "$GH_AW_COPILOT_SRC" "$GH_AW_COPILOT_BIN" | ||
| fi | ||
| chmod 755 "$GH_AW_COPILOT_BIN" | ||
| (umask 177 && touch /tmp/gh-aw/threat-detection/detection.log) | ||
| GH_AW_MAX_AI_CREDITS="${GH_AW_MAX_AI_CREDITS:-400}" | ||
| if [[ ! "$GH_AW_MAX_AI_CREDITS" =~ ^[0-9]+$ ]]; then | ||
| GH_AW_MAX_AI_CREDITS="400" | ||
| fi | ||
| printf '%s\n' "{\"\$schema\":\"https://github.com/github/gh-aw-firewall/releases/download/v0.28.16/awf-config.schema.json\",\"apiProxy\":{\"enabled\":true,\"enableTokenSteering\":true,\"maxRuns\":500,\"maxAiCredits\":${GH_AW_MAX_AI_CREDITS},\"maxCacheMisses\":5,\"models\":{\"agent\":[\"sonnet-6x\",\"gpt-6\",\"gpt-5.4\",\"gpt-5.5\",\"gpt-5.6\",\"gpt-5.3\",\"gemini-pro\",\"any\"],\"antigravity\":[\"copilot/antigravity*\",\"google/antigravity*\",\"gemini/antigravity*\"],\"any\":[\"copilot/*\",\"anthropic/*\",\"openai/*\",\"google/*\",\"gemini/*\"],\"auto\":[\"copilot/auto\",\"large\"],\"claude\":[\"agent\"],\"codex\":[\"agent\"],\"coding\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\",\"gpt-5-codex\",\"kimi\"],\"computer-use\":[\"copilot/*computer-use*\",\"google/*computer-use*\",\"gemini/*computer-use*\",\"openai/*computer-use*\"],\"copilot\":[\"agent\"],\"deep-research\":[\"copilot/deep-research*\",\"copilot/o3-deep-research*\",\"copilot/o4-mini-deep-research*\",\"google/deep-research*\",\"gemini/deep-research*\",\"openai/o3-deep-research*\",\"openai/o4-mini-deep-research*\"],\"detection\":[\"small\"],\"evals\":[\"small\"],\"fable\":[\"copilot/*fable*\",\"anthropic/*fable*\"],\"gemini\":[\"agent\"],\"gemini-3-flash\":[\"copilot/gemini-3*flash*\",\"google/gemini-3*flash*\",\"gemini/gemini-3*flash*\"],\"gemini-3-pro\":[\"copilot/gemini-3*pro*\",\"google/gemini-3*pro*\",\"google/nano-banana*\",\"gemini/gemini-3*pro*\"],\"gemini-3.1-flash\":[\"copilot/gemini-3.1*flash*\",\"google/gemini-3.1*flash*\",\"gemini/gemini-3.1*flash*\"],\"gemini-3.1-pro\":[\"copilot/gemini-3.1*pro*\",\"google/gemini-3.1*pro*\",\"gemini/gemini-3.1*pro*\"],\"gemini-3.5-flash\":[\"copilot/gemini-3.5*flash*\",\"google/gemini-3.5*flash*\",\"gemini/gemini-3.5*flash*\"],\"gemini-3.6-flash\":[\"copilot/gemini-3.6*flash*\",\"google/gemini-3.6*flash*\",\"gemini/gemini-3.6*flash*\"],\"gemini-3.7-flash\":[\"copilot/gemini-3.7*flash*\",\"google/gemini-3.7*flash*\",\"gemini/gemini-3.7*flash*\"],\"gemini-flash\":[\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"],\"gemini-flash-lite\":[\"copilot/gemini-*flash*lite*\",\"google/gemini-*flash*lite*\",\"gemini/gemini-*flash*lite*\"],\"gemini-omni\":[\"copilot/gemini-omni*\",\"google/gemini-omni*\",\"gemini/gemini-omni*\"],\"gemini-pro\":[\"copilot/gemini-*pro*\",\"google/gemini-*pro*\",\"gemini/gemini-*pro*\"],\"gemma\":[\"copilot/gemma*\",\"google/gemma*\",\"gemini/gemma*\"],\"gpt-5\":[\"copilot/gpt-5*\",\"openai/gpt-5*\"],\"gpt-5-codex\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\"],\"gpt-5-mini\":[\"copilot/gpt-5*mini*\",\"openai/gpt-5*mini*\"],\"gpt-5-nano\":[\"copilot/gpt-5*nano*\",\"openai/gpt-5*nano*\"],\"gpt-5-pro\":[\"copilot/gpt-5*pro*\",\"openai/gpt-5*pro*\"],\"gpt-5.1\":[\"copilot/gpt-5.1*\",\"openai/gpt-5.1*\"],\"gpt-5.2\":[\"copilot/gpt-5.2*\",\"openai/gpt-5.2*\"],\"gpt-5.3\":[\"copilot/gpt-5.3*\",\"openai/gpt-5.3*\"],\"gpt-5.4\":[\"copilot/gpt-5.4*\",\"openai/gpt-5.4*\"],\"gpt-5.5\":[\"copilot/gpt-5.5*\",\"openai/gpt-5.5*\"],\"gpt-5.6\":[\"copilot/gpt-5.6*\",\"openai/gpt-5.6*\"],\"gpt-6\":[\"copilot/gpt-6*\",\"openai/gpt-6*\"],\"grok\":[\"copilot/*grok*\",\"openai/*grok*\"],\"haiku\":[\"copilot/*haiku*\",\"anthropic/*haiku*\"],\"image-generation\":[\"copilot/gpt-image*\",\"openai/gpt-image*\",\"openai/chatgpt-image*\",\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"google/imagen*\"],\"kimi\":[\"copilot/kimi*\",\"openai/kimi*\"],\"kiwi\":[\"copilot/kiwi*\",\"openai/kiwi*\"],\"large\":[\"sonnet\",\"gpt-6\",\"gpt-5-pro\",\"gpt-5\",\"gemini-pro\"],\"lyria\":[\"google/lyria*\",\"gemini/lyria*\",\"copilot/lyria*\"],\"mai-code\":[\"copilot/MAI-Code*\",\"copilot/mai-code*\",\"openai/MAI-Code*\"],\"mai-code-1-flash-picker\":[\"copilot/MAI-Code-1-Flash-picker*\",\"copilot/mai-code-1-flash-picker*\",\"openai/MAI-Code-1-Flash-picker*\"],\"mini\":[\"haiku\",\"gpt-5-mini\",\"gpt-5-nano\",\"gemini-flash-lite\"],\"nano-banana\":[\"copilot/nano-banana*\",\"google/nano-banana*\",\"gemini/nano-banana*\"],\"opus\":[\"copilot/*opus*\",\"anthropic/*opus*\"],\"opusplan\":[\"opus?effort=high\"],\"raptor-mini\":[\"copilot/raptor*\",\"openai/raptor*\"],\"reasoning\":[\"copilot/o1*\",\"copilot/o3*\",\"copilot/o4*\",\"openai/o1*\",\"openai/o3*\",\"openai/o4*\"],\"robotics\":[\"copilot/*robotics*\",\"google/*robotics*\",\"gemini/*robotics*\"],\"small\":[\"mini\"],\"small-agent\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash\"],\"sonnet\":[\"copilot/*sonnet*\",\"anthropic/*sonnet*\"],\"sonnet-6x\":[\"copilot/*sonnet-4.5*\",\"copilot/*sonnet-4.6*\",\"copilot/*sonnet-5*\",\"copilot/*sonnet-4-5-*\",\"anthropic/*sonnet-4-5-*\",\"copilot/*sonnet-4-6*\",\"anthropic/*sonnet-4-6*\",\"anthropic/*sonnet-5*\"],\"summarization\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash-lite\",\"mini\"],\"veo\":[\"google/veo*\",\"gemini/veo*\"],\"vision\":[\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"]}},\"container\":{\"imageTag\":\"0.28.16,squid=sha256:452197f2e241b2cda8eb0b5674960aa8ca544a64dc242e24f8263c3df6452919,agent=sha256:57a3e27388a6d7d32719088581e52567727fa0bf2f0d477bf565b0c4baa12a3f,api-proxy=sha256:cd400948638ffe1b87ec319abf73fa29b6ac9881b015da58bba971c4cc13a400,cli-proxy=sha256:3ed2226be062870df95bda2cc9d9f81b4a03be68b43fe036e510b61d787738a9\"},\"logging\":{\"proxyLogsDir\":\"/tmp/gh-aw/sandbox/firewall/logs\",\"auditDir\":\"/tmp/gh-aw/sandbox/firewall/audit\"}}" > "${RUNNER_TEMP}/gh-aw/awf-config.json" | ||
| cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json | ||
| export GH_AW_MODELS_JSON_PATH="/tmp/gh-aw/models.json" | ||
| GH_AW_DOCKER_HOST="" | ||
| if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then | ||
| GH_AW_DOCKER_HOST="${DOCKER_HOST}" | ||
| fi | ||
| if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then | ||
| _GH_AW_CHROOT_JSON=$(jq -c --arg src "${RUNNER_TEMP}/gh-aw" --arg user "$(id -un)" --argjson uid "$(id -u)" --argjson gid "$(id -g)" --arg home "${RUNNER_TEMP}/gh-aw/home" '.chroot={"binariesSourcePath":$src,"identity":{"user":$user,"uid":$uid,"gid":$gid,"home":$home}}' "${RUNNER_TEMP}/gh-aw/awf-config.json") || { echo "chroot config patch failed" >&2; exit 1; } | ||
| printf '%s\n' "$_GH_AW_CHROOT_JSON" > "${RUNNER_TEMP}/gh-aw/awf-config.json" | ||
| fi | ||
| GH_AW_TOOL_CACHE_MOUNT="" | ||
| GH_AW_TOOL_CACHE="${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}" | ||
| if [ -d "$GH_AW_TOOL_CACHE" ]; then | ||
| if [[ "$GH_AW_TOOL_CACHE" != /opt/* ]]; then | ||
| GH_AW_TOOL_CACHE_MOUNT="$GH_AW_TOOL_CACHE:$GH_AW_TOOL_CACHE:ro" | ||
| fi | ||
| fi | ||
| # shellcheck disable=SC1003,SC2016,SC2086 | ||
| awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_TOOL_CACHE_MOUNT:+--mount "$GH_AW_TOOL_CACHE_MOUNT"} ${GH_AW_DOCKER_HOST:+--docker-host "$GH_AW_DOCKER_HOST"} --env-all --exclude-env ACTIONS_ID_TOKEN_REQUEST_TOKEN --exclude-env ACTIONS_ID_TOKEN_REQUEST_URL --exclude-env COPILOT_GITHUB_TOKEN --mount /tmp/gh-aw:/tmp/gh-aw:rw --mount /tmp/gh-aw/threat-detection:/tmp/gh-aw/threat-detection:rw --log-level info --skip-pull \ | ||
| -- /bin/bash -c 'set +o histexpand; export PATH="${RUNNER_TEMP}/gh-aw/bin:$PATH" && : "${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}"; GH_AW_TOOL_CACHE="$RUNNER_TOOL_CACHE"; GH_AW_TOOL_BINS="$(find "$GH_AW_TOOL_CACHE" -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')"; GH_AW_TOOL_BINS="${GH_AW_TOOL_BINS%:}"; export PATH="$PATH${GH_AW_TOOL_BINS:+:}$GH_AW_TOOL_BINS"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true; [ -n "$ERLANG_HOME" ] && export PATH="$ERLANG_HOME/bin:$PATH" || true && threat-detect --engine copilot --output /tmp/gh-aw/threat-detection/detection_result.json /tmp/gh-aw/threat-detection' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log | ||
| - name: Render detection log | ||
| if: always() && steps.detection_guard.outputs.run_detection == 'true' | ||
| continue-on-error: true | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| with: | ||
| script: | | ||
| const path = require('path'); | ||
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | ||
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require(path.join(actionsDir, 'render_detection_log.cjs')); | ||
| await main(); | ||
| - name: Copy detection firewall logs | ||
| if: always() && steps.detection_guard.outputs.run_detection == 'true' | ||
| continue-on-error: true | ||
| run: | | ||
| mkdir -p /tmp/gh-aw/threat-detection/sandbox/firewall | ||
| if [ -d /tmp/gh-aw/sandbox/firewall/logs ]; then mkdir -p /tmp/gh-aw/threat-detection/sandbox/firewall/logs && cp -r /tmp/gh-aw/sandbox/firewall/logs/. /tmp/gh-aw/threat-detection/sandbox/firewall/logs/; fi | ||
| if [ -d /tmp/gh-aw/sandbox/firewall/audit ]; then mkdir -p /tmp/gh-aw/threat-detection/sandbox/firewall/audit && cp -r /tmp/gh-aw/sandbox/firewall/audit/. /tmp/gh-aw/threat-detection/sandbox/firewall/audit/; fi | ||
| - name: Parse threat detection token usage for step summary | ||
| id: parse_detection_token_usage | ||
| if: always() | ||
| continue-on-error: true | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| env: | ||
| GH_AW_TOKEN_USAGE_SUMMARY_TITLE: Threat Detection Token Usage | ||
| GH_AW_AGENT_USAGE_PATH: /tmp/gh-aw/threat-detection/detection_usage.json | ||
| GH_AW_AGENT_USAGE_JSONL_PATH: /tmp/gh-aw/threat-detection/detection_usage.jsonl | ||
| GH_AW_WRITE_EMPTY_USAGE: "true" | ||
| with: | ||
| script: | | ||
| const path = require('path'); | ||
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | ||
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require(path.join(actionsDir, 'parse_token_usage.cjs')); | ||
| await main(); | ||
| - name: Upload threat detection artifact | ||
| if: always() | ||
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | ||
| with: | ||
| name: detection | ||
| path: | | ||
| /tmp/gh-aw/threat-detection/detection_result.json | ||
| /tmp/gh-aw/threat-detection/execution.json | ||
| /tmp/gh-aw/threat-detection/detection_usage.json | ||
| /tmp/gh-aw/threat-detection/detection_usage.jsonl | ||
| /tmp/gh-aw/threat-detection/sandbox/firewall/logs/ | ||
| /tmp/gh-aw/threat-detection/sandbox/firewall/audit/ | ||
| if-no-files-found: ignore | ||
| - name: Conclude threat detection | ||
| id: detection_conclusion | ||
| if: always() | ||
| continue-on-error: true | ||
| env: | ||
| RUN_DETECTION: ${{ steps.detection_guard.outputs.run_detection }} | ||
| DETECTION_AGENTIC_EXECUTION_OUTCOME: ${{ steps.detection_agentic_execution.outcome }} | ||
| GH_AW_DETECTION_CONTINUE_ON_ERROR: "true" | ||
| run: | | ||
| bash "${RUNNER_TEMP}/gh-aw/actions/conclude_threat_detection.sh" /tmp/gh-aw/threat-detection/detection_result.json | ||
| pre_activation: | ||
| if: github.event_name == 'workflow_dispatch' || github.event.label.name == 'panel-review' | ||
| runs-on: ubuntu-slim | ||
| env: | ||
| GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} | ||
| outputs: | ||
| activated: ${{ steps.check_membership.outputs.is_team_member == 'true' }} | ||
| matched_command: '' | ||
| setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }} | ||
| setup-span-id: ${{ steps.setup.outputs.span-id }} | ||
| setup-trace-id: ${{ steps.setup.outputs.trace-id }} | ||
| steps: | ||
| - name: Setup Scripts | ||
| id: setup | ||
| uses: github/gh-aw-actions/setup@045beb2d14bda8d0c1f2e83b41527f63770d2855 # v0.89.15 | ||
| with: | ||
| destination: ${{ runner.temp }}/gh-aw/actions | ||
| job-name: ${{ github.job }} | ||
| env: | ||
| GH_AW_SETUP_WORKFLOW_NAME: "PR Review Panel" | ||
| GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/pr-review-panel.lock.yml@${{ github.ref }} | ||
| GH_AW_INFO_VERSION: "1.0.83" | ||
| GH_AW_INFO_AWF_VERSION: "v0.28.16" | ||
| GH_AW_INFO_ENGINE_ID: "copilot" | ||
| - name: Check team membership for workflow | ||
| id: check_membership | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| env: | ||
| GH_AW_REQUIRED_ROLES: "admin,maintainer,write" | ||
| with: | ||
| github-token: ${{ secrets.GITHUB_TOKEN }} | ||
| script: | | ||
| const path = require('path'); | ||
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | ||
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require(path.join(actionsDir, 'check_membership.cjs')); | ||
| await main(); | ||
| safe_outputs: | ||
| needs: | ||
| - activation | ||
| - agent | ||
| - detection | ||
| if: (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success' | ||
| runs-on: ubuntu-slim | ||
| permissions: | ||
| issues: write | ||
| pull-requests: write | ||
| timeout-minutes: 45 | ||
| env: | ||
| GH_AW_AGENT_AIC: ${{ needs.agent.outputs.aic }} | ||
| GH_AW_AIC: ${{ needs.agent.outputs.aic }} | ||
| GH_AW_AMBIENT_CONTEXT: ${{ needs.agent.outputs.ambient_context }} | ||
| GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/pr-review-panel" | ||
| GH_AW_DETECTION_CONCLUSION: ${{ needs.detection.outputs.detection_conclusion }} | ||
| GH_AW_DETECTION_REASON: ${{ needs.detection.outputs.detection_reason }} | ||
| GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens }} | ||
| GH_AW_ENGINE_ID: "copilot" | ||
| GH_AW_ENGINE_MODEL: ${{ needs.agent.outputs.model }} | ||
| GH_AW_HEAD_SHA: ${{ github.event.pull_request.head.sha }} | ||
| GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} | ||
| GH_AW_THREAT_DETECTION_AIC: ${{ needs.detection.outputs.aic }} | ||
| GH_AW_WORKFLOW_ID: "pr-review-panel" | ||
| GH_AW_WORKFLOW_NAME: "PR Review Panel" | ||
| GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/pr-review-panel.md" | ||
| outputs: | ||
| code_push_failure_count: ${{ steps.process_safe_outputs.outputs.code_push_failure_count }} | ||
| code_push_failure_errors: ${{ steps.process_safe_outputs.outputs.code_push_failure_errors }} | ||
| comment_id: ${{ steps.process_safe_outputs.outputs.comment_id }} | ||
| comment_url: ${{ steps.process_safe_outputs.outputs.comment_url }} | ||
| create_discussion_error_count: ${{ steps.process_safe_outputs.outputs.create_discussion_error_count }} | ||
| create_discussion_errors: ${{ steps.process_safe_outputs.outputs.create_discussion_errors }} | ||
| process_safe_outputs_items_applied: ${{ steps.process_safe_outputs.outputs.items_applied }} | ||
| process_safe_outputs_items_cancelled: ${{ steps.process_safe_outputs.outputs.items_cancelled }} | ||
| process_safe_outputs_items_deferred: ${{ steps.process_safe_outputs.outputs.items_deferred }} | ||
| process_safe_outputs_items_failed: ${{ steps.process_safe_outputs.outputs.items_failed }} | ||
| process_safe_outputs_items_skipped: ${{ steps.process_safe_outputs.outputs.items_skipped }} | ||
| process_safe_outputs_items_succeeded: ${{ steps.process_safe_outputs.outputs.items_succeeded }} | ||
| process_safe_outputs_items_warnings: ${{ steps.process_safe_outputs.outputs.items_warnings }} | ||
| process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} | ||
| process_safe_outputs_status: ${{ steps.process_safe_outputs.outputs.status }} | ||
| steps: | ||
| - name: Setup Scripts | ||
| id: setup | ||
| uses: github/gh-aw-actions/setup@045beb2d14bda8d0c1f2e83b41527f63770d2855 # v0.89.15 | ||
| with: | ||
| destination: ${{ runner.temp }}/gh-aw/actions | ||
| job-name: ${{ github.job }} | ||
| trace-id: ${{ needs.activation.outputs.setup-trace-id }} | ||
| parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} | ||
| env: | ||
| GH_AW_SETUP_WORKFLOW_NAME: "PR Review Panel" | ||
| GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/pr-review-panel.lock.yml@${{ github.ref }} | ||
| GH_AW_INFO_VERSION: "1.0.83" | ||
| GH_AW_INFO_AWF_VERSION: "v0.28.16" | ||
| GH_AW_INFO_ENGINE_ID: "copilot" | ||
| - name: Mask OTLP telemetry headers | ||
| run: bash "${RUNNER_TEMP}/gh-aw/actions/mask_otlp_headers.sh" | ||
| - name: Download agent output artifact | ||
| id: download-agent-output | ||
| continue-on-error: true | ||
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | ||
| with: | ||
| pattern: "{agent,agent-output-fallback}" | ||
| merge-multiple: true | ||
| path: /tmp/gh-aw/ | ||
| - name: Setup agent output environment variable | ||
| id: setup-agent-output-env | ||
| if: steps.download-agent-output.outcome == 'success' | ||
| run: | | ||
| mkdir -p /tmp/gh-aw/ | ||
| find "/tmp/gh-aw/" -type f -print | ||
| if [ -f "/tmp/gh-aw/agent_output.json" ]; then | ||
| echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT" | ||
| fi | ||
| - name: Configure GH_HOST for enterprise compatibility | ||
| id: ghes-host-config | ||
| shell: bash | ||
| run: | # zizmor: ignore[github-env] - GITHUB_SERVER_URL is set by GitHub Actions, not user input. | ||
| # Derive GH_HOST from GITHUB_SERVER_URL so the gh CLI targets the correct | ||
| # GitHub instance (GHES/GHEC). On github.com this is a harmless no-op. | ||
| GH_HOST="${GITHUB_SERVER_URL#https://}" | ||
| GH_HOST="${GH_HOST#http://}" | ||
| echo "GH_HOST=${GH_HOST}" >> "$GITHUB_ENV" | ||
| - name: Process Safe Outputs | ||
| id: process_safe_outputs | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| env: | ||
| GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} | ||
| GH_AW_COMMENT_ID: ${{ needs.activation.outputs.comment_id }} | ||
| GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.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,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.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,patch-diff.githubusercontent.com,patchdiff.githubusercontent.com,ppa.launchpad.net,raw.githubusercontent.com,s.symcb.com,s.symcd.com,security.ubuntu.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com" | ||
| GITHUB_SERVER_URL: ${{ github.server_url }} | ||
| GITHUB_API_URL: ${{ github.api_url }} | ||
| GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":2},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"remove_labels\":{\"allowed\":[\"panel-review\",\"panel-approved\",\"panel-rejected\"],\"max\":3},\"report_incomplete\":{}}" | ||
| with: | ||
| github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | ||
| script: | | ||
| const path = require('path'); | ||
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | ||
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require(path.join(actionsDir, 'process_safe_outputs.cjs')); | ||
| await main(); | ||
| - name: Upload Safe Outputs Items | ||
| if: always() | ||
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | ||
| with: | ||
| name: safe-outputs-items | ||
| path: | | ||
| /tmp/gh-aw/safe-output-items.jsonl | ||
| /tmp/gh-aw/temporary-id-map.json | ||
| /tmp/gh-aw/safe-output-errors.json | ||
| if-no-files-found: ignore | ||