diff --git a/.github/workflows/agentics-maintenance.yml b/.github/workflows/agentics-maintenance.yml index 2c1975dfab9..608a43249f0 100644 --- a/.github/workflows/agentics-maintenance.yml +++ b/.github/workflows/agentics-maintenance.yml @@ -23,13 +23,13 @@ # # This file defines the generated agentic maintenance workflow for this repository. # It runs scheduled cleanup for expiring safe outputs and supports manual maintenance operations. -# +# # This workflow is generated automatically when workflows use expiring safe outputs # or when repository maintenance features are enabled in .github/workflows/aw.json. -# +# # To disable maintenance workflow generation, set in .github/workflows/aw.json: # {"maintenance": false} -# +# # Agentic maintenance docs: # https://github.github.com/gh-aw/reference/ephemerals/#manual-maintenance-operations # diff --git a/.github/workflows/issue-monster.lock.yml b/.github/workflows/issue-monster.lock.yml index 3c7e98b88c0..4b700a3346e 100644 --- a/.github/workflows/issue-monster.lock.yml +++ b/.github/workflows/issue-monster.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"023c46ef7e81367dc4574b9c1021df1233ea90c865ab0f40ffa8475c5a522fc9","body_hash":"a3bf9a70bb50b41de2a9702fe982e186a89322f435a5a1633ab73ac76758e592","strict":true,"agent_id":"pi","agent_model":"copilot/gpt-5.4","engine_versions":{"pi":"0.79.9"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"fa30052f9aa0cf492f68e01c6439bf48d98ed213c570be0c496b8bc138e89cad","body_hash":"a3bf9a70bb50b41de2a9702fe982e186a89322f435a5a1633ab73ac76758e592","strict":true,"agent_id":"pi","agent_model":"copilot/gpt-5.4","engine_versions":{"pi":"0.79.9"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_AGENT_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"27d5ce7f107fe9357f9df03efb73ab90386fccae","version":"v5.0.5"},{"repo":"actions/cache/save","sha":"27d5ce7f107fe9357f9df03efb73ab90386fccae","version":"v5.0.5"},{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7","digest":"sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7@sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7","digest":"sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7@sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.7","digest":"sha256:4757f198a3fa20f88bdbe70be7ae1a05f127d9c0a9e96a5d6460ef40c08fc83d","pinned_image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.7@sha256:4757f198a3fa20f88bdbe70be7ae1a05f127d9c0a9e96a5d6460ef40c08fc83d"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7","digest":"sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7@sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.27","digest":"sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.27@sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.4.0","digest":"sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036","pinned_image":"ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036"}]} # This file was automatically generated by gh-aw. DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # @@ -650,20 +650,20 @@ jobs: run: | bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh" { - cat << 'GH_AW_PROMPT_fa1b36766ac003d3_EOF' + cat << 'GH_AW_PROMPT_b511cb2f2b072516_EOF' - GH_AW_PROMPT_fa1b36766ac003d3_EOF + GH_AW_PROMPT_b511cb2f2b072516_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md" cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md" cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md" cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md" - cat << 'GH_AW_PROMPT_fa1b36766ac003d3_EOF' + cat << 'GH_AW_PROMPT_b511cb2f2b072516_EOF' Tools: add_comment(max:3), assign_to_agent(max:3), missing_tool, missing_data, noop - GH_AW_PROMPT_fa1b36766ac003d3_EOF + GH_AW_PROMPT_b511cb2f2b072516_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md" - cat << 'GH_AW_PROMPT_fa1b36766ac003d3_EOF' + cat << 'GH_AW_PROMPT_b511cb2f2b072516_EOF' The following GitHub context information is available for this workflow: {{#if github.actor}} @@ -692,15 +692,15 @@ jobs: {{/if}} - GH_AW_PROMPT_fa1b36766ac003d3_EOF + GH_AW_PROMPT_b511cb2f2b072516_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/cli_proxy_with_safeoutputs_prompt.md" - cat << 'GH_AW_PROMPT_fa1b36766ac003d3_EOF' + cat << 'GH_AW_PROMPT_b511cb2f2b072516_EOF' {{#runtime-import .github/workflows/shared/github-guard-policy.md}} {{#runtime-import .github/workflows/shared/activation-app.md}} {{#runtime-import .github/workflows/shared/otlp.md}} {{#runtime-import .github/workflows/issue-monster.md}} - GH_AW_PROMPT_fa1b36766ac003d3_EOF + GH_AW_PROMPT_b511cb2f2b072516_EOF } > "$GH_AW_PROMPT" - name: Interpolate variables and render templates uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 @@ -928,9 +928,9 @@ jobs: mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs" mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs - cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_a8d626339be29201_EOF' - {"add_comment":{"max":3,"target":"*"},"assign_to_agent":{"allowed":["copilot"],"max":3,"target":"*"},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{}} - GH_AW_SAFE_OUTPUTS_CONFIG_a8d626339be29201_EOF + cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_373024bd152641ac_EOF' + {"add_comment":{"max":3,"target":"*"},"assign_to_agent":{"allowed":["copilot"],"ignore-if-error":true,"max":3,"target":"*"},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{}} + GH_AW_SAFE_OUTPUTS_CONFIG_373024bd152641ac_EOF - name: Generate Safe Outputs Tools env: GH_AW_TOOLS_META_JSON: | @@ -1108,7 +1108,7 @@ jobs: export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host --name awmg-mcpg --add-host host.docker.internal:127.0.0.1 --user '"${MCP_GATEWAY_UID}"':'"${MCP_GATEWAY_GID}"' --group-add '"${DOCKER_SOCK_GID}"' -v '"${DOCKER_SOCK_PATH}"':/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DOCKER_HOST=unix:///var/run/docker.sock -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_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 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 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.3.27' GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) - cat << GH_AW_MCP_CONFIG_317d62f563f888f6_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" + cat << GH_AW_MCP_CONFIG_5a62c05d6d2f1358_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" { "mcpServers": { "safeoutputs": { @@ -1153,7 +1153,7 @@ jobs: } } } - GH_AW_MCP_CONFIG_317d62f563f888f6_EOF + GH_AW_MCP_CONFIG_5a62c05d6d2f1358_EOF - name: Mount MCP servers as CLIs id: mount-mcp-clis continue-on-error: true @@ -2441,7 +2441,7 @@ jobs: GH_AW_ALLOWED_DOMAINS: "*.grafana.net,*.sentry.io,api.githubcopilot.com,api.pi.ai,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} - GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":3,\"target\":\"*\"},\"assign_to_agent\":{\"allowed\":[\"copilot\"],\"max\":3,\"target\":\"*\"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"report_incomplete\":{}}" + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":3,\"target\":\"*\"},\"assign_to_agent\":{\"allowed\":[\"copilot\"],\"ignore-if-error\":true,\"max\":3,\"target\":\"*\"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"report_incomplete\":{}}" GH_AW_ASSIGN_TO_AGENT_TOKEN: ${{ secrets.GH_AW_AGENT_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/issue-monster.md b/.github/workflows/issue-monster.md index c89e8fee01e..ac4fab9caf8 100644 --- a/.github/workflows/issue-monster.md +++ b/.github/workflows/issue-monster.md @@ -430,6 +430,7 @@ safe-outputs: max: 3 target: "*" # Requires explicit issue_number in agent output allowed: [copilot] # Only allow copilot agent + ignore-if-error: true # Don't fail the workflow if copilot is temporarily unavailable add-comment: max: 3 target: "*" diff --git a/actions/setup/js/assign_to_agent.cjs b/actions/setup/js/assign_to_agent.cjs index f903db34587..295b89b2339 100644 --- a/actions/setup/js/assign_to_agent.cjs +++ b/actions/setup/js/assign_to_agent.cjs @@ -87,7 +87,7 @@ async function main(config = {}) { if (configuredBaseBranch) core.info(`Configured base branch: ${configuredBaseBranch}`); core.info(`Target configuration: ${targetConfig}`); core.info(`Max count: ${maxCount}`); - if (ignoreIfError) core.info("Ignore-if-error mode enabled: Will not fail if agent assignment encounters auth errors"); + if (ignoreIfError) core.info("Ignore-if-error mode enabled: Will not fail if agent assignment encounters auth or availability errors"); if (allowedAgents) core.info(`Allowed agents: ${allowedAgents.join(", ")}`); core.info(`Default target repo: ${defaultTargetRepo}`); if (allowedRepos.size > 0) core.info(`Allowed repos: ${[...allowedRepos].join(", ")}`); @@ -393,15 +393,17 @@ async function main(config = {}) { let errorMessage = getErrorMessage(error); const isAuthError = ["Bad credentials", "Not Authenticated", "Resource not accessible", "Insufficient permissions", "requires authentication"].some(msg => errorMessage.includes(msg)); + const isAvailabilityError = errorMessage.includes("coding agent is not available for this repository"); - if (ignoreIfError && isAuthError) { - core.warning(`Agent assignment failed for ${agentName} on ${type} #${number} due to authentication/permission error. Skipping due to ignore-if-error=true.`); + if (ignoreIfError && (isAuthError || isAvailabilityError)) { + const errorType = isAuthError ? "authentication/permission" : "agent availability"; + core.warning(`Agent assignment failed for ${agentName} on ${type} #${number} due to ${errorType} error. Skipping due to ignore-if-error=true.`); core.info(`Error details: ${errorMessage}`); _allResults.push({ issue_number: issueNumber, pull_number: pullNumber, agent: agentName, owner: effectiveOwner, repo: effectiveRepo, pull_request_repo: effectivePullRequestRepoSlug, success: true, skipped: true }); return { success: true, skipped: true }; } - if (errorMessage.includes("coding agent is not available for this repository")) { + if (isAvailabilityError) { try { const available = await getAvailableAgentLogins(effectiveOwner, effectiveRepo, githubClient); if (available.length > 0) errorMessage += ` (available agents: ${available.join(", ")})`; diff --git a/actions/setup/js/assign_to_agent.test.cjs b/actions/setup/js/assign_to_agent.test.cjs index 5c02e2a4fde..fffbe254250 100644 --- a/actions/setup/js/assign_to_agent.test.cjs +++ b/actions/setup/js/assign_to_agent.test.cjs @@ -1032,7 +1032,7 @@ describe("assign_to_agent", () => { await eval(`(async () => { ${assignToAgentScript}; ${STANDALONE_RUNNER} })()`); // Should log that ignore-if-error is enabled - expect(mockCore.info).toHaveBeenCalledWith(expect.stringContaining("Ignore-if-error mode enabled: Will not fail if agent assignment encounters auth errors")); + expect(mockCore.info).toHaveBeenCalledWith(expect.stringContaining("Ignore-if-error mode enabled: Will not fail if agent assignment encounters auth or availability errors")); // Should warn about skipping but not fail expect(mockCore.warning).toHaveBeenCalledWith(expect.stringContaining("Agent assignment failed")); @@ -1109,8 +1109,41 @@ describe("assign_to_agent", () => { expect(mockCore.setFailed).not.toHaveBeenCalled(); }); + it("should skip assignment and not fail when ignore-if-error is true and agent is not available for the repository", async () => { + process.env.GH_AW_AGENT_IGNORE_IF_ERROR = "true"; + setAgentOutput({ + items: [ + { + type: "assign_to_agent", + issue_number: 42, + agent: "copilot", + }, + ], + errors: [], + }); + + // Simulate agent not available (checkUserCanBeAssigned returns 404) + const notFoundError = new Error("Not Found"); + notFoundError.status = 404; + mockGithub.rest.issues.checkUserCanBeAssigned.mockRejectedValueOnce(notFoundError); + // getAvailableAgentLogins also calls checkUserCanBeAssigned - return 404 for that too + mockGithub.rest.issues.checkUserCanBeAssigned.mockRejectedValue(notFoundError); + + await eval(`(async () => { ${assignToAgentScript}; ${STANDALONE_RUNNER} })()`); + + // Should warn about skipping due to agent availability error, but not fail + expect(mockCore.warning).toHaveBeenCalledWith(expect.stringContaining("agent availability")); + expect(mockCore.warning).toHaveBeenCalledWith(expect.stringContaining("ignore-if-error=true")); + expect(mockCore.setFailed).not.toHaveBeenCalled(); + + // Summary should show skipped assignments + expect(mockCore.summary.addRaw).toHaveBeenCalled(); + const summaryCall = mockCore.summary.addRaw.mock.calls[0][0]; + expect(summaryCall).toContain("⏭️ Skipped"); + }); + it("should still fail on non-auth errors even with ignore-if-error enabled", async () => { - process.env.GH_AW_AGENT_IGNORE_IF_MISSING = "true"; + process.env.GH_AW_AGENT_IGNORE_IF_ERROR = "true"; setAgentOutput({ items: [ { @@ -1122,9 +1155,9 @@ describe("assign_to_agent", () => { errors: [], }); - // Simulate a different error (not auth-related) + // Simulate a different error (not auth-related) during assignment const otherError = new Error("Network timeout"); - mockGithub.rest.issues.checkUserCanBeAssigned.mockRejectedValue(otherError); + mockGithub.request.mockRejectedValue(otherError); await eval(`(async () => { ${assignToAgentScript}; ${STANDALONE_RUNNER} })()`);