From ed0ad14c22fb0a1230cbdcc299e1cde9ce839683 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 16 Jun 2026 15:45:08 +0000 Subject: [PATCH 1/4] Initial plan From 598ca833042174418301012829fde98b1d6b8b1f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 16 Jun 2026 16:00:38 +0000 Subject: [PATCH 2/4] Plan update Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/objective-impact-report.lock.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/objective-impact-report.lock.yml b/.github/workflows/objective-impact-report.lock.yml index 9d6b1ddc080..08d79a242d5 100644 --- a/.github/workflows/objective-impact-report.lock.yml +++ b/.github/workflows/objective-impact-report.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"e357ecaf2b1bac669b156ce98fc34afb31faf80acf85b19f4b1cd9069ab7aca6","body_hash":"24d3f0499ec3d16953e9b5b48edfdc9b9887e87b9de4c92c2955b04698b04122","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.60"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"e357ecaf2b1bac669b156ce98fc34afb31faf80acf85b19f4b1cd9069ab7aca6","body_hash":"978c9220dcee75830e508fe03ceb61df8818787eac3aa9325c1624699a03a99c","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.60"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache","sha":"27d5ce7f107fe9357f9df03efb73ab90386fccae","version":"v5.0.5"},{"repo":"actions/cache/restore","sha":"27d5ce7f107fe9357f9df03efb73ab90386fccae","version":"v5.0.5"},{"repo":"actions/cache/save","sha":"27d5ce7f107fe9357f9df03efb73ab90386fccae","version":"v5.0.5"},{"repo":"actions/checkout","sha":"df4cb1c069e1874edd31b4311f1884172cec0e10","version":"v6.0.3"},{"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.2","digest":"sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.2@sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2","digest":"sha256:ee39841d980878ebbb87592903b06d31a1af500c71525c9616f7e8e2a27041a4","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2@sha256:ee39841d980878ebbb87592903b06d31a1af500c71525c9616f7e8e2a27041a4"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.2","digest":"sha256:02f3ec08f32dc26c5427920c6a2e2f3036238fce44802f2f11ef49ed8621b5d0","pinned_image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.2@sha256:02f3ec08f32dc26c5427920c6a2e2f3036238fce44802f2f11ef49ed8621b5d0"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.2","digest":"sha256:2e3a717e5f19a654cd9a2263beb52012b56bcb68562ec5ae2e42f9d156b49591","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.2@sha256:2e3a717e5f19a654cd9a2263beb52012b56bcb68562ec5ae2e42f9d156b49591"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.25","digest":"sha256:c10331ad17668ef89f38f5e356678788a40b0cd5fef96e8f92e1d9c1de47cbaa","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.25@sha256:c10331ad17668ef89f38f5e356678788a40b0cd5fef96e8f92e1d9c1de47cbaa"},{"image":"ghcr.io/github/github-mcp-server:v1.1.2","digest":"sha256:30197479d8036c7811892bc07e06f9a05c9ef3cdd79bc59f256d50647f95788c","pinned_image":"ghcr.io/github/github-mcp-server:v1.1.2@sha256:30197479d8036c7811892bc07e06f9a05c9ef3cdd79bc59f256d50647f95788c"}]} # 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 # From e345a07cce04c38c9f53aae27e1928debd952d96 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 16 Jun 2026 16:06:01 +0000 Subject: [PATCH 3/4] Fix centralized workflow_dispatch safe-output issue resolution Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- actions/setup/js/add_labels.cjs | 6 ++- actions/setup/js/add_labels.test.cjs | 34 ++++++++++++++ actions/setup/js/remove_labels.cjs | 6 ++- actions/setup/js/remove_labels.test.cjs | 34 ++++++++++++++ actions/setup/js/safe_output_helpers.cjs | 20 ++++---- actions/setup/js/safe_output_helpers.test.cjs | 46 +++++++++++++++++++ actions/setup/js/update_issue.test.cjs | 30 ++++++++++++ 7 files changed, 164 insertions(+), 12 deletions(-) diff --git a/actions/setup/js/add_labels.cjs b/actions/setup/js/add_labels.cjs index a2c4865c3f8..469ce2a56d7 100644 --- a/actions/setup/js/add_labels.cjs +++ b/actions/setup/js/add_labels.cjs @@ -32,6 +32,7 @@ const { attachExecutionState, fetchIssueState, normalizeLabelNames } = require(" const { MAX_LABELS } = require("./constants.cjs"); const { createCountGatedHandler } = require("./handler_scaffold.cjs"); const { withRetry, RATE_LIMIT_RETRY_CONFIG } = require("./error_recovery.cjs"); +const { resolveInvocationContext } = require("./invocation_context_helpers.cjs"); /** * Main handler factory for add_labels @@ -78,7 +79,8 @@ const main = createCountGatedHandler({ // Accept common aliases: issue_number, pr_number, and pull_number are normalised to item_number const targetResult = resolveSafeOutputIssueTarget({ message, resolvedTemporaryIds, repoParts, handlerType: HANDLER_TYPE }); if (!targetResult.success) return targetResult; - const itemNumber = targetResult.number ?? context.payload?.issue?.number ?? context.payload?.pull_request?.number; + const effectiveContext = resolveInvocationContext(context); + const itemNumber = targetResult.number ?? effectiveContext.eventPayload?.issue?.number ?? effectiveContext.eventPayload?.pull_request?.number; if (!itemNumber || Number.isNaN(Number(itemNumber))) { const error = "No issue/PR number available"; @@ -86,7 +88,7 @@ const main = createCountGatedHandler({ return { success: false, error }; } - const contextType = context.payload?.pull_request ? "pull request" : "issue"; + const contextType = effectiveContext.eventPayload?.pull_request ? "pull request" : "issue"; const requestedLabels = message.labels ?? []; core.info(`Requested labels: ${JSON.stringify(requestedLabels)}`); diff --git a/actions/setup/js/add_labels.test.cjs b/actions/setup/js/add_labels.test.cjs index 81529fba70e..fbd0dceec57 100644 --- a/actions/setup/js/add_labels.test.cjs +++ b/actions/setup/js/add_labels.test.cjs @@ -201,6 +201,40 @@ describe("add_labels", () => { expect(result.contextType).toBe("issue"); }); + it("should add labels from workflow_dispatch aw_context when issue payload is absent", async () => { + mockContext.eventName = "workflow_dispatch"; + mockContext.payload = { + inputs: { + aw_context: JSON.stringify({ + event_type: "issue_comment", + item_type: "issue", + item_number: 456, + repo: "test-owner/test-repo", + }), + }, + }; + + const handler = await main({ max: 10 }); + const addLabelsCalls = []; + + mockGithub.rest.issues.addLabels = async params => { + addLabelsCalls.push(params); + return {}; + }; + + const result = await handler( + { + labels: ["documentation"], + }, + {} + ); + + expect(result.success).toBe(true); + expect(result.number).toBe(456); + expect(result.contextType).toBe("issue"); + expect(addLabelsCalls[0].issue_number).toBe(456); + }); + it("should add labels to a pull request from context", async () => { mockContext.payload = { pull_request: { diff --git a/actions/setup/js/remove_labels.cjs b/actions/setup/js/remove_labels.cjs index 6b90769ebc7..494f4cf16e1 100644 --- a/actions/setup/js/remove_labels.cjs +++ b/actions/setup/js/remove_labels.cjs @@ -15,6 +15,7 @@ const { logStagedPreviewInfo } = require("./staged_preview.cjs"); const { createAuthenticatedGitHubClient } = require("./handler_auth.cjs"); const { resolveSafeOutputIssueTarget } = require("./temporary_id.cjs"); const { createCountGatedHandler } = require("./handler_scaffold.cjs"); +const { resolveInvocationContext } = require("./invocation_context_helpers.cjs"); /** * Main handler factory for remove_labels @@ -69,7 +70,8 @@ const main = createCountGatedHandler({ // Accept common aliases: issue_number, pr_number, and pull_number are normalised to item_number const targetResult = resolveSafeOutputIssueTarget({ message, resolvedTemporaryIds, repoParts, handlerType: HANDLER_TYPE }); if (!targetResult.success) return targetResult; - const itemNumber = targetResult.number ?? context.payload?.issue?.number ?? context.payload?.pull_request?.number; + const effectiveContext = resolveInvocationContext(context); + const itemNumber = targetResult.number ?? effectiveContext.eventPayload?.issue?.number ?? effectiveContext.eventPayload?.pull_request?.number; if (!itemNumber || Number.isNaN(Number(itemNumber))) { const error = "No issue/PR number available"; @@ -77,7 +79,7 @@ const main = createCountGatedHandler({ return { success: false, error }; } - const contextType = context.payload?.pull_request ? "pull request" : "issue"; + const contextType = effectiveContext.eventPayload?.pull_request ? "pull request" : "issue"; const requestedLabels = message.labels ?? []; core.info(`Requested labels to remove: ${JSON.stringify(requestedLabels)}`); diff --git a/actions/setup/js/remove_labels.test.cjs b/actions/setup/js/remove_labels.test.cjs index 6f4e0a455af..e5e32bd0a17 100644 --- a/actions/setup/js/remove_labels.test.cjs +++ b/actions/setup/js/remove_labels.test.cjs @@ -195,6 +195,40 @@ describe("remove_labels", () => { expect(result.contextType).toBe("issue"); }); + it("should remove labels from workflow_dispatch aw_context when issue payload is absent", async () => { + mockContext.eventName = "workflow_dispatch"; + mockContext.payload = { + inputs: { + aw_context: JSON.stringify({ + event_type: "issue_comment", + item_type: "issue", + item_number: 456, + repo: "test-owner/test-repo", + }), + }, + }; + + const handler = await main({ max: 10 }); + const removeLabelCalls = []; + + mockGithub.rest.issues.removeLabel = async params => { + removeLabelCalls.push(params); + return {}; + }; + + const result = await handler( + { + labels: ["documentation"], + }, + {} + ); + + expect(result.success).toBe(true); + expect(result.number).toBe(456); + expect(result.contextType).toBe("issue"); + expect(removeLabelCalls[0].issue_number).toBe(456); + }); + it("should remove labels from a pull request from context", async () => { mockContext.payload = { pull_request: { diff --git a/actions/setup/js/safe_output_helpers.cjs b/actions/setup/js/safe_output_helpers.cjs index 9e6f02b55c0..a7962521be0 100644 --- a/actions/setup/js/safe_output_helpers.cjs +++ b/actions/setup/js/safe_output_helpers.cjs @@ -9,6 +9,7 @@ const { getErrorMessage } = require("./error_helpers.cjs"); const { matchesSimpleGlob } = require("./glob_pattern_helpers.cjs"); const { logStagedPreviewInfo } = require("./staged_preview.cjs"); +const { resolveInvocationContext } = require("./invocation_context_helpers.cjs"); /** * Parse a comma-separated list of allowed items from environment variable @@ -70,12 +71,15 @@ function parseMaxCount(envValue, defaultValue = 3) { */ function resolveTarget(params) { const { targetConfig, item, context, itemType, supportsPR = false, supportsIssue = false } = params; + const invocationContext = resolveInvocationContext(context); + const effectiveEventName = invocationContext?.eventName || context.eventName; + const effectivePayload = invocationContext?.eventPayload || context.payload; // Check context type const prEventNames = new Set(["pull_request", "pull_request_target", "pull_request_review", "pull_request_review_comment"]); - const isIssueCommentOnPR = context.eventName === "issue_comment" && Boolean(context.payload?.issue?.pull_request); - const isIssueContext = context.eventName === "issues" || (context.eventName === "issue_comment" && !isIssueCommentOnPR); - const isPRContext = prEventNames.has(context.eventName) || isIssueCommentOnPR; + const isIssueCommentOnPR = effectiveEventName === "issue_comment" && Boolean(effectivePayload?.issue?.pull_request); + const isIssueContext = effectiveEventName === "issues" || (effectiveEventName === "issue_comment" && !isIssueCommentOnPR); + const isPRContext = prEventNames.has(effectiveEventName) || isIssueCommentOnPR; // Default target is "triggering" const target = targetConfig || "triggering"; @@ -202,8 +206,8 @@ function resolveTarget(params) { } else { // Use triggering context if (isIssueContext) { - if (context.payload.issue) { - itemNumber = context.payload.issue.number; + if (effectivePayload.issue) { + itemNumber = effectivePayload.issue.number; contextType = "issue"; } else { return { @@ -213,11 +217,11 @@ function resolveTarget(params) { }; } } else if (isPRContext) { - if (context.payload.pull_request) { - itemNumber = context.payload.pull_request.number; + if (effectivePayload.pull_request) { + itemNumber = effectivePayload.pull_request.number; contextType = "pull request"; } else if (isIssueCommentOnPR) { - itemNumber = context.payload.issue.number; + itemNumber = effectivePayload.issue.number; contextType = "pull request"; } else { return { diff --git a/actions/setup/js/safe_output_helpers.test.cjs b/actions/setup/js/safe_output_helpers.test.cjs index 281858cab3f..188a62eb56d 100644 --- a/actions/setup/js/safe_output_helpers.test.cjs +++ b/actions/setup/js/safe_output_helpers.test.cjs @@ -95,6 +95,29 @@ describe("safe_output_helpers", () => { expect(result.contextType).toBe("issue"); }); + it("should resolve workflow_dispatch issue context from aw_context", () => { + const result = helpers.resolveTarget({ + ...baseParams, + context: { + eventName: "workflow_dispatch", + repo: { owner: "test-owner", repo: "test-repo" }, + payload: { + inputs: { + aw_context: JSON.stringify({ + event_type: "issue_comment", + item_type: "issue", + item_number: 321, + repo: "test-owner/test-repo", + }), + }, + }, + }, + }); + expect(result.success).toBe(true); + expect(result.number).toBe(321); + expect(result.contextType).toBe("issue"); + }); + it("should resolve triggering PR context", () => { const result = helpers.resolveTarget({ ...baseParams, @@ -458,6 +481,29 @@ describe("safe_output_helpers", () => { expect(result.contextType).toBe("issue"); }); + it("should resolve workflow_dispatch issue context from aw_context", () => { + const result = helpers.resolveTarget({ + ...baseParams, + context: { + eventName: "workflow_dispatch", + repo: { owner: "test-owner", repo: "test-repo" }, + payload: { + inputs: { + aw_context: JSON.stringify({ + event_type: "issue_comment", + item_type: "issue", + item_number: 654, + repo: "test-owner/test-repo", + }), + }, + }, + }, + }); + expect(result.success).toBe(true); + expect(result.number).toBe(654); + expect(result.contextType).toBe("issue"); + }); + it("should fail when triggering and not in issue context", () => { const result = helpers.resolveTarget({ ...baseParams, diff --git a/actions/setup/js/update_issue.test.cjs b/actions/setup/js/update_issue.test.cjs index be92fd6ae4f..8c31111b8bb 100644 --- a/actions/setup/js/update_issue.test.cjs +++ b/actions/setup/js/update_issue.test.cjs @@ -778,6 +778,36 @@ describe("update_issue.cjs - cross-repo and operation integration", () => { expect(capturedRepo).toBe("other-repo"); }); + it("should resolve triggering issue from workflow_dispatch aw_context", async () => { + mockContext.eventName = "workflow_dispatch"; + mockContext.payload = { + inputs: { + aw_context: JSON.stringify({ + event_type: "issue_comment", + item_type: "issue", + item_number: 123, + repo: "testowner/testrepo", + }), + }, + }; + let capturedIssueNumber; + + mockGithub.rest.issues.get.mockResolvedValue({ + data: { number: 123, title: "Test", body: "Original", html_url: "https://github.com/testowner/testrepo/issues/123" }, + }); + mockGithub.rest.issues.update.mockImplementation(async ({ issue_number, body }) => { + capturedIssueNumber = issue_number; + return { data: { number: issue_number, title: "Test", body, html_url: `https://github.com/testowner/testrepo/issues/${issue_number}` } }; + }); + + const { main } = await import("./update_issue.cjs"); + const handler = await main(); + const result = await handler({ body: "Updated from centralized dispatch" }, {}); + + expect(result.success).toBe(true); + expect(capturedIssueNumber).toBe(123); + }); + it("should use current workflow repo in attribution URL for cross-repo updates", async () => { let capturedBody; From 4d2ebfba8231966404b03f49537c359a5de99768 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 16 Jun 2026 17:16:46 +0000 Subject: [PATCH 4/4] Fix resolveTarget try/catch and mockContext restore in test Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/agentics-maintenance.yml | 8 +- .../objective-impact-report.lock.yml | 76 +++++++------------ actions/setup/js/safe_output_helpers.cjs | 11 ++- actions/setup/js/update_issue.test.cjs | 4 + 4 files changed, 47 insertions(+), 52 deletions(-) diff --git a/.github/workflows/agentics-maintenance.yml b/.github/workflows/agentics-maintenance.yml index 962d24c8254..6dc884a6868 100644 --- a/.github/workflows/agentics-maintenance.yml +++ b/.github/workflows/agentics-maintenance.yml @@ -221,7 +221,9 @@ jobs: - name: Record outputs id: record - run: echo "operation=${{ inputs.operation }}" >> "$GITHUB_OUTPUT" + env: + GH_AW_OPERATION: ${{ inputs.operation }} + run: echo "operation=$GH_AW_OPERATION" >> "$GITHUB_OUTPUT" update_pull_request_branches: if: ${{ (github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call') && inputs.operation == 'update_pull_request_branches' && (!(github.event.repository.fork)) }} @@ -313,7 +315,9 @@ jobs: - name: Record outputs id: record - run: echo "run_url=${{ inputs.run_url }}" >> "$GITHUB_OUTPUT" + env: + GH_AW_RUN_URL: ${{ inputs.run_url }} + run: echo "run_url=$GH_AW_RUN_URL" >> "$GITHUB_OUTPUT" create_labels: if: ${{ (github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call') && inputs.operation == 'create_labels' && (!(github.event.repository.fork)) }} diff --git a/.github/workflows/objective-impact-report.lock.yml b/.github/workflows/objective-impact-report.lock.yml index 89546580fa1..85cb271b3b1 100644 --- a/.github/workflows/objective-impact-report.lock.yml +++ b/.github/workflows/objective-impact-report.lock.yml @@ -672,52 +672,13 @@ jobs: setupGlobals(core, github, context, exec, io, getOctokit); const { main } = require('${{ runner.temp }}/gh-aw/actions/generate_safe_outputs_tools.cjs'); await main(); - - name: Generate Safe Outputs MCP Server Config - id: safe-outputs-config - run: | - # Generate a secure random API key (360 bits of entropy, 40+ chars) - # Mask immediately to prevent timing vulnerabilities - API_KEY=$(openssl rand -base64 45 | tr -d '/+=') - echo "::add-mask::${API_KEY}" - - PORT=3001 - - # Set outputs for next steps - { - echo "safe_outputs_api_key=${API_KEY}" - echo "safe_outputs_port=${PORT}" - } >> "$GITHUB_OUTPUT" - - echo "Safe Outputs MCP server will run on port ${PORT}" - - - name: Start Safe Outputs MCP HTTP Server - id: safe-outputs-start - env: - DEBUG: '*' - GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} - GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }} - GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }} - GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ runner.temp }}/gh-aw/safeoutputs/tools.json - GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ runner.temp }}/gh-aw/safeoutputs/config.json - GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs - run: | - # Environment variables are set above to prevent template injection - export DEBUG - export GH_AW_SAFE_OUTPUTS - export GH_AW_SAFE_OUTPUTS_PORT - export GH_AW_SAFE_OUTPUTS_API_KEY - export GH_AW_SAFE_OUTPUTS_TOOLS_PATH - export GH_AW_SAFE_OUTPUTS_CONFIG_PATH - export GH_AW_MCP_LOG_DIR - - bash "${RUNNER_TEMP}/gh-aw/actions/start_safe_outputs_server.sh" - - name: Start MCP Gateway id: start-mcp-gateway env: GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} - GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }} - GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }} + 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 }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | set -eo pipefail mkdir -p "${RUNNER_TEMP}/gh-aw/mcp-config" @@ -744,18 +705,35 @@ jobs: * ) DOCKER_SOCK_PATH=/var/run/docker.sock ;; esac DOCKER_SOCK_GID=$(stat -c '%g' "$DOCKER_SOCK_PATH" 2>/dev/null || echo '0') - export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host --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 GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.3.25' + export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host --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 -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.3.25' 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_01ac58299f295f38_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" + cat << GH_AW_MCP_CONFIG_93f8eeeee7c97101_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" { "mcpServers": { "safeoutputs": { - "type": "http", - "url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT", - "headers": { - "Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}" + "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/mcp-logs/safeoutputs:/tmp/gh-aw/mcp-logs/safeoutputs:rw"], + "args": ["-w", "\${GITHUB_WORKSPACE}"], + "entrypoint": "sh", + "entrypointArgs": ["-c", "exec node ${GITHUB_WORKSPACE}/actions/setup/js/safe_outputs_mcp_server.cjs"], + "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}", + "GITHUB_REPOSITORY": "\${GITHUB_REPOSITORY}", + "GITHUB_SERVER_URL": "\${GITHUB_SERVER_URL}", + "GITHUB_TOKEN": "\${GITHUB_TOKEN}", + "GITHUB_WORKSPACE": "\${GITHUB_WORKSPACE}", + "RUNNER_TEMP": "\${RUNNER_TEMP}" }, "guard-policies": { "write-sink": { @@ -773,7 +751,7 @@ jobs: "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}" } } - GH_AW_MCP_CONFIG_01ac58299f295f38_EOF + GH_AW_MCP_CONFIG_93f8eeeee7c97101_EOF - name: Mount MCP servers as CLIs id: mount-mcp-clis continue-on-error: true diff --git a/actions/setup/js/safe_output_helpers.cjs b/actions/setup/js/safe_output_helpers.cjs index a7962521be0..c89ab62d2b9 100644 --- a/actions/setup/js/safe_output_helpers.cjs +++ b/actions/setup/js/safe_output_helpers.cjs @@ -71,7 +71,16 @@ function parseMaxCount(envValue, defaultValue = 3) { */ function resolveTarget(params) { const { targetConfig, item, context, itemType, supportsPR = false, supportsIssue = false } = params; - const invocationContext = resolveInvocationContext(context); + let invocationContext; + try { + invocationContext = resolveInvocationContext(context); + } catch (err) { + return { + success: false, + error: `Failed to resolve invocation context for ${itemType}: ${getErrorMessage(err)}`, + shouldFail: true, + }; + } const effectiveEventName = invocationContext?.eventName || context.eventName; const effectivePayload = invocationContext?.eventPayload || context.payload; diff --git a/actions/setup/js/update_issue.test.cjs b/actions/setup/js/update_issue.test.cjs index 8c31111b8bb..a5562b17f37 100644 --- a/actions/setup/js/update_issue.test.cjs +++ b/actions/setup/js/update_issue.test.cjs @@ -779,6 +779,8 @@ describe("update_issue.cjs - cross-repo and operation integration", () => { }); it("should resolve triggering issue from workflow_dispatch aw_context", async () => { + const savedEventName = mockContext.eventName; + const savedPayload = mockContext.payload; mockContext.eventName = "workflow_dispatch"; mockContext.payload = { inputs: { @@ -806,6 +808,8 @@ describe("update_issue.cjs - cross-repo and operation integration", () => { expect(result.success).toBe(true); expect(capturedIssueNumber).toBe(123); + mockContext.eventName = savedEventName; + mockContext.payload = savedPayload; }); it("should use current workflow repo in attribution URL for cross-repo updates", async () => {