Domain Validation Assist #21693
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":"70b9b6c8e7884db3079759e79952cf0c62bb61773305cb3640563f3c8758fb6d","body_hash":"7b7931c0763b153ed59d2663bad4758affe3ed1b9fe3bc9fac20262c00fdf5d5","compiler_version":"v0.88.7","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.80"}} | ||
|
Check warning on line 1 in .github/workflows/domain-validation-assist.lock.yml
|
||
| # gh-aw-manifest: {"version":1,"secrets":["GH_AW_DEFAULT_OTLP_HEADERS","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"3d3c42e5aac5ba805825da76410c181273ba90b1","version":"v7.0.1"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"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":"5e508589e03a7757a7e05b26e834292f5445bfb6","version":"v0.88.7"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.28.14","digest":"sha256:f7df036c86575527b61f3f7df91c4412349a12b2a74988d929eafa2999230c98","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.28.14@sha256:f7df036c86575527b61f3f7df91c4412349a12b2a74988d929eafa2999230c98"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.28.14","digest":"sha256:6f95e2234dd9bd6333a8ff28ccea7ecf0204acd4a09108723844dbd2bf6268c5","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.28.14@sha256:6f95e2234dd9bd6333a8ff28ccea7ecf0204acd4a09108723844dbd2bf6268c5"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.28.14","digest":"sha256:2ce8df3abf3e9b76e9c0cf5863da41f1ab3f89b20ad14b988806ab89e7bf2cd5","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.28.14@sha256:2ce8df3abf3e9b76e9c0cf5863da41f1ab3f89b20ad14b988806ab89e7bf2cd5"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.18","digest":"sha256:85b940556a8faa4e1fdbef124bfd75f2c4ebd855a10b88a1c3b6f3e97f6f1a53","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.18@sha256:85b940556a8faa4e1fdbef124bfd75f2c4ebd855a10b88a1c3b6f3e97f6f1a53"},{"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.11.0","digest":"sha256:fbec75de11c255213fa08d80fb166abe73d851fff631c51c0079872967720699","pinned_image":"ghcr.io/github/github-mcp-server:v1.11.0@sha256:fbec75de11c255213fa08d80fb166abe73d851fff631c51c0079872967720699"}],"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":["noop","post_domain_validation_comment"]}]} | ||
| # This file was automatically generated by gh-aw (v0.88.7). 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/ | ||
| # | ||
| # Experimental author assist for trusted WinGetValidator URL and domain results. Posts one bounded recommendation only for explicit URL evidence or an exact maintained approval-inventory match. | ||
| # | ||
| # Secrets used: | ||
| # - GH_AW_DEFAULT_OTLP_HEADERS | ||
| # - GH_AW_GITHUB_MCP_SERVER_TOKEN | ||
| # - GH_AW_GITHUB_TOKEN | ||
| # - GITHUB_TOKEN | ||
| # | ||
| # Custom actions used: | ||
| # - actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | ||
| # - actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | ||
| # - actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | ||
| # - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | ||
| # - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 (source v8) | ||
| # - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| # - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 (source v9) | ||
| # - actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 | ||
| # - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | ||
| # - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 (source v7) | ||
| # - github/gh-aw-actions/setup@5e508589e03a7757a7e05b26e834292f5445bfb6 # v0.88.7 | ||
| # | ||
| # Container images used: | ||
| # - ghcr.io/github/gh-aw-firewall/agent:0.28.14@sha256:f7df036c86575527b61f3f7df91c4412349a12b2a74988d929eafa2999230c98 | ||
| # - ghcr.io/github/gh-aw-firewall/api-proxy:0.28.14@sha256:6f95e2234dd9bd6333a8ff28ccea7ecf0204acd4a09108723844dbd2bf6268c5 | ||
| # - ghcr.io/github/gh-aw-firewall/squid:0.28.14@sha256:2ce8df3abf3e9b76e9c0cf5863da41f1ab3f89b20ad14b988806ab89e7bf2cd5 | ||
| # - ghcr.io/github/gh-aw-mcpg:v0.4.18@sha256:85b940556a8faa4e1fdbef124bfd75f2c4ebd855a10b88a1c3b6f3e97f6f1a53 | ||
| # - ghcr.io/github/gh-aw-node@sha256:0daa8971fa4732b647150cb6524a6b0804b68d5d24f6f58b5dd1af23bd63fb23 | ||
| # - ghcr.io/github/github-mcp-server:v1.11.0@sha256:fbec75de11c255213fa08d80fb166abe73d851fff631c51c0079872967720699 | ||
| name: "Domain Validation Assist" | ||
| on: | ||
| # bots: # Bots processed as bot check in pre-activation job | ||
| # - wingetvalidator-prod[bot] # Bots processed as bot check in pre-activation job | ||
| pull_request_target: | ||
| types: | ||
| - labeled | ||
| # 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 | ||
| permissions: {} | ||
| concurrency: | ||
| cancel-in-progress: false | ||
| group: gh-aw-${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }} | ||
| queue: max | ||
| run-name: "Domain Validation Assist" | ||
| env: | ||
| OTEL_EXPORTER_OTLP_ENDPOINT: ${{ vars.GH_AW_DEFAULT_OTLP_ENDPOINT }} | ||
| OTEL_SERVICE_NAME: gh-aw.domain-validation-assist | ||
| OTEL_RESOURCE_ATTRIBUTES: 'gh-aw.workflow.name=Domain%20Validation%20Assist,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":"${{ 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 == 'pull_request_target' && github.event.action == 'labeled' && github.actor == 'wingetvalidator-prod[bot]' && github.event.pull_request.user.login != 'wingetbot' && contains( | ||
| fromJSON('["Error-Installer-Availability","Validate-Domain-Installer","Validation-404-Error","Validation-Agreement-Domain","Validation-Domain","Validation-Domains-Mismatch","Validation-Forbidden-URL-Error","Validation-Indirect-URL","Validation-Open-Url-Failed","Validation-Unapproved-URL"]'), | ||
| github.event.label.name | ||
| )) | ||
| 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_status: ${{ steps.daily-effective-workflow-guardrail.outputs.daily_ai_credits_guardrail_status || '' }} | ||
| daily_ai_credits_threshold: ${{ steps.daily-effective-workflow-guardrail.outputs.daily_ai_credits_threshold || '' }} | ||
| daily_ai_credits_total_effective_tokens: ${{ steps.daily-effective-workflow-guardrail.outputs.daily_ai_credits_total_effective_tokens || '' }} | ||
| engine_id: ${{ steps.generate_aw_info.outputs.engine_id }} | ||
| lockdown_check_failed: ${{ steps.generate_aw_info.outputs.lockdown_check_failed == 'true' }} | ||
| model: ${{ steps.generate_aw_info.outputs.model }} | ||
| oauth_token_check_failed: ${{ steps.check-oauth-tokens.outputs.oauth_token_check_failed == 'true' }} | ||
| setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }} | ||
| setup-span-id: ${{ steps.setup.outputs.span-id }} | ||
| setup-trace-id: ${{ steps.setup.outputs.trace-id }} | ||
| stale_lock_file_failed: ${{ steps.check-lock-file.outputs.stale_lock_file_failed == 'true' }} | ||
| text: ${{ steps.sanitized.outputs.text }} | ||
| title: ${{ steps.sanitized.outputs.title }} | ||
| steps: | ||
| - name: Setup Scripts | ||
| id: setup | ||
| uses: github/gh-aw-actions/setup@5e508589e03a7757a7e05b26e834292f5445bfb6 # v0.88.7 | ||
| 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: "Domain Validation Assist" | ||
| GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/domain-validation-assist.lock.yml@${{ github.ref }} | ||
| GH_AW_INFO_VERSION: "1.0.80" | ||
| GH_AW_INFO_AWF_VERSION: "v0.28.14" | ||
| 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.80" | ||
| GH_AW_INFO_AGENT_VERSION: "1.0.80" | ||
| GH_AW_INFO_CLI_VERSION: "v0.88.7" | ||
| GH_AW_INFO_WORKFLOW_NAME: "Domain Validation Assist" | ||
| GH_AW_INFO_EXPERIMENTAL: "false" | ||
| GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" | ||
| GH_AW_INFO_STAGED: "false" | ||
| GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]' | ||
| GH_AW_INFO_FIREWALL_ENABLED: "true" | ||
| GH_AW_INFO_AWF_VERSION: "v0.28.14" | ||
| GH_AW_INFO_AWMG_VERSION: "" | ||
| GH_AW_INFO_FIREWALL_TYPE: "squid" | ||
| GH_AW_INFO_AGENT_RUNTIME: "" | ||
| GH_AW_INFO_FRONTMATTER_EMOJI: "🌐" | ||
| 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 usage cache | ||
| id: restore-daily-aic-cache | ||
| if: ${{ env.GH_AW_MAX_DAILY_AI_CREDITS != '' }} | ||
| continue-on-error: true | ||
| uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | ||
| with: | ||
| key: agentic-workflow-usage-domainvalidationassist-${{ github.run_id }} | ||
| restore-keys: agentic-workflow-usage-domainvalidationassist- | ||
| path: /tmp/gh-aw/agentic-workflow-usage-cache.jsonl | ||
| - name: Restore daily AIC usage cache (artifact fallback) | ||
| id: restore-daily-aic-cache-fallback | ||
| if: ${{ env.GH_AW_MAX_DAILY_AI_CREDITS != '' }} | ||
| continue-on-error: true | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| env: | ||
| GH_AW_RESTORE_DAILY_AIC_CACHE_HIT: ${{ steps.restore-daily-aic-cache.outputs.cache-hit }} | ||
| GH_AW_RESTORE_DAILY_AIC_CACHE_MATCHED_KEY: ${{ steps.restore-daily-aic-cache.outputs.cache-matched-key }} | ||
| with: | ||
| github-token: ${{ secrets.GITHUB_TOKEN }} | ||
| script: | | ||
| const 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_usage_cache_fallback.cjs')); | ||
| await main(); | ||
| - name: Check daily workflow token guardrail | ||
| id: daily-effective-workflow-guardrail | ||
| if: ${{ env.GH_AW_MAX_DAILY_AI_CREDITS != '' }} | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| env: | ||
| GH_AW_WORKFLOW_NAME: "Domain Validation Assist" | ||
| GH_AW_WORKFLOW_ID: "domain-validation-assist" | ||
| 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: Check for OAuth tokens | ||
| id: check-oauth-tokens | ||
| run: bash "${RUNNER_TEMP}/gh-aw/actions/check_oauth_tokens.sh" | ||
| env: | ||
| 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: "domain-validation-assist.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.88.7" | ||
| 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_BOTS: "wingetvalidator-prod[bot]" | ||
| GH_AW_ALLOWED_DOMAINS: "api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,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\"}]}" | ||
| GH_AW_EXPR_1A3A194A: ${{ github.event.discussion.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'discussion' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} | ||
| GH_AW_EXPR_463A214A: ${{ github.event.pull_request.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'pull_request' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} | ||
| GH_AW_EXPR_802A9F6A: ${{ github.event.issue.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'issue' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} | ||
| GH_AW_EXPR_FF1D34CE: ${{ github.event.comment.id || fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').comment_id }} | ||
| GH_AW_GITHUB_ACTOR: ${{ github.actor }} | ||
| GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} | ||
| GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} | ||
| GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} | ||
| GH_AW_PROMPT_CONTENT_0000: "<system>\n" | ||
| GH_AW_PROMPT_CONTENT_0001: "<safe-output-tools>\nTools: noop, post_domain_validation_comment\n" | ||
| GH_AW_PROMPT_CONTENT_0002: "</safe-output-tools>\n" | ||
| GH_AW_PROMPT_CONTENT_0003: "<github-context>\nThe following GitHub context information is available for this workflow:\n{{#if github.actor}}\n- **actor**: __GH_AW_GITHUB_ACTOR__\n{{/if}}\n{{#if github.repository}}\n- **repository**: __GH_AW_GITHUB_REPOSITORY__\n{{/if}}\n{{#if github.workspace}}\n- **workspace**: __GH_AW_GITHUB_WORKSPACE__\n{{/if}}\n{{#if github.event.issue.number || (github.aw.context.item_type == 'issue' && github.aw.context.item_number)}}\n- **issue-number**: #__GH_AW_EXPR_802A9F6A__\n{{/if}}\n{{#if github.event.discussion.number || (github.aw.context.item_type == 'discussion' && github.aw.context.item_number)}}\n- **discussion-number**: #__GH_AW_EXPR_1A3A194A__\n{{/if}}\n{{#if github.event.pull_request.number || (github.aw.context.item_type == 'pull_request' && github.aw.context.item_number)}}\n- **pull-request-number**: #__GH_AW_EXPR_463A214A__\n{{/if}}\n{{#if github.event.comment.id || github.aw.context.comment_id}}\n- **comment-id**: __GH_AW_EXPR_FF1D34CE__\n{{/if}}\n{{#if github.run_id}}\n- **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__\n{{/if}}\n</github-context>\n\n" | ||
| GH_AW_PROMPT_CONTENT_0004: "</system>\n" | ||
| GH_AW_PROMPT_CONTENT_0005: "{{#runtime-import .github/workflows/domain-validation-assist.md}}\n" | ||
| with: | ||
| script: | | ||
| const { setupGlobals } = require(process.env.GH_AW_ACTIONS_DIR + '/setup_globals.cjs'); | ||
| setupGlobals(core, github, context, exec, io, getOctokit); | ||
| const { main } = require(process.env.GH_AW_ACTIONS_DIR + '/create_prompt.cjs'); | ||
| await main(core); | ||
| - name: Interpolate variables and render templates | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| env: | ||
| GH_AW_PROMPT: ${{ runner.temp }}/gh-aw/aw-prompts/prompt.txt | ||
| GH_AW_ENGINE_ID: "copilot" | ||
| with: | ||
| script: | | ||
| const 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_FF1D34CE: ${{ github.event.comment.id || fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').comment_id }} | ||
| GH_AW_GITHUB_ACTOR: ${{ github.actor }} | ||
| GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} | ||
| GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} | ||
| GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} | ||
| GH_AW_MCP_CLI_SERVERS_LIST: "- `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_FF1D34CE: process.env.GH_AW_EXPR_FF1D34CE, | ||
| GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR, | ||
| GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY, | ||
| GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID, | ||
| GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE, | ||
| GH_AW_MCP_CLI_SERVERS_LIST: process.env.GH_AW_MCP_CLI_SERVERS_LIST, | ||
| GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED | ||
| } | ||
| }); | ||
| - name: Validate prompt placeholders | ||
| env: | ||
| GH_AW_PROMPT: ${{ runner.temp }}/gh-aw/aw-prompts/prompt.txt | ||
| run: | | ||
| bash "${RUNNER_TEMP}/gh-aw/actions/validate_prompt_placeholders.sh" | ||
| - name: Print prompt | ||
| env: | ||
| GH_AW_PROMPT: ${{ runner.temp }}/gh-aw/aw-prompts/prompt.txt | ||
| run: | | ||
| bash "${RUNNER_TEMP}/gh-aw/actions/print_prompt_summary.sh" | ||
| - name: Stage prompt files for artifact upload | ||
| run: | | ||
| mkdir -p /tmp/gh-aw/aw-prompts | ||
| cp -a "${RUNNER_TEMP}/gh-aw/aw-prompts/." /tmp/gh-aw/aw-prompts/ | ||
| - name: Upload activation artifact | ||
| if: success() || 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 | ||
| if: needs.activation.outputs.daily_ai_credits_exceeded != 'true' | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| checks: read | ||
| contents: read | ||
| copilot-requests: write | ||
| 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: domainvalidationassist | ||
| outputs: | ||
| agentic_engine_timeout: ${{ steps.detect-agent-errors.outputs.agentic_engine_timeout || 'false' }} | ||
| ai_credits_rate_limit_error: ${{ steps.parse-mcp-gateway.outputs.ai_credits_rate_limit_error || 'false' }} | ||
| aic: ${{ steps.parse-mcp-gateway.outputs.aic }} | ||
| ambient_context: ${{ steps.parse-mcp-gateway.outputs.ambient_context }} | ||
| 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@5e508589e03a7757a7e05b26e834292f5445bfb6 # v0.88.7 | ||
| 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: "Domain Validation Assist" | ||
| GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/domain-validation-assist.lock.yml@${{ github.ref }} | ||
| GH_AW_INFO_VERSION: "1.0.80" | ||
| GH_AW_INFO_AWF_VERSION: "v0.28.14" | ||
| GH_AW_INFO_ENGINE_ID: "copilot" | ||
| - name: Set runtime paths | ||
| id: set-runtime-paths | ||
| env: | ||
| GH_AW_RUNNER_TOOL_CACHE: ${{ runner.tool_cache }} | ||
| run: | | ||
| 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: 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: 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.88.7 | ||
| - name: Install AWF binary | ||
| run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.28.14 --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: 'none' | ||
| GH_AW_GITHUB_REPOS: '["microsoft/winget-pkgs"]' | ||
| 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" | ||
| - env: | ||
| TARGET_PR: ${{ github.event.pull_request.number || '' }} | ||
| TRIGGER_HEAD_SHA: ${{ github.event.pull_request.head.sha || '' }} | ||
| name: Collect trusted validation Checks | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 (source v9) | ||
| with: | ||
| github-token: ${{ github.token }} | ||
| script: "const fs = require(\"fs\");\nconst outputPath = \"/tmp/gh-aw/validation-checks.json\";\nfs.mkdirSync(\"/tmp/gh-aw\", { recursive: true });\nconst owner = \"microsoft\";\nconst repo = \"winget-pkgs\";\nconst trustedAppId = 1451866;\nconst trustedAppSlug = \"wingetvalidator-prod\";\nconst pullRequestNumber = Number(process.env.TARGET_PR);\nconst triggerHeadSha = String(process.env.TRIGGER_HEAD_SHA ?? \"\").trim();\nconst maxEvidenceBytes = 600000;\nconst output = {\n available: false,\n pullRequestNumber: null,\n headSha: null,\n operationId: null,\n currentLabels: [],\n completionLabels: [],\n completionCheck: null,\n checks: [],\n checksTruncated: false,\n};\nconst writeOutput = () =>\n fs.writeFileSync(outputPath, JSON.stringify(output));\nconst isTrustedCheck = (check, headSha) =>\n check?.app?.id === trustedAppId &&\n check?.app?.slug === trustedAppSlug &&\n check.head_sha === headSha;\nconst mapCheck = (check) => {\n const limits = { title: 1000, summary: 4000, text: 32000 };\n const raw = {};\n for (const key of Object.keys(limits)) {\n raw[key] = String(check.output?.[key] ?? \"\");\n }\n return {\n id: check.id,\n name: check.name,\n conclusion: check.conclusion,\n completedAt: check.completed_at,\n externalId: check.external_id,\n output: {\n title: raw.title.slice(0, limits.title),\n summary: raw.summary.slice(0, limits.summary),\n text: raw.text.slice(0, limits.text),\n truncated: Object.keys(limits).some(\n (key) => raw[key].length > limits[key],\n ),\n },\n };\n};\nif (!Number.isSafeInteger(pullRequestNumber) || pullRequestNumber <= 0) {\n output.reason = \"The targeted pull request number is invalid.\";\n writeOutput();\n return;\n}\ntry {\n const pull = await github.rest.pulls.get({\n owner,\n repo,\n pull_number: pullRequestNumber,\n });\n const headSha = String(pull.data.head.sha ?? \"\").trim();\n output.headSha = headSha;\n output.currentLabels = (pull.data.labels ?? [])\n .map((label) => String(label?.name ?? \"\").trim())\n .filter(Boolean);\n if (\n pull.data.state !== \"open\" ||\n !/^[0-9a-f]{40}$/i.test(triggerHeadSha) ||\n triggerHeadSha !== headSha\n ) {\n output.reason =\n \"The pull request is closed or the triggering head SHA is missing or stale.\";\n return;\n }\n let checkRuns = [];\n let totalCheckRuns = 0;\n let completionCheck = null;\n for (let attempt = 0; attempt < 2; attempt++) {\n const response = await github.rest.checks.listForRef({\n owner,\n repo,\n ref: headSha,\n app_id: trustedAppId,\n filter: \"all\",\n per_page: 100,\n });\n checkRuns = response.data.check_runs ?? [];\n totalCheckRuns = response.data.total_count ?? checkRuns.length;\n completionCheck = checkRuns\n .filter((check) =>\n isTrustedCheck(check, headSha) &&\n check.name === \"10. Validation Completed\" &&\n check.status === \"completed\",\n )\n .sort((left, right) => {\n const timeDifference =\n Date.parse(right.completed_at ?? \"\") -\n Date.parse(left.completed_at ?? \"\");\n return timeDifference || Number(right.id) - Number(left.id);\n })[0];\n if (completionCheck || attempt === 1) {\n break;\n }\n await new Promise((resolve) => setTimeout(resolve, 10000));\n }\n const completionJsonBlocks = [...String(\n completionCheck?.output?.text ?? \"\",\n ).matchAll(\n /```json\\s*([\\s\\S]*?)```/gi,\n )];\n let completionPayload = null;\n if (completionJsonBlocks.length === 1) {\n try {\n completionPayload = JSON.parse(completionJsonBlocks[0][1]);\n } catch {\n completionPayload = null;\n }\n }\n const completionPullRequestNumber = completionPayload?.PullRequestNumber;\n const completionOperationId =\n String(completionPayload?.OperationId ?? \"\").trim();\n const completionExternalId =\n String(completionCheck?.external_id ?? \"\").trim();\n if (\n !completionCheck ||\n !Number.isSafeInteger(completionPullRequestNumber) ||\n completionPullRequestNumber !== pullRequestNumber ||\n !completionOperationId ||\n completionOperationId !== completionExternalId\n ) {\n output.reason =\n \"The newest Validation Completed Check is missing or does not bind this pull request to one operation.\";\n return;\n }\n const completionTime = Date.parse(completionCheck.completed_at ?? \"\");\n const newerPendingCheck = checkRuns.some(\n (check) =>\n isTrustedCheck(check, headSha) &&\n [\"queued\", \"in_progress\"].includes(check.status) &&\n (Number(check.id) > Number(completionCheck.id) ||\n Date.parse(check.started_at ?? \"\") > completionTime),\n );\n if (totalCheckRuns > checkRuns.length || newerPendingCheck) {\n output.reason =\n \"Check data is incomplete or a newer validation operation is still running.\";\n return;\n }\n output.pullRequestNumber = completionPullRequestNumber;\n output.operationId = completionOperationId;\n output.completionLabels = Array.isArray(completionPayload?.Labels)\n ? completionPayload.Labels.map((label) => ({\n name: String(label?.Name ?? \"\").trim(),\n result: String(label?.Result ?? \"\").trim(),\n })).filter((label) => label.name)\n : [];\n const operationChecks = checkRuns\n .filter(\n (check) =>\n isTrustedCheck(check, headSha) &&\n check.status === \"completed\" &&\n check.name !== \"10. Validation Completed\" &&\n String(check.external_id ?? \"\").trim() ===\n completionOperationId,\n )\n .sort((left, right) =>\n String(left.name).localeCompare(String(right.name)),\n );\n output.completionCheck = mapCheck(completionCheck);\n output.checks = operationChecks.slice(0, 12).map(mapCheck);\n output.checksTruncated =\n operationChecks.length > output.checks.length ||\n output.completionCheck.output.truncated ||\n output.checks.some((check) => check.output.truncated);\n output.available = output.checks.length > 0 && !output.checksTruncated;\n if (output.checksTruncated) {\n output.reason =\n \"One or more trusted Check outputs were truncated.\";\n }\n if (!output.available) {\n output.reason ??=\n \"No completed trusted Check belongs to the newest validation operation.\";\n }\n if (\n output.available &&\n Buffer.byteLength(JSON.stringify(output), \"utf8\") >\n maxEvidenceBytes\n ) {\n output.available = false;\n output.operationId = null;\n output.completionCheck = null;\n output.checks = [];\n output.reason =\n \"The complete evidence envelope exceeds the review bound.\";\n }\n} catch (error) {\n output.reason = `Validation Check retrieval failed: ${\n error instanceof Error ? error.message : String(error)\n }`;\n} finally {\n writeOutput();\n}\n" | ||
| - env: | ||
| GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} | ||
| name: Skip agent when domain evidence is unavailable | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 (source v9) | ||
| with: | ||
| script: "const fs = require(\"fs\");\nconst path = require(\"path\");\nconst evidence = JSON.parse(\n fs.readFileSync(\"/tmp/gh-aw/validation-checks.json\", \"utf8\"),\n);\nif (evidence.available !== true) {\n const safeOutputsPath =\n String(process.env.GH_AW_SAFE_OUTPUTS ?? \"\").trim() ||\n path.join(\n process.env.RUNNER_TEMP || \"/tmp\",\n \"gh-aw\",\n \"safeoutputs\",\n \"outputs.jsonl\",\n );\n fs.mkdirSync(path.dirname(safeOutputsPath), { recursive: true });\n fs.appendFileSync(\n safeOutputsPath,\n `${JSON.stringify({\n type: \"noop\",\n message: \"No trusted domain validation evidence is available.\",\n })}\\n`,\n );\n}\n" | ||
| - name: Upload sealed domain validation evidence | ||
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 (source v7) | ||
| with: | ||
| if-no-files-found: error | ||
| name: domain-validation-evidence-${{ github.run_id }}-${{ github.run_attempt }} | ||
| path: /tmp/gh-aw/validation-checks.json | ||
| retention-days: 1 | ||
| - name: Download container images | ||
| run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.28.14@sha256:f7df036c86575527b61f3f7df91c4412349a12b2a74988d929eafa2999230c98 ghcr.io/github/gh-aw-firewall/api-proxy:0.28.14@sha256:6f95e2234dd9bd6333a8ff28ccea7ecf0204acd4a09108723844dbd2bf6268c5 ghcr.io/github/gh-aw-firewall/squid:0.28.14@sha256:2ce8df3abf3e9b76e9c0cf5863da41f1ab3f89b20ad14b988806ab89e7bf2cd5 ghcr.io/github/gh-aw-mcpg:v0.4.18@sha256:85b940556a8faa4e1fdbef124bfd75f2c4ebd855a10b88a1c3b6f3e97f6f1a53 ghcr.io/github/gh-aw-node@sha256:0daa8971fa4732b647150cb6524a6b0804b68d5d24f6f58b5dd1af23bd63fb23 ghcr.io/github/github-mcp-server:v1.11.0@sha256:fbec75de11c255213fa08d80fb166abe73d851fff631c51c0079872967720699 | ||
| - 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: "{\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"post-domain-validation-comment\":{\"description\":\"Post the one validated domain-assist comment to the triggering pull request from structured, evidence-bound fields.\",\"inputs\":{\"check_name\":{\"default\":null,\"description\":\"Exact trusted Check Run name containing the evidence\",\"required\":true,\"type\":\"string\"},\"classification\":{\"default\":null,\"description\":\"Exact supported domain finding class\",\"required\":true,\"type\":\"string\"},\"hostname\":{\"default\":null,\"description\":\"Lowercase hostname named by the trusted Check\",\"required\":true,\"type\":\"string\"}}},\"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": {}, | ||
| "repo_params": {}, | ||
| "dynamic_tools": [ | ||
| { | ||
| "description": "Post the one validated domain-assist comment to the triggering pull request from structured, evidence-bound fields.", | ||
| "inputSchema": { | ||
| "additionalProperties": false, | ||
| "properties": { | ||
| "check_name": { | ||
| "description": "Exact trusted Check Run name containing the evidence", | ||
| "type": "string" | ||
| }, | ||
| "classification": { | ||
| "description": "Exact supported domain finding class", | ||
| "type": "string" | ||
| }, | ||
| "hostname": { | ||
| "description": "Lowercase hostname named by the trusted Check", | ||
| "type": "string" | ||
| } | ||
| }, | ||
| "required": [ | ||
| "check_name", | ||
| "classification", | ||
| "hostname" | ||
| ], | ||
| "type": "object" | ||
| }, | ||
| "name": "post_domain_validation_comment" | ||
| } | ||
| ] | ||
| } | ||
| GH_AW_VALIDATION_JSON: | | ||
| { | ||
| "noop": { | ||
| "defaultMax": 1, | ||
| "fields": { | ||
| "message": { | ||
| "required": true, | ||
| "type": "string", | ||
| "sanitize": true, | ||
| "maxLength": 65000 | ||
| } | ||
| } | ||
| }, | ||
| "report_incomplete": { | ||
| "defaultMax": 5, | ||
| "fields": { | ||
| "details": { | ||
| "type": "string", | ||
| "sanitize": true, | ||
| "maxLength": 65000 | ||
| }, | ||
| "reason": { | ||
| "required": true, | ||
| "type": "string", | ||
| "sanitize": true, | ||
| "maxLength": 1024 | ||
| } | ||
| } | ||
| } | ||
| } | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| with: | ||
| script: | | ||
| const 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.18' | ||
| 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_c5c8d9e37ec43bcb_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.11.0", | ||
| "env": { | ||
| "GITHUB_FEATURES": "fields_param", | ||
| "GITHUB_HOST": "${GITHUB_SERVER_URL}", | ||
| "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_MCP_SERVER_TOKEN}", | ||
| "GITHUB_READ_ONLY": "1", | ||
| "GITHUB_TOOLSETS": "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": "none", | ||
| "repos": [ | ||
| "microsoft/winget-pkgs" | ||
| ], | ||
| "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/winget-pkgs" | ||
| ], | ||
| "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_c5c8d9e37ec43bcb_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): | ||
| # --allow-tool github | ||
| # --allow-tool safeoutputs | ||
| # --allow-tool shell(cat) | ||
| # --allow-tool shell(date) | ||
| # --allow-tool shell(echo) | ||
| # --allow-tool shell(github:*) | ||
| # --allow-tool shell(grep) | ||
| # --allow-tool shell(head) | ||
| # --allow-tool shell(ls) | ||
| # --allow-tool shell(printf) | ||
| # --allow-tool shell(pwd) | ||
| # --allow-tool shell(safeoutputs:*) | ||
| # --allow-tool shell(sort) | ||
| # --allow-tool shell(tail) | ||
| # --allow-tool shell(uniq) | ||
| # --allow-tool shell(wc) | ||
| # --allow-tool shell(yq) | ||
| # --allow-tool write | ||
| timeout-minutes: ${{ fromJSON(vars.GH_AW_DEFAULT_TIMEOUT_MINUTES || '20') }} | ||
| run: | | ||
| set -o pipefail | ||
| printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt | ||
| trap 'gh_aw_exit_code=$?; mkdir -p /tmp/gh-aw >/dev/null 2>&1 || true; printf "%s" "$gh_aw_exit_code" > /tmp/gh-aw/agent_execution_exit_code.txt || true; rm -f "$HOME/.copilot/settings.json"; 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.14/awf-config.schema.json\",\"network\":{\"allowDomains\":[\"api.snapcraft.io\",\"archive.ubuntu.com\",\"azure.archive.ubuntu.com\",\"crl.geotrust.com\",\"crl.globalsign.com\",\"crl.identrust.com\",\"crl.sectigo.com\",\"crl.thawte.com\",\"crl.usertrust.com\",\"crl.verisign.com\",\"crl3.digicert.com\",\"crl4.digicert.com\",\"crls.ssl.com\",\"json-schema.org\",\"json.schemastore.org\",\"keyserver.ubuntu.com\",\"ocsp.digicert.com\",\"ocsp.geotrust.com\",\"ocsp.globalsign.com\",\"ocsp.identrust.com\",\"ocsp.sectigo.com\",\"ocsp.ssl.com\",\"ocsp.thawte.com\",\"ocsp.usertrust.com\",\"ocsp.verisign.com\",\"packagecloud.io\",\"packages.cloud.google.com\",\"packages.microsoft.com\",\"ppa.launchpad.net\",\"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-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*\"],\"grok\":[\"copilot/*grok*\",\"openai/*grok*\"],\"haiku\":[\"copilot/*haiku*\",\"anthropic/*haiku*\"],\"image-generation\":[\"copilot/gpt-image*\",\"openai/gpt-image*\",\"openai/chatgpt-image*\",\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"google/imagen*\"],\"kimi\":[\"copilot/kimi*\",\"openai/kimi*\"],\"kiwi\":[\"copilot/kiwi*\",\"openai/kiwi*\"],\"large\":[\"sonnet\",\"gpt-5-pro\",\"gpt-5\",\"gemini-pro\"],\"lyria\":[\"google/lyria*\",\"gemini/lyria*\",\"copilot/lyria*\"],\"mai-code\":[\"copilot/MAI-Code*\",\"copilot/mai-code*\",\"openai/MAI-Code*\"],\"mai-code-1-flash-picker\":[\"copilot/MAI-Code-1-Flash-picker*\",\"copilot/mai-code-1-flash-picker*\",\"openai/MAI-Code-1-Flash-picker*\"],\"mini\":[\"haiku\",\"gpt-5-mini\",\"gpt-5-nano\",\"gemini-flash-lite\"],\"nano-banana\":[\"copilot/nano-banana*\",\"google/nano-banana*\",\"gemini/nano-banana*\"],\"opus\":[\"copilot/*opus*\",\"anthropic/*opus*\"],\"opusplan\":[\"opus?effort=high\"],\"raptor-mini\":[\"copilot/raptor*\",\"openai/raptor*\"],\"reasoning\":[\"copilot/o1*\",\"copilot/o3*\",\"copilot/o4*\",\"openai/o1*\",\"openai/o3*\",\"openai/o4*\"],\"robotics\":[\"copilot/*robotics*\",\"google/*robotics*\",\"gemini/*robotics*\"],\"small\":[\"mini\"],\"small-agent\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash\"],\"sonnet\":[\"copilot/*sonnet*\",\"anthropic/*sonnet*\"],\"sonnet-6x\":[\"copilot/*sonnet-4.5*\",\"copilot/*sonnet-4.6*\",\"copilot/*sonnet-5*\",\"copilot/*sonnet-4-5-*\",\"anthropic/*sonnet-4-5-*\",\"copilot/*sonnet-4-6*\",\"anthropic/*sonnet-4-6*\",\"anthropic/*sonnet-5*\"],\"summarization\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash-lite\",\"mini\"],\"veo\":[\"google/veo*\",\"gemini/veo*\"],\"vision\":[\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"]}},\"container\":{\"imageTag\":\"0.28.14,squid=sha256:2ce8df3abf3e9b76e9c0cf5863da41f1ab3f89b20ad14b988806ab89e7bf2cd5,agent=sha256:f7df036c86575527b61f3f7df91c4412349a12b2a74988d929eafa2999230c98,api-proxy=sha256:6f95e2234dd9bd6333a8ff28ccea7ecf0204acd4a09108723844dbd2bf6268c5,cli-proxy=sha256:3a379c5e96e29499c815e9dd2a71334d01c326a9b73991c76544fda9cae35c34\"},\"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 | ||
| 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-tool github --allow-tool safeoutputs --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(date)'\'' --allow-tool '\''shell(echo)'\'' --allow-tool '\''shell(github:*)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(printf)'\'' --allow-tool '\''shell(pwd)'\'' --allow-tool '\''shell(safeoutputs:*)'\'' --allow-tool '\''shell(sort)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(uniq)'\'' --allow-tool '\''shell(wc)'\'' --allow-tool '\''shell(yq)'\'' --allow-tool write --allow-all-paths --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' | ||
| env: | ||
| AWF_REFLECT_ENABLED: 1 | ||
| COPILOT_AGENT_RUNNER_TYPE: STANDALONE | ||
| COPILOT_DUMMY_BYOK: dummy-byok-key-for-offline-mode | ||
| COPILOT_GITHUB_TOKEN: ${{ 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: ${{ fromJSON(vars.GH_AW_DEFAULT_TIMEOUT_MINUTES || '20') }} | ||
| GH_AW_VERSION: v0.88.7 | ||
| 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 }} | ||
| S2STOKENS: true | ||
| 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: ${{ fromJSON(vars.GH_AW_DEFAULT_TIMEOUT_MINUTES || '20') }} | ||
| 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: 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: 'GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,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: "api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,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() | ||
| 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/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-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 | ||
| conclusion: | ||
| needs: | ||
| - activation | ||
| - agent | ||
| - detection | ||
| - post_domain_validation_comment | ||
| if: > | ||
| always() && (needs.agent.result != 'skipped' || needs.activation.outputs.lockdown_check_failed == 'true' || | ||
| needs.activation.outputs.oauth_token_check_failed == 'true' || needs.activation.outputs.stale_lock_file_failed == 'true' || | ||
| needs.activation.outputs.daily_ai_credits_exceeded == 'true') | ||
| runs-on: ubuntu-slim | ||
| permissions: | ||
| actions: write | ||
| concurrency: | ||
| group: "gh-aw-conclusion-domain-validation-assist" | ||
| 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 }} | ||
| steps: | ||
| - name: Setup Scripts | ||
| id: setup | ||
| uses: github/gh-aw-actions/setup@5e508589e03a7757a7e05b26e834292f5445bfb6 # v0.88.7 | ||
| 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: "Domain Validation Assist" | ||
| GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/domain-validation-assist.lock.yml@${{ github.ref }} | ||
| GH_AW_INFO_VERSION: "1.0.80" | ||
| GH_AW_INFO_AWF_VERSION: "v0.28.14" | ||
| 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/detection/token_usage.jsonl | ||
| /tmp/gh-aw/usage/activity/summary.json | ||
| if-no-files-found: ignore | ||
| - name: Restore daily AIC usage cache | ||
| id: restore-daily-aic-cache-conclusion | ||
| if: always() | ||
| continue-on-error: true | ||
| uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | ||
| with: | ||
| key: agentic-workflow-usage-domainvalidationassist-${{ github.run_id }} | ||
| restore-keys: agentic-workflow-usage-domainvalidationassist- | ||
| path: /tmp/gh-aw/agentic-workflow-usage-cache.jsonl | ||
| - name: Write daily AIC usage cache entry | ||
| id: write-daily-aic-cache | ||
| if: always() | ||
| continue-on-error: true | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| with: | ||
| github-token: ${{ github.token }} | ||
| script: | | ||
| const 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); | ||
| const { main } = require(path.join(actionsDir, 'write_daily_aic_usage_cache.cjs')); | ||
| await main(); | ||
| - name: Save daily AIC usage cache | ||
| id: save-daily-aic-cache | ||
| if: always() | ||
| continue-on-error: true | ||
| uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | ||
| with: | ||
| key: agentic-workflow-usage-domainvalidationassist-${{ github.run_id }} | ||
| path: /tmp/gh-aw/agentic-workflow-usage-cache.jsonl | ||
| - name: Upload daily AIC usage cache artifact | ||
| id: upload-daily-aic-cache | ||
| if: always() | ||
| continue-on-error: true | ||
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | ||
| with: | ||
| name: aic-usage-cache | ||
| path: /tmp/gh-aw/agentic-workflow-usage-cache.jsonl | ||
| if-no-files-found: ignore | ||
| retention-days: 7 | ||
| - name: Process no-op messages | ||
| id: noop | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| env: | ||
| GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} | ||
| GH_AW_NOOP_MAX: "1" | ||
| GH_AW_WORKFLOW_NAME: "Domain Validation Assist" | ||
| GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/domain-validation-assist.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: "domain-validation-assist" | ||
| 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: "Domain Validation Assist" | ||
| GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/domain-validation-assist.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 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: "false" | ||
| GH_AW_REPORT_INCOMPLETE_TITLE_PREFIX: "[incomplete]" | ||
| GH_AW_WORKFLOW_NAME: "Domain Validation Assist" | ||
| GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/domain-validation-assist.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: "Domain Validation Assist" | ||
| GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/domain-validation-assist.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: "domain-validation-assist" | ||
| GH_AW_ACTION_FAILURE_ISSUE_EXPIRES_HOURS: "0" | ||
| GH_AW_ENGINE_ID: "copilot" | ||
| 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_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: "false" | ||
| GH_AW_MISSING_TOOL_REPORT_AS_FAILURE: "true" | ||
| GH_AW_MISSING_DATA_REPORT_AS_FAILURE: "true" | ||
| GH_AW_TIMEOUT_MINUTES: "${{ fromJSON(vars.GH_AW_DEFAULT_TIMEOUT_MINUTES || '20') }}" | ||
| 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(); | ||
| detection: | ||
| needs: | ||
| - activation | ||
| - agent | ||
| if: always() && needs.agent.result != 'skipped' | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: read | ||
| copilot-requests: write | ||
| 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@5e508589e03a7757a7e05b26e834292f5445bfb6 # v0.88.7 | ||
| 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: "Domain Validation Assist" | ||
| GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/domain-validation-assist.lock.yml@${{ github.ref }} | ||
| GH_AW_INFO_VERSION: "1.0.80" | ||
| GH_AW_INFO_AWF_VERSION: "v0.28.14" | ||
| 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: 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.14@sha256:f7df036c86575527b61f3f7df91c4412349a12b2a74988d929eafa2999230c98 ghcr.io/github/gh-aw-firewall/api-proxy:0.28.14@sha256:6f95e2234dd9bd6333a8ff28ccea7ecf0204acd4a09108723844dbd2bf6268c5 ghcr.io/github/gh-aw-firewall/squid:0.28.14@sha256:2ce8df3abf3e9b76e9c0cf5863da41f1ab3f89b20ad14b988806ab89e7bf2cd5 | ||
| - 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: "Domain Validation Assist" | ||
| WORKFLOW_DESCRIPTION: "Experimental author assist for trusted WinGetValidator URL and domain results. Posts one bounded recommendation only for explicit URL evidence or an exact maintained approval-inventory match." | ||
| 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.14 --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.88.7 | ||
| - 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: ${{ 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.88.7 | ||
| 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 }} | ||
| S2STOKENS: true | ||
| 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: "Domain Validation Assist" | ||
| WORKFLOW_DESCRIPTION: "Experimental author assist for trusted WinGetValidator URL and domain results. Posts one bounded recommendation only for explicit URL evidence or an exact maintained approval-inventory match." | ||
| HAS_PATCH: ${{ needs.agent.outputs.has_patch }} | ||
| GH_AW_DETECTION_CONTINUE_ON_ERROR: "true" | ||
| run: | | ||
| 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.14/awf-config.schema.json\",\"apiProxy\":{\"enabled\":true,\"enableTokenSteering\":true,\"maxRuns\":500,\"maxAiCredits\":${GH_AW_MAX_AI_CREDITS},\"maxCacheMisses\":5,\"models\":{\"agent\":[\"sonnet-6x\",\"gpt-5.4\",\"gpt-5.5\",\"gpt-5.6\",\"gpt-5.3\",\"gemini-pro\",\"any\"],\"antigravity\":[\"copilot/antigravity*\",\"google/antigravity*\",\"gemini/antigravity*\"],\"any\":[\"copilot/*\",\"anthropic/*\",\"openai/*\",\"google/*\",\"gemini/*\"],\"auto\":[\"copilot/auto\",\"large\"],\"claude\":[\"agent\"],\"codex\":[\"agent\"],\"coding\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\",\"gpt-5-codex\",\"kimi\"],\"computer-use\":[\"copilot/*computer-use*\",\"google/*computer-use*\",\"gemini/*computer-use*\",\"openai/*computer-use*\"],\"copilot\":[\"agent\"],\"deep-research\":[\"copilot/deep-research*\",\"copilot/o3-deep-research*\",\"copilot/o4-mini-deep-research*\",\"google/deep-research*\",\"gemini/deep-research*\",\"openai/o3-deep-research*\",\"openai/o4-mini-deep-research*\"],\"detection\":[\"small\"],\"evals\":[\"small\"],\"fable\":[\"copilot/*fable*\",\"anthropic/*fable*\"],\"gemini\":[\"agent\"],\"gemini-3-flash\":[\"copilot/gemini-3*flash*\",\"google/gemini-3*flash*\",\"gemini/gemini-3*flash*\"],\"gemini-3-pro\":[\"copilot/gemini-3*pro*\",\"google/gemini-3*pro*\",\"google/nano-banana*\",\"gemini/gemini-3*pro*\"],\"gemini-3.1-flash\":[\"copilot/gemini-3.1*flash*\",\"google/gemini-3.1*flash*\",\"gemini/gemini-3.1*flash*\"],\"gemini-3.1-pro\":[\"copilot/gemini-3.1*pro*\",\"google/gemini-3.1*pro*\",\"gemini/gemini-3.1*pro*\"],\"gemini-3.5-flash\":[\"copilot/gemini-3.5*flash*\",\"google/gemini-3.5*flash*\",\"gemini/gemini-3.5*flash*\"],\"gemini-3.6-flash\":[\"copilot/gemini-3.6*flash*\",\"google/gemini-3.6*flash*\",\"gemini/gemini-3.6*flash*\"],\"gemini-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*\"],\"grok\":[\"copilot/*grok*\",\"openai/*grok*\"],\"haiku\":[\"copilot/*haiku*\",\"anthropic/*haiku*\"],\"image-generation\":[\"copilot/gpt-image*\",\"openai/gpt-image*\",\"openai/chatgpt-image*\",\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"google/imagen*\"],\"kimi\":[\"copilot/kimi*\",\"openai/kimi*\"],\"kiwi\":[\"copilot/kiwi*\",\"openai/kiwi*\"],\"large\":[\"sonnet\",\"gpt-5-pro\",\"gpt-5\",\"gemini-pro\"],\"lyria\":[\"google/lyria*\",\"gemini/lyria*\",\"copilot/lyria*\"],\"mai-code\":[\"copilot/MAI-Code*\",\"copilot/mai-code*\",\"openai/MAI-Code*\"],\"mai-code-1-flash-picker\":[\"copilot/MAI-Code-1-Flash-picker*\",\"copilot/mai-code-1-flash-picker*\",\"openai/MAI-Code-1-Flash-picker*\"],\"mini\":[\"haiku\",\"gpt-5-mini\",\"gpt-5-nano\",\"gemini-flash-lite\"],\"nano-banana\":[\"copilot/nano-banana*\",\"google/nano-banana*\",\"gemini/nano-banana*\"],\"opus\":[\"copilot/*opus*\",\"anthropic/*opus*\"],\"opusplan\":[\"opus?effort=high\"],\"raptor-mini\":[\"copilot/raptor*\",\"openai/raptor*\"],\"reasoning\":[\"copilot/o1*\",\"copilot/o3*\",\"copilot/o4*\",\"openai/o1*\",\"openai/o3*\",\"openai/o4*\"],\"robotics\":[\"copilot/*robotics*\",\"google/*robotics*\",\"gemini/*robotics*\"],\"small\":[\"mini\"],\"small-agent\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash\"],\"sonnet\":[\"copilot/*sonnet*\",\"anthropic/*sonnet*\"],\"sonnet-6x\":[\"copilot/*sonnet-4.5*\",\"copilot/*sonnet-4.6*\",\"copilot/*sonnet-5*\",\"copilot/*sonnet-4-5-*\",\"anthropic/*sonnet-4-5-*\",\"copilot/*sonnet-4-6*\",\"anthropic/*sonnet-4-6*\",\"anthropic/*sonnet-5*\"],\"summarization\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash-lite\",\"mini\"],\"veo\":[\"google/veo*\",\"gemini/veo*\"],\"vision\":[\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"]}},\"container\":{\"imageTag\":\"0.28.14,squid=sha256:2ce8df3abf3e9b76e9c0cf5863da41f1ab3f89b20ad14b988806ab89e7bf2cd5,agent=sha256:f7df036c86575527b61f3f7df91c4412349a12b2a74988d929eafa2999230c98,api-proxy=sha256:6f95e2234dd9bd6333a8ff28ccea7ecf0204acd4a09108723844dbd2bf6268c5,cli-proxy=sha256:3a379c5e96e29499c815e9dd2a71334d01c326a9b73991c76544fda9cae35c34\"},\"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: Upload threat detection artifact | ||
| if: always() && steps.detection_guard.outputs.run_detection == 'true' | ||
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | ||
| with: | ||
| name: detection | ||
| path: | | ||
| /tmp/gh-aw/threat-detection/detection_result.json | ||
| /tmp/gh-aw/threat-detection/sandbox/firewall/logs/ | ||
| /tmp/gh-aw/threat-detection/sandbox/firewall/audit/ | ||
| if-no-files-found: ignore | ||
| - name: Parse threat detection token usage for step summary | ||
| id: parse_detection_token_usage | ||
| if: always() | ||
| continue-on-error: true | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| env: | ||
| GH_AW_TOKEN_USAGE_SUMMARY_TITLE: Threat Detection Token Usage | ||
| with: | ||
| script: | | ||
| const 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: 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 | ||
| post_domain_validation_comment: | ||
| needs: | ||
| - agent | ||
| - detection | ||
| - detection | ||
| if: > | ||
| (!cancelled()) && needs.agent.result != 'skipped' && contains(needs.agent.outputs.output_types, 'post_domain_validation_comment') && | ||
| (needs.detection.result == 'success' && needs.detection.outputs.detection_success == 'true') | ||
| runs-on: ubuntu-slim | ||
| permissions: | ||
| checks: read | ||
| contents: read | ||
| pull-requests: write | ||
| steps: | ||
| - name: Download agent output artifact | ||
| continue-on-error: true | ||
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | ||
| with: | ||
| pattern: "{agent,agent-output-fallback}" | ||
| merge-multiple: true | ||
| path: ${{ runner.temp }}/gh-aw/safe-jobs/ | ||
| - name: Download sealed domain validation evidence | ||
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 (source v8) | ||
| env: | ||
| GH_AW_AGENT_OUTPUT: ${{ runner.temp }}/gh-aw/safe-jobs/agent_output.json | ||
| with: | ||
| name: domain-validation-evidence-${{ github.run_id }}-${{ github.run_attempt }} | ||
| path: ${{ runner.temp }}/domain-validation-evidence | ||
| - name: Revalidate and post fixed-target comment | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 (source v9) | ||
| env: | ||
| EVENT_HEAD: ${{ github.event.pull_request.head.sha || '' }} | ||
| EVENT_LABEL: ${{ github.event.label.name || '' }} | ||
| EVIDENCE_PATH: ${{ runner.temp }}/domain-validation-evidence/validation-checks.json | ||
| GH_AW_AGENT_OUTPUT: ${{ runner.temp }}/gh-aw/safe-jobs/agent_output.json | ||
| TARGET_PR: ${{ github.event.pull_request.number || '' }} | ||
| with: | ||
| github-token: ${{ github.token }} | ||
| script: | | ||
| const fs = require("fs"); | ||
| const owner = "microsoft"; | ||
| const repo = "winget-pkgs"; | ||
| const targetPr = Number(process.env.TARGET_PR); | ||
| const eventHead = String(process.env.EVENT_HEAD ?? "").trim(); | ||
| const eventLabel = String(process.env.EVENT_LABEL ?? "").trim(); | ||
| const type = "post_domain_validation_comment"; | ||
| const trustedAppId = 1451866; | ||
| const trustedAppSlug = "wingetvalidator-prod"; | ||
| const footer = | ||
| "###### Template: msftbot/authorAssist/domainValidation"; | ||
| const classifications = new Set([ | ||
| "DEAD_URL", "MALFORMED_URL", "WAIVER_REVIEW", | ||
| "CDN_REDIRECT_REVIEW", | ||
| ]); | ||
| const supported = new Set([ | ||
| "Error-Installer-Availability", "Validate-Domain-Installer", | ||
| "Validation-404-Error", "Validation-Agreement-Domain", | ||
| "Validation-Domain", "Validation-Domains-Mismatch", | ||
| "Validation-Forbidden-URL-Error", "Validation-Indirect-URL", | ||
| "Validation-Open-Url-Failed", "Validation-Unapproved-URL", | ||
| ]); | ||
| const security = new Set([ | ||
| "Binary-Validation-Error", "Blocking-Issue", | ||
| "Error-Analysis-Timeout", "Error-Hash-Mismatch", | ||
| "Internal-Error", "Internal-Error-AppsAndFeaturesVersion", | ||
| "Internal-Error-Dependencies", "Internal-Error-Domain", | ||
| "Internal-Error-Dynamic-Scan", "Internal-Error-Keyword-Policy", | ||
| "Internal-Error-Manifest", "Internal-Error-Manifest-Installer", | ||
| "Internal-Error-NoArchitectures", | ||
| "Internal-Error-NoSupportedArchitectures", "Internal-Error-PR", | ||
| "Internal-Error-Static-Scan", "Internal-Error-URL", | ||
| "Internal-Error-Webhook", "Needs-SmartScreen-Investigation", | ||
| "Network-Blocker", "Package-Flagged", "PUA-Detection", | ||
| "PullRequest-Error", "Scripted-Application", | ||
| "URL-Validation-Error", "Validation-Certificate-Root", | ||
| "Validation-Defender-Error", "Validation-Executable-Error", | ||
| "Validation-Hash-Flagged", "Validation-Hash-Verification-Failed", | ||
| "Validation-HTTP-Error", "Validation-No-Executables", | ||
| "Validation-Shell-Execute", "Validation-SmartScreen", | ||
| "Validation-SmartScreen-Error", "Validation-Submission-Expired", | ||
| "Validation-Submission-Failed", | ||
| "Validation-Submission-Mismatch", | ||
| "Validation-Submission-Missing", | ||
| "Validation-Submission-Unsupported", | ||
| "Validation-Virus-Scan-Error", | ||
| ]); | ||
| const fail = (message) => { | ||
| core.setFailed(message); | ||
| return false; | ||
| }; | ||
| const parseCompletionPayload = (check) => { | ||
| const blocks = [...String(check?.output?.text ?? "").matchAll( | ||
| /```json\s*([\s\S]*?)```/gi, | ||
| )]; | ||
| if (blocks.length !== 1) return null; | ||
| try { | ||
| return JSON.parse(blocks[0][1]); | ||
| } catch { | ||
| return null; | ||
| } | ||
| }; | ||
| const hostnameFromUri = (value) => { | ||
| try { | ||
| return new URL(value).hostname.toLowerCase(); | ||
| } catch { | ||
| return String(value).match( | ||
| /^[a-z][a-z0-9+.-]*:\/\/([^/:?#\s]+)/i, | ||
| )?.[1]?.toLowerCase() ?? null; | ||
| } | ||
| }; | ||
| const parseCsv = (text) => { | ||
| const rows = []; | ||
| let row = []; | ||
| let field = ""; | ||
| let quoted = false; | ||
| for (let index = 0; index < text.length; index++) { | ||
| const character = text[index]; | ||
| if (quoted) { | ||
| if (character === '"') { | ||
| if (text[index + 1] === '"') { | ||
| field += '"'; | ||
| index++; | ||
| } else { | ||
| quoted = false; | ||
| } | ||
| } else { | ||
| field += character; | ||
| } | ||
| } else if (character === '"') { | ||
| if (field.length !== 0) { | ||
| throw new Error("Invalid CSV quoting."); | ||
| } | ||
| quoted = true; | ||
| } else if (character === ",") { | ||
| row.push(field); | ||
| field = ""; | ||
| } else if (character === "\n") { | ||
| row.push(field.replace(/\r$/, "")); | ||
| rows.push(row); | ||
| row = []; | ||
| field = ""; | ||
| } else { | ||
| field += character; | ||
| } | ||
| } | ||
| if (quoted) throw new Error("Unterminated CSV field."); | ||
| if (field.length !== 0 || row.length !== 0) { | ||
| row.push(field.replace(/\r$/, "")); | ||
| rows.push(row); | ||
| } | ||
| return rows; | ||
| }; | ||
| if ( | ||
| !Number.isSafeInteger(targetPr) || | ||
| targetPr <= 0 || | ||
| !/^[0-9a-f]{40}$/i.test(eventHead) || | ||
| !supported.has(eventLabel) | ||
| ) { | ||
| return fail("Invalid trusted pull-request event context."); | ||
| } | ||
| let items, evidence; | ||
| try { | ||
| const evidencePath = process.env.EVIDENCE_PATH; | ||
| if (!evidencePath || fs.statSync(evidencePath).size > 600000) { | ||
| throw new Error("Sealed evidence is unavailable or oversized."); | ||
| } | ||
| evidence = JSON.parse(fs.readFileSync(evidencePath, "utf8")); | ||
| items = JSON.parse(fs.readFileSync( | ||
| process.env.GH_AW_AGENT_OUTPUT, "utf8", | ||
| )).items; | ||
| } catch { | ||
| return fail("Agent output or sealed evidence is missing or invalid."); | ||
| } | ||
| const operationId = String(evidence?.operationId ?? ""); | ||
| if ( | ||
| evidence?.available !== true || | ||
| evidence?.checksTruncated !== false || | ||
| evidence?.pullRequestNumber !== targetPr || | ||
| evidence?.headSha !== eventHead || | ||
| operationId.length === 0 || | ||
| operationId.length > 128 || | ||
| !Array.isArray(evidence?.currentLabels) || | ||
| !evidence.currentLabels.includes(eventLabel) || | ||
| !Array.isArray(evidence?.completionLabels) || | ||
| !evidence.completionLabels.some( | ||
| (label) => label?.name === eventLabel, | ||
| ) || | ||
| evidence?.completionCheck?.name !== | ||
| "10. Validation Completed" || | ||
| evidence.completionCheck.externalId !== operationId || | ||
| !Array.isArray(evidence?.checks) || | ||
| evidence.checks.length === 0 || | ||
| evidence.checks.length > 12 || | ||
| evidence.checks.some( | ||
| (check) => check?.externalId !== operationId, | ||
| ) | ||
| ) { | ||
| return fail("Sealed domain evidence is not publishable."); | ||
| } | ||
| const matches = Array.isArray(items) | ||
| ? items.filter((item) => item?.type === type) : []; | ||
| if (matches.length !== 1) { | ||
| return fail("Expected exactly one domain comment output."); | ||
| } | ||
| const item = matches[0]; | ||
| const expectedKeys = [ | ||
| "check_name", "classification", "hostname", "type", | ||
| ]; | ||
| if ( | ||
| Object.keys(item).sort().join(",") !== | ||
| expectedKeys.sort().join(",") | ||
| ) { | ||
| return fail("Domain comment output has unexpected fields."); | ||
| } | ||
| const classification = String(item.classification ?? "").trim(); | ||
| const checkName = String(item.check_name ?? "").trim(); | ||
| const hostname = String(item.hostname ?? "").trim(); | ||
| if ( | ||
| !classifications.has(classification) || | ||
| !["03. URLs Validation", "04. URL Domain Validation"].includes( | ||
| checkName, | ||
| ) || | ||
| hostname !== hostname.toLowerCase() || | ||
| hostname.length > 253 || | ||
| !/^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/.test( | ||
| hostname, | ||
| ) | ||
| ) { | ||
| return fail("Structured domain output failed validation."); | ||
| } | ||
| const pull = await github.rest.pulls.get({ | ||
| owner, | ||
| repo, | ||
| pull_number: targetPr, | ||
| }); | ||
| const labels = (pull.data.labels ?? []) | ||
| .map((label) => String(label?.name ?? "")); | ||
| const active = labels.filter((label) => supported.has(label)); | ||
| if ( | ||
| pull.data.state !== "open" || | ||
| pull.data.head?.sha !== eventHead || | ||
| active.length !== 1 || | ||
| active[0] !== eventLabel || | ||
| labels.some((label) => security.has(label)) | ||
| ) { | ||
| core.notice("Pull request is no longer eligible for a comment."); | ||
| return; | ||
| } | ||
| const files = await github.paginate( | ||
| github.rest.pulls.listFiles, | ||
| { owner, repo, pull_number: targetPr, per_page: 100 }, | ||
| ); | ||
| if (files.length === 0 || files.length > 100) { | ||
| return fail("Current changed-file evidence is incomplete."); | ||
| } | ||
| const versionFolders = new Set(); | ||
| const packageIdentifiers = new Set(); | ||
| for (const file of files) { | ||
| const path = String(file?.filename ?? ""); | ||
| const match = path.match( | ||
| /^manifests\/[0-9a-z]\/((?:[^/]+\/)+)([^/]+)\/[^/]+\.yaml$/, | ||
| ); | ||
| if (!match) { | ||
| return fail("Current files are outside one package version."); | ||
| } | ||
| versionFolders.add( | ||
| path.substring(0, path.lastIndexOf("/")), | ||
| ); | ||
| packageIdentifiers.add( | ||
| match[1].slice(0, -1).replaceAll("/", "."), | ||
| ); | ||
| } | ||
| if ( | ||
| versionFolders.size !== 1 || | ||
| packageIdentifiers.size !== 1 | ||
| ) { | ||
| return fail("Current files do not identify one package version."); | ||
| } | ||
| const packageIdentifier = [...packageIdentifiers][0]; | ||
| const classLabels = { | ||
| DEAD_URL: new Set([ | ||
| "Error-Installer-Availability", "Validation-404-Error", | ||
| ]), | ||
| MALFORMED_URL: new Set([ | ||
| "Validation-Open-Url-Failed", | ||
| ]), | ||
| WAIVER_REVIEW: new Set([ | ||
| "Validation-Agreement-Domain", "Validation-Domain", | ||
| "Validation-Forbidden-URL-Error", | ||
| "Validation-Unapproved-URL", | ||
| ]), | ||
| CDN_REDIRECT_REVIEW: new Set([ | ||
| "Validate-Domain-Installer", "Validation-Domains-Mismatch", | ||
| "Validation-Indirect-URL", | ||
| ]), | ||
| }; | ||
| if (!classLabels[classification].has(eventLabel)) { | ||
| return fail("The active label does not support this class."); | ||
| } | ||
| let manifestField = null; | ||
| if ( | ||
| classification === "WAIVER_REVIEW" || | ||
| classification === "CDN_REDIRECT_REVIEW" | ||
| ) { | ||
| const repository = await github.rest.repos.get({ | ||
| owner, | ||
| repo, | ||
| }); | ||
| const defaultBranch = String( | ||
| repository.data.default_branch ?? "", | ||
| ); | ||
| if ( | ||
| !defaultBranch || | ||
| pull.data.base?.repo?.full_name !== `${owner}/${repo}` || | ||
| pull.data.base?.ref !== defaultBranch | ||
| ) { | ||
| return fail("The pull request does not target the default branch."); | ||
| } | ||
| const inventoryResponse = await github.rest.repos.getContent({ | ||
| owner, | ||
| repo, | ||
| path: "Tools/ManualValidation/Autowaiver.csv", | ||
| ref: defaultBranch, | ||
| }); | ||
| const inventory = inventoryResponse.data; | ||
| if ( | ||
| Array.isArray(inventory) || | ||
| inventory.type !== "file" || | ||
| inventory.encoding !== "base64" || | ||
| !Number.isSafeInteger(inventory.size) || | ||
| inventory.size <= 0 || | ||
| inventory.size > 200000 | ||
| ) { | ||
| return fail("The current Autowaiver inventory is unavailable."); | ||
| } | ||
| let rows; | ||
| try { | ||
| rows = parseCsv( | ||
| Buffer.from(inventory.content, "base64").toString("utf8"), | ||
| ); | ||
| } catch { | ||
| return fail("The current Autowaiver inventory is invalid."); | ||
| } | ||
| const header = [ | ||
| "PackageIdentifier", "ManifestValue", "ManifestKey", | ||
| "RemoveLabel", | ||
| ]; | ||
| if ( | ||
| rows.length < 2 || | ||
| rows[0].length !== header.length || | ||
| rows[0].some((value, index) => value !== header[index]) || | ||
| rows.slice(1).some((row) => row.length !== header.length) | ||
| ) { | ||
| return fail("The current Autowaiver inventory shape is invalid."); | ||
| } | ||
| const expected = [ | ||
| packageIdentifier, hostname, eventLabel, | ||
| ].map((value) => value.toLowerCase()); | ||
| const matchingRows = rows.slice(1).filter((row) => | ||
| row[0].trim().toLowerCase() === expected[0] && | ||
| row[1].trim().toLowerCase() === expected[1] && | ||
| row[3].trim().toLowerCase() === expected[2] && | ||
| /^[A-Za-z][A-Za-z0-9]{0,63}(?:Url|URL)$/.test( | ||
| row[2].trim(), | ||
| ), | ||
| ); | ||
| if (matchingRows.length !== 1) { | ||
| return fail("No exact current Autowaiver tuple supports this review."); | ||
| } | ||
| manifestField = matchingRows[0][2].trim(); | ||
| } | ||
| const [ | ||
| finalPullResponse, | ||
| comments, | ||
| reviews, | ||
| reviewComments, | ||
| ] = await Promise.all([ | ||
| github.rest.pulls.get({ | ||
| owner, | ||
| repo, | ||
| pull_number: targetPr, | ||
| }), | ||
| github.paginate( | ||
| github.rest.issues.listComments, | ||
| { owner, repo, issue_number: targetPr, per_page: 100 }, | ||
| ), | ||
| github.paginate( | ||
| github.rest.pulls.listReviews, | ||
| { owner, repo, pull_number: targetPr, per_page: 100 }, | ||
| ), | ||
| github.paginate( | ||
| github.rest.pulls.listReviewComments, | ||
| { owner, repo, pull_number: targetPr, per_page: 100 }, | ||
| ), | ||
| ]); | ||
| const finalPull = finalPullResponse.data; | ||
| const finalLabels = (finalPull.labels ?? []) | ||
| .map((label) => String(label?.name ?? "")); | ||
| const finalActive = finalLabels.filter((label) => | ||
| supported.has(label), | ||
| ); | ||
| const duplicate = comments.some((comment) => | ||
| String(comment?.body ?? "").includes(footer) && | ||
| String(comment?.body ?? "").includes( | ||
| `Head SHA: \`${eventHead}\``, | ||
| ), | ||
| ); | ||
| const feedbackPattern = | ||
| /\b(?:URL|URI|domain|hostname|redirect|404|forbidden|waiv(?:e|er))\b/i; | ||
| const addressPattern = | ||
| /(?:https?:\/\/[^\s<>()]+|\b(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}\b)/i; | ||
| const isHumanFeedback = (item) => | ||
| item?.user?.type === "User" && | ||
| !String(item.user?.login ?? "").endsWith("[bot]") && | ||
| ( | ||
| feedbackPattern.test(String(item.body ?? "")) || | ||
| addressPattern.test(String(item.body ?? "")) | ||
| ); | ||
| const humanFeedback = | ||
| comments.some(isHumanFeedback) || | ||
| reviews.some( | ||
| (review) => | ||
| review.state !== "DISMISSED" && | ||
| isHumanFeedback(review), | ||
| ) || | ||
| reviewComments.some(isHumanFeedback); | ||
| if ( | ||
| finalPull.state !== "open" || | ||
| finalPull.head?.sha !== eventHead || | ||
| finalActive.length !== 1 || | ||
| finalActive[0] !== eventLabel || | ||
| finalLabels.some((label) => security.has(label)) || | ||
| finalLabels.some((label) => label.startsWith("Waived-")) || | ||
| humanFeedback || | ||
| duplicate | ||
| ) { | ||
| core.notice("Final pull request gate suppressed the comment."); | ||
| return; | ||
| } | ||
| // Keep this as the final evidence read before rendering and posting. | ||
| const checksResponse = await github.rest.checks.listForRef({ | ||
| owner, | ||
| repo, | ||
| ref: eventHead, | ||
| app_id: trustedAppId, | ||
| filter: "all", | ||
| per_page: 100, | ||
| }); | ||
| const checkRuns = checksResponse.data.check_runs ?? []; | ||
| if ( | ||
| (checksResponse.data.total_count ?? checkRuns.length) > | ||
| checkRuns.length | ||
| ) { | ||
| return fail("Fresh trusted Check evidence is incomplete."); | ||
| } | ||
| const trustedChecks = checkRuns.filter( | ||
| (check) => | ||
| check?.app?.id === trustedAppId && | ||
| check?.app?.slug === trustedAppSlug && | ||
| check.head_sha === eventHead, | ||
| ); | ||
| const operationPattern = new RegExp( | ||
| `^WinGetSvc-Validation-${targetPr}-([0-9]+)$`, | ||
| ); | ||
| const selectedSequence = operationPattern.exec(operationId); | ||
| const operationSequences = trustedChecks.map((check) => { | ||
| const match = operationPattern.exec( | ||
| String(check.external_id ?? "").trim(), | ||
| ); | ||
| return match ? BigInt(match[1]) : null; | ||
| }); | ||
| const completionCheck = trustedChecks | ||
| .filter( | ||
| (check) => | ||
| check.name === "10. Validation Completed" && | ||
| check.status === "completed", | ||
| ) | ||
| .sort( | ||
| (left, right) => | ||
| Date.parse(right.completed_at ?? "") - | ||
| Date.parse(left.completed_at ?? "") || | ||
| Number(right.id) - Number(left.id), | ||
| )[0]; | ||
| const completionPayload = | ||
| parseCompletionPayload(completionCheck); | ||
| const freshOperationId = String( | ||
| completionPayload?.OperationId ?? "", | ||
| ).trim(); | ||
| const completionTime = Date.parse( | ||
| completionCheck?.completed_at ?? "", | ||
| ); | ||
| const newerTrustedCheck = trustedChecks.some( | ||
| (check) => | ||
| check.id !== completionCheck?.id && | ||
| ( | ||
| Number(check.id) > Number(completionCheck?.id) || | ||
| Date.parse(check.started_at ?? "") > completionTime | ||
| ), | ||
| ); | ||
| if ( | ||
| !completionCheck || | ||
| !selectedSequence || | ||
| operationSequences.some( | ||
| (sequence) => | ||
| sequence === null || | ||
| sequence > BigInt(selectedSequence[1]), | ||
| ) || | ||
| completionPayload?.PullRequestNumber !== targetPr || | ||
| freshOperationId !== operationId || | ||
| String(completionCheck.external_id ?? "").trim() !== | ||
| operationId || | ||
| newerTrustedCheck | ||
| ) { | ||
| return fail("The sealed validation operation is no longer newest."); | ||
| } | ||
| const selectedChecks = trustedChecks.filter( | ||
| (check) => | ||
| check.status === "completed" && | ||
| check.name === checkName && | ||
| String(check.external_id ?? "").trim() === operationId, | ||
| ); | ||
| if (selectedChecks.length !== 1) { | ||
| return fail("The selected trusted Check is not unique."); | ||
| } | ||
| const selectedCheck = selectedChecks[0]; | ||
| if ( | ||
| !["neutral", "failure", "action_required"].includes( | ||
| String(selectedCheck.conclusion ?? "").toLowerCase(), | ||
| ) || | ||
| !evidence.checks.some( | ||
| (check) => | ||
| check?.id === selectedCheck.id && | ||
| check?.name === checkName && | ||
| check?.externalId === operationId, | ||
| ) | ||
| ) { | ||
| return fail("The selected Check is not bound to sealed evidence."); | ||
| } | ||
| const checkText = [ | ||
| selectedCheck.output?.title, | ||
| selectedCheck.output?.summary, | ||
| selectedCheck.output?.text, | ||
| ].map((value) => String(value ?? "")).join("\n"); | ||
| const lines = checkText | ||
| .split(/\r?\n/) | ||
| .map((line) => line.trim()) | ||
| .filter(Boolean); | ||
| const failedUriLines = lines.filter((line) => | ||
| /\bURI:\s*.*?,\s*Validation result:\s*Failed\b/i.test(line), | ||
| ); | ||
| const urlRecords = lines.map((line) => { | ||
| const match = line.match( | ||
| /\bURI:\s*(.*?),\s*Validation result:\s*([A-Za-z]+)(.*)$/i, | ||
| ); | ||
| const details = String(match?.[3] ?? ""); | ||
| const status = details.match( | ||
| /(?:^|,\s*)Http status code:\s*([^,\r\n]+)/i, | ||
| )?.[1]?.trim().toLowerCase() ?? ""; | ||
| const diagnostic = details.match( | ||
| /(?:^|,\s*)(?:Error Message|Exception|Error):\s*(.+)$/i, | ||
| )?.[1]?.trim() ?? ""; | ||
| const raw = String(match?.[1] ?? "").trim(); | ||
| return match | ||
| ? { | ||
| diagnostic, | ||
| hostname: hostnameFromUri(raw), | ||
| href: (() => { | ||
| try { | ||
| return new URL(raw).href; | ||
| } catch { | ||
| return null; | ||
| } | ||
| })(), | ||
| line, | ||
| raw, | ||
| result: match[2].toLowerCase(), | ||
| status, | ||
| } | ||
| : null; | ||
| }).filter(Boolean); | ||
| const failedRecords = urlRecords.filter( | ||
| (record) => | ||
| record.result === "failed" && | ||
| record.hostname, | ||
| ); | ||
| if ( | ||
| failedUriLines.length !== failedRecords.length || | ||
| failedUriLines.length !== | ||
| urlRecords.filter( | ||
| (record) => record.result === "failed", | ||
| ).length | ||
| ) { | ||
| return fail("A failed URI record is incomplete or unparseable."); | ||
| } | ||
| const deadRecords = failedRecords.filter( | ||
| (record) => | ||
| /^(?:404|not[\s_-]*found)$/.test(record.status), | ||
| ); | ||
| const malformedRecords = failedRecords.filter( | ||
| (record) => | ||
| /\b(?:(?:invalid|malformed)\s+(?:url|uri)|(?:url|uri)\s+(?:is\s+)?(?:invalid|malformed)|(?:url|uri)\s+format)\b/i.test( | ||
| record.diagnostic, | ||
| ), | ||
| ); | ||
| const forbiddenRecords = failedRecords.filter( | ||
| (record) => | ||
| /^(?:403|forbidden)$/.test(record.status), | ||
| ); | ||
| const domainHostnames = new Set(); | ||
| for (const line of lines) { | ||
| const match = line.match( | ||
| /(?:^|\b\d{2}:\d{2}:\d{2}Z\s+)-\s+([a-z0-9.-]+)\s*$/i, | ||
| ); | ||
| if (match) domainHostnames.add(match[1].toLowerCase()); | ||
| } | ||
| const oneHost = (records) => { | ||
| const hostnames = new Set( | ||
| records.map((record) => record.hostname), | ||
| ); | ||
| return hostnames.size === 1 && hostnames.has(hostname); | ||
| }; | ||
| const manualDomainReview = | ||
| domainHostnames.size === 1 && | ||
| domainHostnames.has(hostname) && | ||
| /installer URLs need to be validated/i.test(checkText) && | ||
| /needs to go to manual review/i.test(checkText); | ||
| const provenClasses = []; | ||
| if ( | ||
| failedRecords.length > 0 && | ||
| deadRecords.length === failedRecords.length && | ||
| oneHost(deadRecords) | ||
| ) { | ||
| provenClasses.push("DEAD_URL"); | ||
| } | ||
| if ( | ||
| failedRecords.length > 0 && | ||
| malformedRecords.length === failedRecords.length && | ||
| oneHost(malformedRecords) | ||
| ) { | ||
| provenClasses.push("MALFORMED_URL"); | ||
| } | ||
| if ( | ||
| ( | ||
| failedRecords.length > 0 && | ||
| forbiddenRecords.length === failedRecords.length && | ||
| oneHost(forbiddenRecords) | ||
| ) || | ||
| ( | ||
| failedRecords.length === 0 && | ||
| classification === "WAIVER_REVIEW" && | ||
| manualDomainReview | ||
| ) | ||
| ) { | ||
| provenClasses.push("WAIVER_REVIEW"); | ||
| } | ||
| if ( | ||
| failedRecords.length === 0 && | ||
| classification === "CDN_REDIRECT_REVIEW" && | ||
| manualDomainReview | ||
| ) { | ||
| provenClasses.push("CDN_REDIRECT_REVIEW"); | ||
| } | ||
| if ( | ||
| provenClasses.length !== 1 || | ||
| provenClasses[0] !== classification | ||
| ) { | ||
| return fail("Fresh Check evidence does not prove one class."); | ||
| } | ||
| const recommendation = | ||
| classification === "WAIVER_REVIEW" | ||
| ? "Wait for maintainer review of the exact approved inventory match; this workflow does not create or promise a waiver." | ||
| : classification === "CDN_REDIRECT_REVIEW" | ||
| ? "Wait for maintainer review of the exact approved redirect or domain inventory match; this workflow does not create or promise a waiver." | ||
| : "Replace or remove the invalid or unavailable URL using verified publisher-controlled information; if it is an InstallerUrl, regenerate InstallerSha256."; | ||
| const finding = | ||
| manifestField | ||
| ? `field **\`${manifestField}\`** on hostname **\`${hostname}\`**` | ||
| : `hostname **\`${hostname}\`**`; | ||
| const body = [ | ||
| "> [!WARNING]", | ||
| "> **Experimental automated suggestion - please verify before acting.**", | ||
| ">", | ||
| `> The trusted validation result reported **\`${classification}\`** for ${finding}.`, | ||
| ">", | ||
| `> **Suggested action:** ${recommendation}`, | ||
| ">", | ||
| "> <details><summary>Validation evidence</summary>", | ||
| ">", | ||
| `> Head SHA: \`${eventHead}\``, | ||
| ">", | ||
| `> Validation check: \`${checkName}\``, | ||
| ">", | ||
| "> </details>", | ||
| ].join("\n"); | ||
| const runUrl = | ||
| `${process.env.GITHUB_SERVER_URL}/` + | ||
| `${process.env.GITHUB_REPOSITORY}/actions/runs/` + | ||
| process.env.GITHUB_RUN_ID; | ||
| const finalBody = | ||
| `${body}\n\n${footer} by ` + | ||
| `[Domain Validation Assist](${runUrl})`; | ||
| if (finalBody.length > 3500) { | ||
| return fail("Final comment exceeds its size limit."); | ||
| } | ||
| await github.rest.issues.createComment({ | ||
| owner, | ||
| repo, | ||
| issue_number: targetPr, | ||
| body: finalBody, | ||
| }); | ||
| pre_activation: | ||
| if: > | ||
| github.event_name == 'pull_request_target' && github.event.action == 'labeled' && github.actor == 'wingetvalidator-prod[bot]' && github.event.pull_request.user.login != 'wingetbot' && contains( | ||
| fromJSON('["Error-Installer-Availability","Validate-Domain-Installer","Validation-404-Error","Validation-Agreement-Domain","Validation-Domain","Validation-Domains-Mismatch","Validation-Forbidden-URL-Error","Validation-Indirect-URL","Validation-Open-Url-Failed","Validation-Unapproved-URL"]'), | ||
| github.event.label.name | ||
| ) | ||
| 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@5e508589e03a7757a7e05b26e834292f5445bfb6 # v0.88.7 | ||
| with: | ||
| destination: ${{ runner.temp }}/gh-aw/actions | ||
| job-name: ${{ github.job }} | ||
| env: | ||
| GH_AW_SETUP_WORKFLOW_NAME: "Domain Validation Assist" | ||
| GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/domain-validation-assist.lock.yml@${{ github.ref }} | ||
| GH_AW_INFO_VERSION: "1.0.80" | ||
| GH_AW_INFO_AWF_VERSION: "v0.28.14" | ||
| 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" | ||
| GH_AW_ALLOWED_BOTS: "wingetvalidator-prod[bot]" | ||
| 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(); | ||