From 59599c5b78752ed39a12dd3b9d81984917a6a640 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 13 Oct 2025 22:22:49 +0000 Subject: [PATCH 1/9] Initial plan From 34b2591b4b55420028678c6051d881261b970dff Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 13 Oct 2025 22:34:38 +0000 Subject: [PATCH 2/9] Update Copilot CLI version to 0.0.340 and implement ${} syntax for env vars Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/artifacts-summary.lock.yml | 8 ++++---- .github/workflows/brave.lock.yml | 8 ++++---- .github/workflows/ci-doctor.lock.yml | 8 ++++---- .github/workflows/daily-news.lock.yml | 8 ++++---- .github/workflows/pdf-summary.lock.yml | 8 ++++---- .github/workflows/plan.lock.yml | 8 ++++---- .github/workflows/poem-bot.lock.yml | 8 ++++---- .github/workflows/q.lock.yml | 8 ++++---- .github/workflows/repo-tree-map.lock.yml | 8 ++++---- .github/workflows/scout.lock.yml | 8 ++++---- .github/workflows/smoke-copilot.lock.yml | 8 ++++---- .github/workflows/tidy.lock.yml | 8 ++++---- pkg/constants/constants.go | 2 +- pkg/workflow/copilot_engine.go | 4 ++-- 14 files changed, 51 insertions(+), 51 deletions(-) diff --git a/.github/workflows/artifacts-summary.lock.yml b/.github/workflows/artifacts-summary.lock.yml index 9aba5d52b50..65b0ab1d6eb 100644 --- a/.github/workflows/artifacts-summary.lock.yml +++ b/.github/workflows/artifacts-summary.lock.yml @@ -195,7 +195,7 @@ jobs: with: node-version: '24' - name: Install GitHub Copilot CLI - run: npm install -g @github/copilot@0.0.339 + run: npm install -g @github/copilot@0.0.340 - name: Setup Safe Outputs Collector MCP run: | mkdir -p /tmp/gh-aw/safe-outputs @@ -957,8 +957,8 @@ jobs: "args": ["/tmp/gh-aw/safe-outputs/mcp-server.cjs"], "tools": ["*"], "env": { - "GITHUB_AW_SAFE_OUTPUTS": "${{ env.GITHUB_AW_SAFE_OUTPUTS }}", - "GITHUB_AW_SAFE_OUTPUTS_CONFIG": ${{ toJSON(env.GITHUB_AW_SAFE_OUTPUTS_CONFIG) }} + "GITHUB_AW_SAFE_OUTPUTS": "\${GITHUB_AW_SAFE_OUTPUTS}", + "GITHUB_AW_SAFE_OUTPUTS_CONFIG": "\${GITHUB_AW_SAFE_OUTPUTS_CONFIG}" } } } @@ -3174,7 +3174,7 @@ jobs: with: node-version: '24' - name: Install GitHub Copilot CLI - run: npm install -g @github/copilot@0.0.339 + run: npm install -g @github/copilot@0.0.340 - name: Execute GitHub Copilot CLI id: agentic_execution # Copilot CLI tool arguments (sorted): diff --git a/.github/workflows/brave.lock.yml b/.github/workflows/brave.lock.yml index 06a2b019475..d050417317d 100644 --- a/.github/workflows/brave.lock.yml +++ b/.github/workflows/brave.lock.yml @@ -673,7 +673,7 @@ jobs: with: node-version: '24' - name: Install GitHub Copilot CLI - run: npm install -g @github/copilot@0.0.339 + run: npm install -g @github/copilot@0.0.340 - name: Setup Proxy Configuration for MCP Network Restrictions run: | echo "Generating proxy configuration files for MCP tools with network restrictions..." @@ -1584,8 +1584,8 @@ jobs: "args": ["/tmp/gh-aw/safe-outputs/mcp-server.cjs"], "tools": ["*"], "env": { - "GITHUB_AW_SAFE_OUTPUTS": "${{ env.GITHUB_AW_SAFE_OUTPUTS }}", - "GITHUB_AW_SAFE_OUTPUTS_CONFIG": ${{ toJSON(env.GITHUB_AW_SAFE_OUTPUTS_CONFIG) }} + "GITHUB_AW_SAFE_OUTPUTS": "\${GITHUB_AW_SAFE_OUTPUTS}", + "GITHUB_AW_SAFE_OUTPUTS_CONFIG": "\${GITHUB_AW_SAFE_OUTPUTS_CONFIG}" } } } @@ -3892,7 +3892,7 @@ jobs: with: node-version: '24' - name: Install GitHub Copilot CLI - run: npm install -g @github/copilot@0.0.339 + run: npm install -g @github/copilot@0.0.340 - name: Execute GitHub Copilot CLI id: agentic_execution # Copilot CLI tool arguments (sorted): diff --git a/.github/workflows/ci-doctor.lock.yml b/.github/workflows/ci-doctor.lock.yml index fdfe436bba6..87ec6c4ec90 100644 --- a/.github/workflows/ci-doctor.lock.yml +++ b/.github/workflows/ci-doctor.lock.yml @@ -166,7 +166,7 @@ jobs: with: node-version: '24' - name: Install GitHub Copilot CLI - run: npm install -g @github/copilot@0.0.339 + run: npm install -g @github/copilot@0.0.340 - name: Setup Safe Outputs Collector MCP run: | mkdir -p /tmp/gh-aw/safe-outputs @@ -928,8 +928,8 @@ jobs: "args": ["/tmp/gh-aw/safe-outputs/mcp-server.cjs"], "tools": ["*"], "env": { - "GITHUB_AW_SAFE_OUTPUTS": "${{ env.GITHUB_AW_SAFE_OUTPUTS }}", - "GITHUB_AW_SAFE_OUTPUTS_CONFIG": ${{ toJSON(env.GITHUB_AW_SAFE_OUTPUTS_CONFIG) }} + "GITHUB_AW_SAFE_OUTPUTS": "\${GITHUB_AW_SAFE_OUTPUTS}", + "GITHUB_AW_SAFE_OUTPUTS_CONFIG": "\${GITHUB_AW_SAFE_OUTPUTS_CONFIG}" } }, "web-fetch": { @@ -3286,7 +3286,7 @@ jobs: with: node-version: '24' - name: Install GitHub Copilot CLI - run: npm install -g @github/copilot@0.0.339 + run: npm install -g @github/copilot@0.0.340 - name: Execute GitHub Copilot CLI id: agentic_execution # Copilot CLI tool arguments (sorted): diff --git a/.github/workflows/daily-news.lock.yml b/.github/workflows/daily-news.lock.yml index 2cde501378c..19f18a9dcf0 100644 --- a/.github/workflows/daily-news.lock.yml +++ b/.github/workflows/daily-news.lock.yml @@ -197,7 +197,7 @@ jobs: with: node-version: '24' - name: Install GitHub Copilot CLI - run: npm install -g @github/copilot@0.0.339 + run: npm install -g @github/copilot@0.0.340 - name: Setup Safe Outputs Collector MCP run: | mkdir -p /tmp/gh-aw/safe-outputs @@ -959,8 +959,8 @@ jobs: "args": ["/tmp/gh-aw/safe-outputs/mcp-server.cjs"], "tools": ["*"], "env": { - "GITHUB_AW_SAFE_OUTPUTS": "${{ env.GITHUB_AW_SAFE_OUTPUTS }}", - "GITHUB_AW_SAFE_OUTPUTS_CONFIG": ${{ toJSON(env.GITHUB_AW_SAFE_OUTPUTS_CONFIG) }} + "GITHUB_AW_SAFE_OUTPUTS": "\${GITHUB_AW_SAFE_OUTPUTS}", + "GITHUB_AW_SAFE_OUTPUTS_CONFIG": "\${GITHUB_AW_SAFE_OUTPUTS_CONFIG}" } }, "tavily": { @@ -3181,7 +3181,7 @@ jobs: with: node-version: '24' - name: Install GitHub Copilot CLI - run: npm install -g @github/copilot@0.0.339 + run: npm install -g @github/copilot@0.0.340 - name: Execute GitHub Copilot CLI id: agentic_execution # Copilot CLI tool arguments (sorted): diff --git a/.github/workflows/pdf-summary.lock.yml b/.github/workflows/pdf-summary.lock.yml index 0c0d64b39e3..7b750d1ae19 100644 --- a/.github/workflows/pdf-summary.lock.yml +++ b/.github/workflows/pdf-summary.lock.yml @@ -718,7 +718,7 @@ jobs: with: node-version: '24' - name: Install GitHub Copilot CLI - run: npm install -g @github/copilot@0.0.339 + run: npm install -g @github/copilot@0.0.340 - name: Setup Safe Outputs Collector MCP run: | mkdir -p /tmp/gh-aw/safe-outputs @@ -1493,8 +1493,8 @@ jobs: "args": ["/tmp/gh-aw/safe-outputs/mcp-server.cjs"], "tools": ["*"], "env": { - "GITHUB_AW_SAFE_OUTPUTS": "${{ env.GITHUB_AW_SAFE_OUTPUTS }}", - "GITHUB_AW_SAFE_OUTPUTS_CONFIG": ${{ toJSON(env.GITHUB_AW_SAFE_OUTPUTS_CONFIG) }} + "GITHUB_AW_SAFE_OUTPUTS": "\${GITHUB_AW_SAFE_OUTPUTS}", + "GITHUB_AW_SAFE_OUTPUTS_CONFIG": "\${GITHUB_AW_SAFE_OUTPUTS_CONFIG}" } } } @@ -3851,7 +3851,7 @@ jobs: with: node-version: '24' - name: Install GitHub Copilot CLI - run: npm install -g @github/copilot@0.0.339 + run: npm install -g @github/copilot@0.0.340 - name: Execute GitHub Copilot CLI id: agentic_execution # Copilot CLI tool arguments (sorted): diff --git a/.github/workflows/plan.lock.yml b/.github/workflows/plan.lock.yml index c6aca8b9e5c..ef5df8dfb7f 100644 --- a/.github/workflows/plan.lock.yml +++ b/.github/workflows/plan.lock.yml @@ -673,7 +673,7 @@ jobs: with: node-version: '24' - name: Install GitHub Copilot CLI - run: npm install -g @github/copilot@0.0.339 + run: npm install -g @github/copilot@0.0.340 - name: Setup Safe Outputs Collector MCP run: | mkdir -p /tmp/gh-aw/safe-outputs @@ -1435,8 +1435,8 @@ jobs: "args": ["/tmp/gh-aw/safe-outputs/mcp-server.cjs"], "tools": ["*"], "env": { - "GITHUB_AW_SAFE_OUTPUTS": "${{ env.GITHUB_AW_SAFE_OUTPUTS }}", - "GITHUB_AW_SAFE_OUTPUTS_CONFIG": ${{ toJSON(env.GITHUB_AW_SAFE_OUTPUTS_CONFIG) }} + "GITHUB_AW_SAFE_OUTPUTS": "\${GITHUB_AW_SAFE_OUTPUTS}", + "GITHUB_AW_SAFE_OUTPUTS_CONFIG": "\${GITHUB_AW_SAFE_OUTPUTS_CONFIG}" } } } @@ -3734,7 +3734,7 @@ jobs: with: node-version: '24' - name: Install GitHub Copilot CLI - run: npm install -g @github/copilot@0.0.339 + run: npm install -g @github/copilot@0.0.340 - name: Execute GitHub Copilot CLI id: agentic_execution # Copilot CLI tool arguments (sorted): diff --git a/.github/workflows/poem-bot.lock.yml b/.github/workflows/poem-bot.lock.yml index 952465283eb..12446424f95 100644 --- a/.github/workflows/poem-bot.lock.yml +++ b/.github/workflows/poem-bot.lock.yml @@ -703,7 +703,7 @@ jobs: with: node-version: '24' - name: Install GitHub Copilot CLI - run: npm install -g @github/copilot@0.0.339 + run: npm install -g @github/copilot@0.0.340 - name: Setup Safe Outputs Collector MCP run: | mkdir -p /tmp/gh-aw/safe-outputs @@ -1469,8 +1469,8 @@ jobs: "args": ["/tmp/gh-aw/safe-outputs/mcp-server.cjs"], "tools": ["*"], "env": { - "GITHUB_AW_SAFE_OUTPUTS": "${{ env.GITHUB_AW_SAFE_OUTPUTS }}", - "GITHUB_AW_SAFE_OUTPUTS_CONFIG": ${{ toJSON(env.GITHUB_AW_SAFE_OUTPUTS_CONFIG) }} + "GITHUB_AW_SAFE_OUTPUTS": "\${GITHUB_AW_SAFE_OUTPUTS}", + "GITHUB_AW_SAFE_OUTPUTS_CONFIG": "\${GITHUB_AW_SAFE_OUTPUTS_CONFIG}" } } } @@ -3922,7 +3922,7 @@ jobs: with: node-version: '24' - name: Install GitHub Copilot CLI - run: npm install -g @github/copilot@0.0.339 + run: npm install -g @github/copilot@0.0.340 - name: Execute GitHub Copilot CLI id: agentic_execution # Copilot CLI tool arguments (sorted): diff --git a/.github/workflows/q.lock.yml b/.github/workflows/q.lock.yml index a9c096fb6eb..3b6663cd84c 100644 --- a/.github/workflows/q.lock.yml +++ b/.github/workflows/q.lock.yml @@ -762,7 +762,7 @@ jobs: with: node-version: '24' - name: Install GitHub Copilot CLI - run: npm install -g @github/copilot@0.0.339 + run: npm install -g @github/copilot@0.0.340 - name: Setup Safe Outputs Collector MCP run: | mkdir -p /tmp/gh-aw/safe-outputs @@ -1531,8 +1531,8 @@ jobs: "args": ["/tmp/gh-aw/safe-outputs/mcp-server.cjs"], "tools": ["*"], "env": { - "GITHUB_AW_SAFE_OUTPUTS": "${{ env.GITHUB_AW_SAFE_OUTPUTS }}", - "GITHUB_AW_SAFE_OUTPUTS_CONFIG": ${{ toJSON(env.GITHUB_AW_SAFE_OUTPUTS_CONFIG) }} + "GITHUB_AW_SAFE_OUTPUTS": "\${GITHUB_AW_SAFE_OUTPUTS}", + "GITHUB_AW_SAFE_OUTPUTS_CONFIG": "\${GITHUB_AW_SAFE_OUTPUTS_CONFIG}" } }, "serena": { @@ -4210,7 +4210,7 @@ jobs: with: node-version: '24' - name: Install GitHub Copilot CLI - run: npm install -g @github/copilot@0.0.339 + run: npm install -g @github/copilot@0.0.340 - name: Execute GitHub Copilot CLI id: agentic_execution # Copilot CLI tool arguments (sorted): diff --git a/.github/workflows/repo-tree-map.lock.yml b/.github/workflows/repo-tree-map.lock.yml index 5a827ed057d..05ff91a6b69 100644 --- a/.github/workflows/repo-tree-map.lock.yml +++ b/.github/workflows/repo-tree-map.lock.yml @@ -193,7 +193,7 @@ jobs: with: node-version: '24' - name: Install GitHub Copilot CLI - run: npm install -g @github/copilot@0.0.339 + run: npm install -g @github/copilot@0.0.340 - name: Setup Safe Outputs Collector MCP run: | mkdir -p /tmp/gh-aw/safe-outputs @@ -955,8 +955,8 @@ jobs: "args": ["/tmp/gh-aw/safe-outputs/mcp-server.cjs"], "tools": ["*"], "env": { - "GITHUB_AW_SAFE_OUTPUTS": "${{ env.GITHUB_AW_SAFE_OUTPUTS }}", - "GITHUB_AW_SAFE_OUTPUTS_CONFIG": ${{ toJSON(env.GITHUB_AW_SAFE_OUTPUTS_CONFIG) }} + "GITHUB_AW_SAFE_OUTPUTS": "\${GITHUB_AW_SAFE_OUTPUTS}", + "GITHUB_AW_SAFE_OUTPUTS_CONFIG": "\${GITHUB_AW_SAFE_OUTPUTS_CONFIG}" } } } @@ -3231,7 +3231,7 @@ jobs: with: node-version: '24' - name: Install GitHub Copilot CLI - run: npm install -g @github/copilot@0.0.339 + run: npm install -g @github/copilot@0.0.340 - name: Execute GitHub Copilot CLI id: agentic_execution # Copilot CLI tool arguments (sorted): diff --git a/.github/workflows/scout.lock.yml b/.github/workflows/scout.lock.yml index 6960bb8c5ef..a0c0026173a 100644 --- a/.github/workflows/scout.lock.yml +++ b/.github/workflows/scout.lock.yml @@ -751,7 +751,7 @@ jobs: with: node-version: '24' - name: Install GitHub Copilot CLI - run: npm install -g @github/copilot@0.0.339 + run: npm install -g @github/copilot@0.0.340 - name: Setup Proxy Configuration for MCP Network Restrictions run: | echo "Generating proxy configuration files for MCP tools with network restrictions..." @@ -1812,8 +1812,8 @@ jobs: "args": ["/tmp/gh-aw/safe-outputs/mcp-server.cjs"], "tools": ["*"], "env": { - "GITHUB_AW_SAFE_OUTPUTS": "${{ env.GITHUB_AW_SAFE_OUTPUTS }}", - "GITHUB_AW_SAFE_OUTPUTS_CONFIG": ${{ toJSON(env.GITHUB_AW_SAFE_OUTPUTS_CONFIG) }} + "GITHUB_AW_SAFE_OUTPUTS": "\${GITHUB_AW_SAFE_OUTPUTS}", + "GITHUB_AW_SAFE_OUTPUTS_CONFIG": "\${GITHUB_AW_SAFE_OUTPUTS_CONFIG}" } }, "tavily": { @@ -4201,7 +4201,7 @@ jobs: with: node-version: '24' - name: Install GitHub Copilot CLI - run: npm install -g @github/copilot@0.0.339 + run: npm install -g @github/copilot@0.0.340 - name: Execute GitHub Copilot CLI id: agentic_execution # Copilot CLI tool arguments (sorted): diff --git a/.github/workflows/smoke-copilot.lock.yml b/.github/workflows/smoke-copilot.lock.yml index b01e9189d89..81c5278b464 100644 --- a/.github/workflows/smoke-copilot.lock.yml +++ b/.github/workflows/smoke-copilot.lock.yml @@ -191,7 +191,7 @@ jobs: with: node-version: '24' - name: Install GitHub Copilot CLI - run: npm install -g @github/copilot@0.0.339 + run: npm install -g @github/copilot@0.0.340 - name: Setup Safe Outputs Collector MCP run: | mkdir -p /tmp/gh-aw/safe-outputs @@ -953,8 +953,8 @@ jobs: "args": ["/tmp/gh-aw/safe-outputs/mcp-server.cjs"], "tools": ["*"], "env": { - "GITHUB_AW_SAFE_OUTPUTS": "${{ env.GITHUB_AW_SAFE_OUTPUTS }}", - "GITHUB_AW_SAFE_OUTPUTS_CONFIG": ${{ toJSON(env.GITHUB_AW_SAFE_OUTPUTS_CONFIG) }} + "GITHUB_AW_SAFE_OUTPUTS": "\${GITHUB_AW_SAFE_OUTPUTS}", + "GITHUB_AW_SAFE_OUTPUTS_CONFIG": "\${GITHUB_AW_SAFE_OUTPUTS_CONFIG}" } } } @@ -3120,7 +3120,7 @@ jobs: with: node-version: '24' - name: Install GitHub Copilot CLI - run: npm install -g @github/copilot@0.0.339 + run: npm install -g @github/copilot@0.0.340 - name: Execute GitHub Copilot CLI id: agentic_execution # Copilot CLI tool arguments (sorted): diff --git a/.github/workflows/tidy.lock.yml b/.github/workflows/tidy.lock.yml index 2d796889934..a7b79797fe2 100644 --- a/.github/workflows/tidy.lock.yml +++ b/.github/workflows/tidy.lock.yml @@ -534,7 +534,7 @@ jobs: with: node-version: '24' - name: Install GitHub Copilot CLI - run: npm install -g @github/copilot@0.0.339 + run: npm install -g @github/copilot@0.0.340 - name: Setup Safe Outputs Collector MCP run: | mkdir -p /tmp/gh-aw/safe-outputs @@ -1296,8 +1296,8 @@ jobs: "args": ["/tmp/gh-aw/safe-outputs/mcp-server.cjs"], "tools": ["*"], "env": { - "GITHUB_AW_SAFE_OUTPUTS": "${{ env.GITHUB_AW_SAFE_OUTPUTS }}", - "GITHUB_AW_SAFE_OUTPUTS_CONFIG": ${{ toJSON(env.GITHUB_AW_SAFE_OUTPUTS_CONFIG) }} + "GITHUB_AW_SAFE_OUTPUTS": "\${GITHUB_AW_SAFE_OUTPUTS}", + "GITHUB_AW_SAFE_OUTPUTS_CONFIG": "\${GITHUB_AW_SAFE_OUTPUTS_CONFIG}" } } } @@ -3673,7 +3673,7 @@ jobs: with: node-version: '24' - name: Install GitHub Copilot CLI - run: npm install -g @github/copilot@0.0.339 + run: npm install -g @github/copilot@0.0.340 - name: Execute GitHub Copilot CLI id: agentic_execution # Copilot CLI tool arguments (sorted): diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go index ae07e8d07f8..8ede08c5ec3 100644 --- a/pkg/constants/constants.go +++ b/pkg/constants/constants.go @@ -18,7 +18,7 @@ const DefaultMCPRegistryURL = "https://api.mcp.github.com/v0" const DefaultClaudeCodeVersion = "2.0.14" // DefaultCopilotVersion is the default version of the GitHub Copilot CLI -const DefaultCopilotVersion = "0.0.339" +const DefaultCopilotVersion = "0.0.340" // DefaultCodexVersion is the default version of the OpenAI Codex CLI const DefaultCodexVersion = "0.46.0" diff --git a/pkg/workflow/copilot_engine.go b/pkg/workflow/copilot_engine.go index 36fec9c82a6..0a76e879896 100644 --- a/pkg/workflow/copilot_engine.go +++ b/pkg/workflow/copilot_engine.go @@ -395,8 +395,8 @@ func (e *CopilotEngine) renderSafeOutputsCopilotMCPConfig(yaml *strings.Builder, yaml.WriteString(" \"args\": [\"/tmp/gh-aw/safe-outputs/mcp-server.cjs\"],\n") yaml.WriteString(" \"tools\": [\"*\"],\n") yaml.WriteString(" \"env\": {\n") - yaml.WriteString(" \"GITHUB_AW_SAFE_OUTPUTS\": \"${{ env.GITHUB_AW_SAFE_OUTPUTS }}\",\n") - yaml.WriteString(" \"GITHUB_AW_SAFE_OUTPUTS_CONFIG\": ${{ toJSON(env.GITHUB_AW_SAFE_OUTPUTS_CONFIG) }}\n") + yaml.WriteString(" \"GITHUB_AW_SAFE_OUTPUTS\": \"\\${GITHUB_AW_SAFE_OUTPUTS}\",\n") + yaml.WriteString(" \"GITHUB_AW_SAFE_OUTPUTS_CONFIG\": \"\\${GITHUB_AW_SAFE_OUTPUTS_CONFIG}\"\n") yaml.WriteString(" }\n") if isLast { From adf63d4f1352467bc6f36fe676c1276d2a4c6472 Mon Sep 17 00:00:00 2001 From: Changeset Generator Date: Mon, 13 Oct 2025 22:51:07 +0000 Subject: [PATCH 3/9] Add changeset for Copilot CLI 0.0.340 update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .changeset/patch-update-copilot-cli-0-0-340.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changeset/patch-update-copilot-cli-0-0-340.md diff --git a/.changeset/patch-update-copilot-cli-0-0-340.md b/.changeset/patch-update-copilot-cli-0-0-340.md new file mode 100644 index 00000000000..e65076b2861 --- /dev/null +++ b/.changeset/patch-update-copilot-cli-0-0-340.md @@ -0,0 +1,7 @@ +--- +"gh-aw": patch +--- + +Update GitHub Copilot CLI to version 0.0.340 and implement ${} syntax for MCP environment variables + +This update upgrades the GitHub Copilot CLI from version 0.0.339 to 0.0.340 and implements the breaking change for MCP server environment variable configuration. The safe-outputs MCP server now uses the new `${VAR}` syntax for environment variable references instead of direct variable names. From 43a958ee7e0242a2cff38e96644728ec5280e277 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 13 Oct 2025 23:56:56 +0000 Subject: [PATCH 4/9] Add GITHUB_PERSONAL_ACCESS_TOKEN env var and ${} passthrough for GitHub MCP Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/artifacts-summary.lock.yml | 8 +++- .github/workflows/audit-workflows.lock.yml | 1 + .github/workflows/brave.lock.yml | 8 +++- .../workflows/changeset-generator.lock.yml | 1 + .github/workflows/ci-doctor.lock.yml | 8 +++- .../workflows/cli-version-checker.lock.yml | 1 + .github/workflows/daily-news.lock.yml | 8 +++- .github/workflows/dev.lock.yml | 4 +- .../duplicate-code-detector.lock.yml | 1 + .../github-mcp-tools-report.lock.yml | 1 + .../workflows/go-pattern-detector.lock.yml | 1 + .github/workflows/issue-classifier.lock.yml | 1 + .github/workflows/lockfile-stats.lock.yml | 1 + .../workflows/notion-issue-summary.lock.yml | 1 + .github/workflows/pdf-summary.lock.yml | 8 +++- .github/workflows/plan.lock.yml | 8 +++- .github/workflows/poem-bot.lock.yml | 8 +++- .github/workflows/q.lock.yml | 8 +++- .github/workflows/repo-tree-map.lock.yml | 8 +++- .github/workflows/scout.lock.yml | 8 +++- .github/workflows/security-fix-pr.lock.yml | 1 + .github/workflows/smoke-claude.lock.yml | 1 + .github/workflows/smoke-codex.lock.yml | 1 + .github/workflows/smoke-copilot.lock.yml | 8 +++- .github/workflows/smoke-genaiscript.lock.yml | 1 + .github/workflows/smoke-opencode.lock.yml | 1 + .../workflows/technical-doc-writer.lock.yml | 1 + .github/workflows/tidy.lock.yml | 8 +++- .github/workflows/unbloat-docs.lock.yml | 1 + pkg/workflow/copilot_engine.go | 38 +++++++--------- pkg/workflow/copilot_engine_test.go | 12 +++-- pkg/workflow/github_remote_mode_test.go | 17 +++++-- pkg/workflow/mcp_config_compilation_test.go | 16 +++++-- pkg/workflow/mcps.go | 44 ++++++++++++++----- 34 files changed, 177 insertions(+), 66 deletions(-) diff --git a/.github/workflows/artifacts-summary.lock.yml b/.github/workflows/artifacts-summary.lock.yml index 65b0ab1d6eb..28869cf1005 100644 --- a/.github/workflows/artifacts-summary.lock.yml +++ b/.github/workflows/artifacts-summary.lock.yml @@ -873,6 +873,7 @@ jobs: - name: Setup MCPs env: + GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-discussion\":{\"max\":1},\"missing-tool\":{}}" run: | @@ -889,7 +890,7 @@ jobs: "-i", "--rm", "-e", - "GITHUB_PERSONAL_ACCESS_TOKEN=${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}", + "GITHUB_PERSONAL_ACCESS_TOKEN", "-e", "GITHUB_TOOLSETS=all", "ghcr.io/github/github-mcp-server:v0.18.0" @@ -949,7 +950,10 @@ jobs: "list_releases", "list_starred_repositories", "list_sub_issues" - ] + ], + "env": { + "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_PERSONAL_ACCESS_TOKEN}" + } }, "safe_outputs": { "type": "local", diff --git a/.github/workflows/audit-workflows.lock.yml b/.github/workflows/audit-workflows.lock.yml index e4430156f93..72bf79ee02a 100644 --- a/.github/workflows/audit-workflows.lock.yml +++ b/.github/workflows/audit-workflows.lock.yml @@ -1018,6 +1018,7 @@ jobs: - name: Setup MCPs env: + GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-discussion\":{\"max\":1},\"missing-tool\":{}}" run: | diff --git a/.github/workflows/brave.lock.yml b/.github/workflows/brave.lock.yml index d050417317d..56c797b4153 100644 --- a/.github/workflows/brave.lock.yml +++ b/.github/workflows/brave.lock.yml @@ -1482,6 +1482,7 @@ jobs: - name: Setup MCPs env: + GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"max\":1},\"missing-tool\":{}}" run: | @@ -1516,7 +1517,7 @@ jobs: "-i", "--rm", "-e", - "GITHUB_PERSONAL_ACCESS_TOKEN=${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}", + "GITHUB_PERSONAL_ACCESS_TOKEN", "-e", "GITHUB_TOOLSETS=all", "ghcr.io/github/github-mcp-server:v0.18.0" @@ -1576,7 +1577,10 @@ jobs: "list_releases", "list_starred_repositories", "list_sub_issues" - ] + ], + "env": { + "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_PERSONAL_ACCESS_TOKEN}" + } }, "safe_outputs": { "type": "local", diff --git a/.github/workflows/changeset-generator.lock.yml b/.github/workflows/changeset-generator.lock.yml index 0ac64273086..3cf7342d108 100644 --- a/.github/workflows/changeset-generator.lock.yml +++ b/.github/workflows/changeset-generator.lock.yml @@ -1455,6 +1455,7 @@ jobs: - name: Setup MCPs env: + GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"missing-tool\":{},\"push-to-pull-request-branch\":{}}" run: | diff --git a/.github/workflows/ci-doctor.lock.yml b/.github/workflows/ci-doctor.lock.yml index 87ec6c4ec90..177a58f5ea3 100644 --- a/.github/workflows/ci-doctor.lock.yml +++ b/.github/workflows/ci-doctor.lock.yml @@ -844,6 +844,7 @@ jobs: - name: Setup MCPs env: + GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"max\":1},\"create-issue\":{\"max\":1},\"missing-tool\":{}}" run: | @@ -860,7 +861,7 @@ jobs: "-i", "--rm", "-e", - "GITHUB_PERSONAL_ACCESS_TOKEN=${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}", + "GITHUB_PERSONAL_ACCESS_TOKEN", "-e", "GITHUB_TOOLSETS=all", "ghcr.io/github/github-mcp-server:v0.18.0" @@ -920,7 +921,10 @@ jobs: "list_releases", "list_starred_repositories", "list_sub_issues" - ] + ], + "env": { + "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_PERSONAL_ACCESS_TOKEN}" + } }, "safe_outputs": { "type": "local", diff --git a/.github/workflows/cli-version-checker.lock.yml b/.github/workflows/cli-version-checker.lock.yml index 536df3e9098..238478f48dc 100644 --- a/.github/workflows/cli-version-checker.lock.yml +++ b/.github/workflows/cli-version-checker.lock.yml @@ -980,6 +980,7 @@ jobs: - name: Setup MCPs env: + GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-pull-request\":{},\"missing-tool\":{}}" run: | diff --git a/.github/workflows/daily-news.lock.yml b/.github/workflows/daily-news.lock.yml index 19f18a9dcf0..1390e00a5b1 100644 --- a/.github/workflows/daily-news.lock.yml +++ b/.github/workflows/daily-news.lock.yml @@ -875,6 +875,7 @@ jobs: - name: Setup MCPs env: + GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-discussion\":{\"max\":1},\"missing-tool\":{}}" run: | @@ -891,7 +892,7 @@ jobs: "-i", "--rm", "-e", - "GITHUB_PERSONAL_ACCESS_TOKEN=${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}", + "GITHUB_PERSONAL_ACCESS_TOKEN", "-e", "GITHUB_TOOLSETS=all", "ghcr.io/github/github-mcp-server:v0.18.0" @@ -951,7 +952,10 @@ jobs: "list_releases", "list_starred_repositories", "list_sub_issues" - ] + ], + "env": { + "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_PERSONAL_ACCESS_TOKEN}" + } }, "safe_outputs": { "type": "local", diff --git a/.github/workflows/dev.lock.yml b/.github/workflows/dev.lock.yml index cc4358f84f8..4a8ca0b3b8f 100644 --- a/.github/workflows/dev.lock.yml +++ b/.github/workflows/dev.lock.yml @@ -872,6 +872,7 @@ jobs: - name: Setup MCPs env: + GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-issue\":{\"max\":1},\"missing-tool\":{}}" run: | @@ -1961,9 +1962,10 @@ jobs: await main(); env: - GITHUB_AW_SECRET_NAMES: 'CODEX_API_KEY,GH_AW_GITHUB_TOKEN,OPENAI_API_KEY' + GITHUB_AW_SECRET_NAMES: 'CODEX_API_KEY,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN,OPENAI_API_KEY' SECRET_CODEX_API_KEY: ${{ secrets.CODEX_API_KEY }} SECRET_GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} + SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SECRET_OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - name: Upload engine output files uses: actions/upload-artifact@v4 diff --git a/.github/workflows/duplicate-code-detector.lock.yml b/.github/workflows/duplicate-code-detector.lock.yml index 5cbe68dd514..a34ec36f7b8 100644 --- a/.github/workflows/duplicate-code-detector.lock.yml +++ b/.github/workflows/duplicate-code-detector.lock.yml @@ -893,6 +893,7 @@ jobs: - name: Setup MCPs env: + GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-issue\":{\"max\":1},\"missing-tool\":{}}" run: | diff --git a/.github/workflows/github-mcp-tools-report.lock.yml b/.github/workflows/github-mcp-tools-report.lock.yml index 70dae3544f3..b49e4abe5d0 100644 --- a/.github/workflows/github-mcp-tools-report.lock.yml +++ b/.github/workflows/github-mcp-tools-report.lock.yml @@ -1000,6 +1000,7 @@ jobs: - name: Setup MCPs env: + GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-discussion\":{\"max\":1},\"missing-tool\":{}}" run: | diff --git a/.github/workflows/go-pattern-detector.lock.yml b/.github/workflows/go-pattern-detector.lock.yml index 193a933f6f5..f5a3a525812 100644 --- a/.github/workflows/go-pattern-detector.lock.yml +++ b/.github/workflows/go-pattern-detector.lock.yml @@ -987,6 +987,7 @@ jobs: - name: Setup MCPs env: + GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-issue\":{\"max\":1},\"missing-tool\":{}}" run: | diff --git a/.github/workflows/issue-classifier.lock.yml b/.github/workflows/issue-classifier.lock.yml index 2b8b59fb3eb..73a97a1f38d 100644 --- a/.github/workflows/issue-classifier.lock.yml +++ b/.github/workflows/issue-classifier.lock.yml @@ -1342,6 +1342,7 @@ jobs: - name: Setup MCPs env: + GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-labels\":{\"allowed\":[\"bug\",\"feature\",\"enhancement\",\"documentation\"],\"max\":1},\"missing-tool\":{}}" run: | diff --git a/.github/workflows/lockfile-stats.lock.yml b/.github/workflows/lockfile-stats.lock.yml index 6e0304ea07f..527c75c6d6a 100644 --- a/.github/workflows/lockfile-stats.lock.yml +++ b/.github/workflows/lockfile-stats.lock.yml @@ -1000,6 +1000,7 @@ jobs: - name: Setup MCPs env: + GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-discussion\":{\"max\":1},\"missing-tool\":{}}" run: | diff --git a/.github/workflows/notion-issue-summary.lock.yml b/.github/workflows/notion-issue-summary.lock.yml index 442cb9decf9..2203458a3b5 100644 --- a/.github/workflows/notion-issue-summary.lock.yml +++ b/.github/workflows/notion-issue-summary.lock.yml @@ -1154,6 +1154,7 @@ jobs: - name: Setup MCPs env: + GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"notion-add-comment\":{\"description\":\"Add a comment to a Notion page\",\"inputs\":{\"comment\":{\"description\":\"The comment text to add\",\"required\":true,\"type\":\"string\"},\"page_id\":{\"description\":\"The Notion page ID to add a comment to\",\"required\":true,\"type\":\"string\"}},\"output\":\"Comment added to Notion successfully!\"}}" run: | diff --git a/.github/workflows/pdf-summary.lock.yml b/.github/workflows/pdf-summary.lock.yml index 7b750d1ae19..7b8ed8f517e 100644 --- a/.github/workflows/pdf-summary.lock.yml +++ b/.github/workflows/pdf-summary.lock.yml @@ -1396,6 +1396,7 @@ jobs: - name: Setup MCPs env: + GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"max\":1},\"missing-tool\":{}}" run: | @@ -1412,7 +1413,7 @@ jobs: "-i", "--rm", "-e", - "GITHUB_PERSONAL_ACCESS_TOKEN=${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}", + "GITHUB_PERSONAL_ACCESS_TOKEN", "-e", "GITHUB_TOOLSETS=all", "ghcr.io/github/github-mcp-server:v0.18.0" @@ -1472,7 +1473,10 @@ jobs: "list_releases", "list_starred_repositories", "list_sub_issues" - ] + ], + "env": { + "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_PERSONAL_ACCESS_TOKEN}" + } }, "markitdown": { "type": "local", diff --git a/.github/workflows/plan.lock.yml b/.github/workflows/plan.lock.yml index ef5df8dfb7f..f57223191a9 100644 --- a/.github/workflows/plan.lock.yml +++ b/.github/workflows/plan.lock.yml @@ -1351,6 +1351,7 @@ jobs: - name: Setup MCPs env: + GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-issue\":{\"max\":5},\"missing-tool\":{}}" run: | @@ -1367,7 +1368,7 @@ jobs: "-i", "--rm", "-e", - "GITHUB_PERSONAL_ACCESS_TOKEN=${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}", + "GITHUB_PERSONAL_ACCESS_TOKEN", "-e", "GITHUB_TOOLSETS=all", "ghcr.io/github/github-mcp-server:v0.18.0" @@ -1427,7 +1428,10 @@ jobs: "list_releases", "list_starred_repositories", "list_sub_issues" - ] + ], + "env": { + "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_PERSONAL_ACCESS_TOKEN}" + } }, "safe_outputs": { "type": "local", diff --git a/.github/workflows/poem-bot.lock.yml b/.github/workflows/poem-bot.lock.yml index 12446424f95..6ef4821157d 100644 --- a/.github/workflows/poem-bot.lock.yml +++ b/.github/workflows/poem-bot.lock.yml @@ -1381,6 +1381,7 @@ jobs: - name: Setup MCPs env: + GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"max\":3,\"target\":\"*\"},\"add-labels\":{\"allowed\":[\"poetry\",\"creative\",\"automation\",\"ai-generated\",\"epic\",\"haiku\",\"sonnet\",\"limerick\"],\"max\":5},\"create-issue\":{\"max\":2},\"create-pull-request\":{},\"create-pull-request-review-comment\":{\"max\":2},\"missing-tool\":{},\"push-to-pull-request-branch\":{},\"update-issue\":{\"max\":2},\"upload-asset\":{}}" GITHUB_AW_ASSETS_BRANCH: "assets/${{ github.workflow }}" @@ -1400,7 +1401,7 @@ jobs: "-i", "--rm", "-e", - "GITHUB_PERSONAL_ACCESS_TOKEN=${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}", + "GITHUB_PERSONAL_ACCESS_TOKEN", "-e", "GITHUB_TOOLSETS=all", "ghcr.io/github/github-mcp-server:v0.18.0" @@ -1461,7 +1462,10 @@ jobs: "list_releases", "list_starred_repositories", "list_sub_issues" - ] + ], + "env": { + "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_PERSONAL_ACCESS_TOKEN}" + } }, "safe_outputs": { "type": "local", diff --git a/.github/workflows/q.lock.yml b/.github/workflows/q.lock.yml index 3b6663cd84c..ad71cb1d0b2 100644 --- a/.github/workflows/q.lock.yml +++ b/.github/workflows/q.lock.yml @@ -1440,6 +1440,7 @@ jobs: - name: Setup MCPs env: + GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-pull-request\":{},\"missing-tool\":{}}" run: | @@ -1463,7 +1464,7 @@ jobs: "-i", "--rm", "-e", - "GITHUB_PERSONAL_ACCESS_TOKEN=${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}", + "GITHUB_PERSONAL_ACCESS_TOKEN", "-e", "GITHUB_TOOLSETS=all", "ghcr.io/github/github-mcp-server:v0.18.0" @@ -1523,7 +1524,10 @@ jobs: "list_releases", "list_starred_repositories", "list_sub_issues" - ] + ], + "env": { + "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_PERSONAL_ACCESS_TOKEN}" + } }, "safe_outputs": { "type": "local", diff --git a/.github/workflows/repo-tree-map.lock.yml b/.github/workflows/repo-tree-map.lock.yml index 05ff91a6b69..8c947c4758d 100644 --- a/.github/workflows/repo-tree-map.lock.yml +++ b/.github/workflows/repo-tree-map.lock.yml @@ -871,6 +871,7 @@ jobs: - name: Setup MCPs env: + GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-discussion\":{\"max\":1},\"missing-tool\":{}}" run: | @@ -887,7 +888,7 @@ jobs: "-i", "--rm", "-e", - "GITHUB_PERSONAL_ACCESS_TOKEN=${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}", + "GITHUB_PERSONAL_ACCESS_TOKEN", "-e", "GITHUB_TOOLSETS=all", "ghcr.io/github/github-mcp-server:v0.18.0" @@ -947,7 +948,10 @@ jobs: "list_releases", "list_starred_repositories", "list_sub_issues" - ] + ], + "env": { + "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_PERSONAL_ACCESS_TOKEN}" + } }, "safe_outputs": { "type": "local", diff --git a/.github/workflows/scout.lock.yml b/.github/workflows/scout.lock.yml index a0c0026173a..03e393ded35 100644 --- a/.github/workflows/scout.lock.yml +++ b/.github/workflows/scout.lock.yml @@ -1678,6 +1678,7 @@ jobs: - name: Setup MCPs env: + GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"max\":1},\"missing-tool\":{}}" run: | @@ -1737,7 +1738,7 @@ jobs: "-i", "--rm", "-e", - "GITHUB_PERSONAL_ACCESS_TOKEN=${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}", + "GITHUB_PERSONAL_ACCESS_TOKEN", "-e", "GITHUB_TOOLSETS=all", "ghcr.io/github/github-mcp-server:v0.18.0" @@ -1797,7 +1798,10 @@ jobs: "list_releases", "list_starred_repositories", "list_sub_issues" - ] + ], + "env": { + "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_PERSONAL_ACCESS_TOKEN}" + } }, "microsoftdocs": { "type": "http", diff --git a/.github/workflows/security-fix-pr.lock.yml b/.github/workflows/security-fix-pr.lock.yml index cd7d0e60611..0c3fcffed6b 100644 --- a/.github/workflows/security-fix-pr.lock.yml +++ b/.github/workflows/security-fix-pr.lock.yml @@ -999,6 +999,7 @@ jobs: - name: Setup MCPs env: + GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-pull-request\":{},\"missing-tool\":{}}" run: | diff --git a/.github/workflows/smoke-claude.lock.yml b/.github/workflows/smoke-claude.lock.yml index f44e03afedb..e6e08b5e25a 100644 --- a/.github/workflows/smoke-claude.lock.yml +++ b/.github/workflows/smoke-claude.lock.yml @@ -976,6 +976,7 @@ jobs: - name: Setup MCPs env: + GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-issue\":{\"max\":1,\"min\":1},\"missing-tool\":{}}" run: | diff --git a/.github/workflows/smoke-codex.lock.yml b/.github/workflows/smoke-codex.lock.yml index b91f0ab3a3b..5db74659ae7 100644 --- a/.github/workflows/smoke-codex.lock.yml +++ b/.github/workflows/smoke-codex.lock.yml @@ -867,6 +867,7 @@ jobs: - name: Setup MCPs env: + GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-issue\":{\"max\":1,\"min\":1},\"missing-tool\":{}}" run: | diff --git a/.github/workflows/smoke-copilot.lock.yml b/.github/workflows/smoke-copilot.lock.yml index 81c5278b464..086e414aecc 100644 --- a/.github/workflows/smoke-copilot.lock.yml +++ b/.github/workflows/smoke-copilot.lock.yml @@ -869,6 +869,7 @@ jobs: - name: Setup MCPs env: + GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-issue\":{\"max\":1,\"min\":1},\"missing-tool\":{}}" run: | @@ -885,7 +886,7 @@ jobs: "-i", "--rm", "-e", - "GITHUB_PERSONAL_ACCESS_TOKEN=${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}", + "GITHUB_PERSONAL_ACCESS_TOKEN", "-e", "GITHUB_TOOLSETS=all", "ghcr.io/github/github-mcp-server:v0.18.0" @@ -945,7 +946,10 @@ jobs: "list_releases", "list_starred_repositories", "list_sub_issues" - ] + ], + "env": { + "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_PERSONAL_ACCESS_TOKEN}" + } }, "safe_outputs": { "type": "local", diff --git a/.github/workflows/smoke-genaiscript.lock.yml b/.github/workflows/smoke-genaiscript.lock.yml index f108ffcf638..ce6c94ed192 100644 --- a/.github/workflows/smoke-genaiscript.lock.yml +++ b/.github/workflows/smoke-genaiscript.lock.yml @@ -869,6 +869,7 @@ jobs: - name: Setup MCPs env: + GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-issue\":{\"max\":1,\"min\":1},\"missing-tool\":{}}" run: | diff --git a/.github/workflows/smoke-opencode.lock.yml b/.github/workflows/smoke-opencode.lock.yml index 04f947f89d9..f953c01cf21 100644 --- a/.github/workflows/smoke-opencode.lock.yml +++ b/.github/workflows/smoke-opencode.lock.yml @@ -869,6 +869,7 @@ jobs: - name: Setup MCPs env: + GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-issue\":{\"max\":1,\"min\":1},\"missing-tool\":{}}" run: | diff --git a/.github/workflows/technical-doc-writer.lock.yml b/.github/workflows/technical-doc-writer.lock.yml index 789e1bf2ee6..19984c977c3 100644 --- a/.github/workflows/technical-doc-writer.lock.yml +++ b/.github/workflows/technical-doc-writer.lock.yml @@ -1015,6 +1015,7 @@ jobs: - name: Setup MCPs env: + GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"max\":1},\"create-pull-request\":{},\"missing-tool\":{},\"upload-asset\":{}}" GITHUB_AW_ASSETS_BRANCH: "assets/${{ github.workflow }}" diff --git a/.github/workflows/tidy.lock.yml b/.github/workflows/tidy.lock.yml index a7b79797fe2..91c6a74e38f 100644 --- a/.github/workflows/tidy.lock.yml +++ b/.github/workflows/tidy.lock.yml @@ -1212,6 +1212,7 @@ jobs: - name: Setup MCPs env: + GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-pull-request\":{},\"missing-tool\":{},\"push-to-pull-request-branch\":{}}" run: | @@ -1228,7 +1229,7 @@ jobs: "-i", "--rm", "-e", - "GITHUB_PERSONAL_ACCESS_TOKEN=${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}", + "GITHUB_PERSONAL_ACCESS_TOKEN", "-e", "GITHUB_TOOLSETS=all", "ghcr.io/github/github-mcp-server:v0.18.0" @@ -1288,7 +1289,10 @@ jobs: "list_releases", "list_starred_repositories", "list_sub_issues" - ] + ], + "env": { + "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_PERSONAL_ACCESS_TOKEN}" + } }, "safe_outputs": { "type": "local", diff --git a/.github/workflows/unbloat-docs.lock.yml b/.github/workflows/unbloat-docs.lock.yml index 654bd6aeddc..f355183f19a 100644 --- a/.github/workflows/unbloat-docs.lock.yml +++ b/.github/workflows/unbloat-docs.lock.yml @@ -1320,6 +1320,7 @@ jobs: - name: Setup MCPs env: + GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"max\":1},\"create-pull-request\":{},\"missing-tool\":{}}" run: | diff --git a/pkg/workflow/copilot_engine.go b/pkg/workflow/copilot_engine.go index 0a76e879896..4159a409097 100644 --- a/pkg/workflow/copilot_engine.go +++ b/pkg/workflow/copilot_engine.go @@ -244,7 +244,6 @@ func (e *CopilotEngine) RenderMCPConfig(yaml *strings.Builder, tools map[string] // Supports both local (Docker) and remote (hosted) modes func (e *CopilotEngine) renderGitHubCopilotMCPConfig(yaml *strings.Builder, githubTool any, isLast bool) { githubType := getGitHubType(githubTool) - customGitHubToken := getGitHubToken(githubTool) readOnly := getGitHubReadOnly(githubTool) toolsets := getGitHubToolsets(githubTool) allowedTools := getGitHubAllowedTools(githubTool) @@ -257,20 +256,12 @@ func (e *CopilotEngine) renderGitHubCopilotMCPConfig(yaml *strings.Builder, gith yaml.WriteString(" \"type\": \"http\",\n") yaml.WriteString(" \"url\": \"https://api.githubcopilot.com/mcp/\",\n") yaml.WriteString(" \"headers\": {\n") - - // Add custom github-token if specified, otherwise use GH_AW_GITHUB_TOKEN - if customGitHubToken != "" { - yaml.WriteString(fmt.Sprintf(" \"Authorization\": \"Bearer %s\"", customGitHubToken)) - } else { - yaml.WriteString(" \"Authorization\": \"Bearer ${{ secrets.GH_AW_GITHUB_TOKEN }}\"") - } + yaml.WriteString(" \"Authorization\": \"Bearer \\${GITHUB_PERSONAL_ACCESS_TOKEN}\"\n") // Add X-MCP-Readonly header if read-only mode is enabled if readOnly { yaml.WriteString(",\n") yaml.WriteString(" \"X-MCP-Readonly\": \"true\"\n") - } else { - yaml.WriteString("\n") } yaml.WriteString(" },\n") @@ -285,10 +276,15 @@ func (e *CopilotEngine) renderGitHubCopilotMCPConfig(yaml *strings.Builder, gith } fmt.Fprintf(yaml, " \"%s\"%s\n", tool, comma) } - yaml.WriteString(" ]\n") + yaml.WriteString(" ],\n") } else { - yaml.WriteString(" \"tools\": [\"*\"]\n") + yaml.WriteString(" \"tools\": [\"*\"],\n") } + + // Add env section for passthrough + yaml.WriteString(" \"env\": {\n") + yaml.WriteString(" \"GITHUB_PERSONAL_ACCESS_TOKEN\": \"\\${GITHUB_PERSONAL_ACCESS_TOKEN}\"\n") + yaml.WriteString(" }\n") } else { // Local mode - use Docker-based GitHub MCP server (default) githubDockerImageVersion := getGitHubDockerImageVersion(githubTool) @@ -303,13 +299,7 @@ func (e *CopilotEngine) renderGitHubCopilotMCPConfig(yaml *strings.Builder, gith yaml.WriteString(" \"-i\",\n") yaml.WriteString(" \"--rm\",\n") yaml.WriteString(" \"-e\",\n") - - // Use custom token if specified, otherwise use default - if customGitHubToken != "" { - yaml.WriteString(fmt.Sprintf(" \"GITHUB_PERSONAL_ACCESS_TOKEN=%s\",\n", customGitHubToken)) - } else { - yaml.WriteString(" \"GITHUB_PERSONAL_ACCESS_TOKEN=${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}\",\n") - } + yaml.WriteString(" \"GITHUB_PERSONAL_ACCESS_TOKEN\",\n") if readOnly { yaml.WriteString(" \"-e\",\n") @@ -343,11 +333,15 @@ func (e *CopilotEngine) renderGitHubCopilotMCPConfig(yaml *strings.Builder, gith } fmt.Fprintf(yaml, " \"%s\"%s\n", tool, comma) } - yaml.WriteString(" ]\n") + yaml.WriteString(" ],\n") } else { - yaml.WriteString(" \"tools\": [\"*\"]\n") + yaml.WriteString(" \"tools\": [\"*\"],\n") } - // copilot does not support env + + // Add env section for passthrough + yaml.WriteString(" \"env\": {\n") + yaml.WriteString(" \"GITHUB_PERSONAL_ACCESS_TOKEN\": \"\\${GITHUB_PERSONAL_ACCESS_TOKEN}\"\n") + yaml.WriteString(" }\n") } if isLast { diff --git a/pkg/workflow/copilot_engine_test.go b/pkg/workflow/copilot_engine_test.go index 644aa472d1f..d0e10897162 100644 --- a/pkg/workflow/copilot_engine_test.go +++ b/pkg/workflow/copilot_engine_test.go @@ -535,9 +535,11 @@ func TestCopilotEngineRenderGitHubMCPConfig(t *testing.T) { `"-i",`, `"--rm",`, `"-e",`, - `"GITHUB_PERSONAL_ACCESS_TOKEN=${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}",`, + `"GITHUB_PERSONAL_ACCESS_TOKEN",`, `"ghcr.io/github/github-mcp-server:v0.18.0"`, `"tools": ["*"]`, + `"env": {`, + `"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_PERSONAL_ACCESS_TOKEN}"`, `},`, }, }, @@ -551,9 +553,11 @@ func TestCopilotEngineRenderGitHubMCPConfig(t *testing.T) { `"github": {`, `"type": "local",`, `"command": "docker",`, - `"GITHUB_PERSONAL_ACCESS_TOKEN=${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}",`, + `"GITHUB_PERSONAL_ACCESS_TOKEN",`, `"ghcr.io/github/github-mcp-server:v1.2.3"`, `"tools": ["*"]`, + `"env": {`, + `"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_PERSONAL_ACCESS_TOKEN}"`, `}`, }, }, @@ -628,7 +632,9 @@ func TestCopilotEngineRenderMCPConfigWithGitHub(t *testing.T) { `"type": "local",`, `"command": "docker",`, `"ghcr.io/github/github-mcp-server:custom-version"`, - `"GITHUB_PERSONAL_ACCESS_TOKEN=${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}",`, + `"GITHUB_PERSONAL_ACCESS_TOKEN",`, + `"env": {`, + `"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_PERSONAL_ACCESS_TOKEN}"`, `"tools": ["*"]`, "EOF", "-------START MCP CONFIG-----------", diff --git a/pkg/workflow/github_remote_mode_test.go b/pkg/workflow/github_remote_mode_test.go index 44430a94628..9c6a62f4f67 100644 --- a/pkg/workflow/github_remote_mode_test.go +++ b/pkg/workflow/github_remote_mode_test.go @@ -216,9 +216,20 @@ This is a test workflow for GitHub remote mode configuration. if tt.expectedURL != "" && !strings.Contains(lockContent, tt.expectedURL) { t.Errorf("Expected URL %s but didn't find it in:\n%s", tt.expectedURL, lockContent) } - if tt.expectedToken != "" { - if !strings.Contains(lockContent, `"Authorization": "Bearer `+tt.expectedToken) { - t.Errorf("Expected Authorization header with token %s but didn't find it in:\n%s", tt.expectedToken, lockContent) + // For Copilot engine, check for new ${} syntax + if tt.engineType == "copilot" { + if !strings.Contains(lockContent, `"Authorization": "Bearer \${GITHUB_PERSONAL_ACCESS_TOKEN}"`) { + t.Errorf("Expected Authorization header with ${GITHUB_PERSONAL_ACCESS_TOKEN} syntax but didn't find it in:\n%s", lockContent) + } + if !strings.Contains(lockContent, `"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_PERSONAL_ACCESS_TOKEN}"`) { + t.Errorf("Expected env section with GITHUB_PERSONAL_ACCESS_TOKEN passthrough but didn't find it in:\n%s", lockContent) + } + } else { + // For other engines, check for old GitHub Actions expression syntax + if tt.expectedToken != "" { + if !strings.Contains(lockContent, `"Authorization": "Bearer `+tt.expectedToken) { + t.Errorf("Expected Authorization header with token %s but didn't find it in:\n%s", tt.expectedToken, lockContent) + } } } // Check for X-MCP-Readonly header if this is a read-only test diff --git a/pkg/workflow/mcp_config_compilation_test.go b/pkg/workflow/mcp_config_compilation_test.go index a9e8ea4a2f3..17f9c4c4848 100644 --- a/pkg/workflow/mcp_config_compilation_test.go +++ b/pkg/workflow/mcp_config_compilation_test.go @@ -167,12 +167,22 @@ This workflow tests that MCP server env vars are sorted alphabetically. t.Fatalf("Failed to generate YAML: %v", err) } - // Find the env section in the generated YAML - envIndex := strings.Index(yamlContent, `"env": {`) + // Find the test-server env section in the generated YAML + // Look for "test-server" first, then find the env section after it + testServerIndex := strings.Index(yamlContent, `"test-server"`) + if testServerIndex == -1 { + t.Fatalf("Could not find test-server section in generated YAML") + } + + // Find env section after test-server + envIndex := strings.Index(yamlContent[testServerIndex:], `"env": {`) if envIndex == -1 { - t.Fatalf("Could not find env section in generated YAML") + t.Fatalf("Could not find env section for test-server in generated YAML") } + // Adjust envIndex to be relative to the full yamlContent + envIndex += testServerIndex + // Extract a portion of YAML starting from env section (next 300 chars should be enough) envSection := yamlContent[envIndex : envIndex+300] diff --git a/pkg/workflow/mcps.go b/pkg/workflow/mcps.go index 9642d89646f..83921ff065f 100644 --- a/pkg/workflow/mcps.go +++ b/pkg/workflow/mcps.go @@ -161,16 +161,34 @@ func (c *Compiler) generateMCPSetup(yaml *strings.Builder, tools map[string]any, // Use the engine's RenderMCPConfig method yaml.WriteString(" - name: Setup MCPs\n") - if HasSafeOutputsEnabled(workflowData.SafeOutputs) { - if safeOutputConfig != "" { - // Add environment variables for JSONL validation - yaml.WriteString(" env:\n") - fmt.Fprintf(yaml, " GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }}\n") - fmt.Fprintf(yaml, " GITHUB_AW_SAFE_OUTPUTS_CONFIG: %q\n", safeOutputConfig) - if workflowData.SafeOutputs != nil && workflowData.SafeOutputs.UploadAssets != nil { - fmt.Fprintf(yaml, " GITHUB_AW_ASSETS_BRANCH: %q\n", workflowData.SafeOutputs.UploadAssets.BranchName) - fmt.Fprintf(yaml, " GITHUB_AW_ASSETS_MAX_SIZE_KB: %d\n", workflowData.SafeOutputs.UploadAssets.MaxSizeKB) - fmt.Fprintf(yaml, " GITHUB_AW_ASSETS_ALLOWED_EXTS: %q\n", strings.Join(workflowData.SafeOutputs.UploadAssets.AllowedExts, ",")) + + // Check if we need to add environment variables + needsEnv := HasSafeOutputsEnabled(workflowData.SafeOutputs) || hasGitHubTool(tools) + + if needsEnv { + yaml.WriteString(" env:\n") + + // Add GITHUB_PERSONAL_ACCESS_TOKEN for GitHub MCP server + if hasGitHubTool(tools) { + githubTool := tools["github"] + customGitHubToken := getGitHubToken(githubTool) + if customGitHubToken != "" { + fmt.Fprintf(yaml, " GITHUB_PERSONAL_ACCESS_TOKEN: %s\n", customGitHubToken) + } else { + yaml.WriteString(" GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}\n") + } + } + + // Add safe outputs environment variables + if HasSafeOutputsEnabled(workflowData.SafeOutputs) { + if safeOutputConfig != "" { + fmt.Fprintf(yaml, " GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }}\n") + fmt.Fprintf(yaml, " GITHUB_AW_SAFE_OUTPUTS_CONFIG: %q\n", safeOutputConfig) + if workflowData.SafeOutputs != nil && workflowData.SafeOutputs.UploadAssets != nil { + fmt.Fprintf(yaml, " GITHUB_AW_ASSETS_BRANCH: %q\n", workflowData.SafeOutputs.UploadAssets.BranchName) + fmt.Fprintf(yaml, " GITHUB_AW_ASSETS_MAX_SIZE_KB: %d\n", workflowData.SafeOutputs.UploadAssets.MaxSizeKB) + fmt.Fprintf(yaml, " GITHUB_AW_ASSETS_ALLOWED_EXTS: %q\n", strings.Join(workflowData.SafeOutputs.UploadAssets.AllowedExts, ",")) + } } } } @@ -192,6 +210,12 @@ func getGitHubDockerImageVersion(githubTool any) string { return githubDockerImageVersion } +// hasGitHubTool checks if the tools map contains a GitHub tool +func hasGitHubTool(tools map[string]any) bool { + _, exists := tools["github"] + return exists +} + // getGitHubType extracts the mode from GitHub tool configuration (local or remote) func getGitHubType(githubTool any) string { if toolConfig, ok := githubTool.(map[string]any); ok { From d7d4bfb9c26186cd7041b117191631aae183c608 Mon Sep 17 00:00:00 2001 From: Peli de Halleux Date: Tue, 14 Oct 2025 00:16:18 +0000 Subject: [PATCH 5/9] Update GitHub Actions workflow to use Copilot engine and simplify configuration --- .github/workflows/dev.lock.yml | 1037 +++++++++++++++++++++++++------- .github/workflows/dev.md | 12 +- 2 files changed, 814 insertions(+), 235 deletions(-) diff --git a/.github/workflows/dev.lock.yml b/.github/workflows/dev.lock.yml index 4a8ca0b3b8f..4905b72b6fd 100644 --- a/.github/workflows/dev.lock.yml +++ b/.github/workflows/dev.lock.yml @@ -5,15 +5,12 @@ name: "Dev" "on": - push: - paths: - - .github/workflows/dev.lock.yml workflow_dispatch: null permissions: {} concurrency: - group: "gh-aw-${{ github.workflow }}-${{ github.ref }}" + group: "gh-aw-${{ github.workflow }}" run-name: "Dev" @@ -134,6 +131,8 @@ jobs: permissions: actions: read contents: read + concurrency: + group: "gh-aw-copilot" env: GITHUB_AW_SAFE_OUTPUTS: /tmp/gh-aw/safe-outputs/outputs.jsonl GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-issue\":{\"max\":1},\"missing-tool\":{}}" @@ -193,8 +192,8 @@ jobs: uses: actions/setup-node@v4 with: node-version: '24' - - name: Install Codex - run: npm install -g @openai/codex@0.46.0 + - name: Install GitHub Copilot CLI + run: npm install -g @github/copilot@0.0.340 - name: Setup Safe Outputs Collector MCP run: | mkdir -p /tmp/gh-aw/safe-outputs @@ -877,24 +876,103 @@ jobs: GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-issue\":{\"max\":1},\"missing-tool\":{}}" run: | mkdir -p /tmp/gh-aw/mcp-config - cat > /tmp/gh-aw/mcp-config/config.toml << EOF - [history] - persistence = "none" - - [mcp_servers.github] - user_agent = "dev" - startup_timeout_sec = 120 - tool_timeout_sec = 120 - url = "https://api.githubcopilot.com/mcp/" - bearer_token_env_var = "GH_AW_GITHUB_TOKEN" - - [mcp_servers.safe_outputs] - command = "node" - args = [ - "/tmp/gh-aw/safe-outputs/mcp-server.cjs", - ] - env = { "GITHUB_AW_SAFE_OUTPUTS" = "${{ env.GITHUB_AW_SAFE_OUTPUTS }}", "GITHUB_AW_SAFE_OUTPUTS_CONFIG" = ${{ toJSON(env.GITHUB_AW_SAFE_OUTPUTS_CONFIG) }}, "GITHUB_AW_ASSETS_BRANCH" = "${{ env.GITHUB_AW_ASSETS_BRANCH }}", "GITHUB_AW_ASSETS_MAX_SIZE_KB" = "${{ env.GITHUB_AW_ASSETS_MAX_SIZE_KB }}", "GITHUB_AW_ASSETS_ALLOWED_EXTS" = "${{ env.GITHUB_AW_ASSETS_ALLOWED_EXTS }}" } + mkdir -p /home/runner/.copilot + cat > /home/runner/.copilot/mcp-config.json << EOF + { + "mcpServers": { + "github": { + "type": "local", + "command": "docker", + "args": [ + "run", + "-i", + "--rm", + "-e", + "GITHUB_PERSONAL_ACCESS_TOKEN", + "-e", + "GITHUB_TOOLSETS=all", + "ghcr.io/github/github-mcp-server:v0.18.0" + ], + "tools": [ + "download_workflow_run_artifact", + "get_job_logs", + "get_workflow_run", + "get_workflow_run_logs", + "get_workflow_run_usage", + "list_workflow_jobs", + "list_workflow_run_artifacts", + "list_workflow_runs", + "list_workflows", + "get_code_scanning_alert", + "list_code_scanning_alerts", + "get_me", + "get_dependabot_alert", + "list_dependabot_alerts", + "get_discussion", + "get_discussion_comments", + "list_discussion_categories", + "list_discussions", + "get_issue", + "get_issue_comments", + "list_issues", + "search_issues", + "get_notification_details", + "list_notifications", + "search_orgs", + "get_label", + "list_label", + "get_pull_request", + "get_pull_request_comments", + "get_pull_request_diff", + "get_pull_request_files", + "get_pull_request_reviews", + "get_pull_request_status", + "list_pull_requests", + "pull_request_read", + "search_pull_requests", + "get_commit", + "get_file_contents", + "get_tag", + "list_branches", + "list_commits", + "list_tags", + "search_code", + "search_repositories", + "get_secret_scanning_alert", + "list_secret_scanning_alerts", + "search_users", + "get_latest_release", + "get_pull_request_review_comments", + "get_release_by_tag", + "list_issue_types", + "list_releases", + "list_starred_repositories", + "list_sub_issues" + ], + "env": { + "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_PERSONAL_ACCESS_TOKEN}" + } + }, + "safe_outputs": { + "type": "local", + "command": "node", + "args": ["/tmp/gh-aw/safe-outputs/mcp-server.cjs"], + "tools": ["*"], + "env": { + "GITHUB_AW_SAFE_OUTPUTS": "\${GITHUB_AW_SAFE_OUTPUTS}", + "GITHUB_AW_SAFE_OUTPUTS_CONFIG": "\${GITHUB_AW_SAFE_OUTPUTS_CONFIG}" + } + } + } + } EOF + echo "-------START MCP CONFIG-----------" + cat /home/runner/.copilot/mcp-config.json + echo "-------END MCP CONFIG-----------" + echo "-------/home/runner/.copilot-----------" + find /home/runner/.copilot + echo "HOME: $HOME" + echo "GITHUB_COPILOT_CLI_MODE: $GITHUB_COPILOT_CLI_MODE" - name: Create prompt env: GITHUB_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt @@ -982,7 +1060,7 @@ jobs: echo '```' >> $GITHUB_STEP_SUMMARY - name: Capture agent version run: | - VERSION_OUTPUT=$(codex --version 2>&1 || echo "unknown") + VERSION_OUTPUT=$(copilot --version 2>&1 || echo "unknown") # Extract semantic version pattern (e.g., 1.2.3, v1.2.3-beta) CLEAN_VERSION=$(echo "$VERSION_OUTPUT" | grep -oE 'v?[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+)?' | head -n1 || echo "unknown") echo "AGENT_VERSION=$CLEAN_VERSION" >> $GITHUB_ENV @@ -994,13 +1072,13 @@ jobs: const fs = require('fs'); const awInfo = { - engine_id: "codex", - engine_name: "Codex", + engine_id: "copilot", + engine_name: "GitHub Copilot CLI", model: "", version: "", agent_version: process.env.AGENT_VERSION || "", workflow_name: "Dev", - experimental: true, + experimental: false, supports_tools_allowlist: true, supports_http_transport: true, run_id: context.runId, @@ -1027,22 +1105,78 @@ jobs: name: aw_info.json path: /tmp/gh-aw/aw_info.json if-no-files-found: warn - - name: Run Codex + - name: Execute GitHub Copilot CLI + id: agentic_execution + # Copilot CLI tool arguments (sorted): + # --allow-tool github(download_workflow_run_artifact) + # --allow-tool github(get_code_scanning_alert) + # --allow-tool github(get_commit) + # --allow-tool github(get_dependabot_alert) + # --allow-tool github(get_discussion) + # --allow-tool github(get_discussion_comments) + # --allow-tool github(get_file_contents) + # --allow-tool github(get_issue) + # --allow-tool github(get_issue_comments) + # --allow-tool github(get_job_logs) + # --allow-tool github(get_label) + # --allow-tool github(get_latest_release) + # --allow-tool github(get_me) + # --allow-tool github(get_notification_details) + # --allow-tool github(get_pull_request) + # --allow-tool github(get_pull_request_comments) + # --allow-tool github(get_pull_request_diff) + # --allow-tool github(get_pull_request_files) + # --allow-tool github(get_pull_request_review_comments) + # --allow-tool github(get_pull_request_reviews) + # --allow-tool github(get_pull_request_status) + # --allow-tool github(get_release_by_tag) + # --allow-tool github(get_secret_scanning_alert) + # --allow-tool github(get_tag) + # --allow-tool github(get_workflow_run) + # --allow-tool github(get_workflow_run_logs) + # --allow-tool github(get_workflow_run_usage) + # --allow-tool github(list_branches) + # --allow-tool github(list_code_scanning_alerts) + # --allow-tool github(list_commits) + # --allow-tool github(list_dependabot_alerts) + # --allow-tool github(list_discussion_categories) + # --allow-tool github(list_discussions) + # --allow-tool github(list_issue_types) + # --allow-tool github(list_issues) + # --allow-tool github(list_label) + # --allow-tool github(list_notifications) + # --allow-tool github(list_pull_requests) + # --allow-tool github(list_releases) + # --allow-tool github(list_secret_scanning_alerts) + # --allow-tool github(list_starred_repositories) + # --allow-tool github(list_sub_issues) + # --allow-tool github(list_tags) + # --allow-tool github(list_workflow_jobs) + # --allow-tool github(list_workflow_run_artifacts) + # --allow-tool github(list_workflow_runs) + # --allow-tool github(list_workflows) + # --allow-tool github(pull_request_read) + # --allow-tool github(search_code) + # --allow-tool github(search_issues) + # --allow-tool github(search_orgs) + # --allow-tool github(search_pull_requests) + # --allow-tool github(search_repositories) + # --allow-tool github(search_users) + # --allow-tool safe_outputs + timeout-minutes: 20 run: | set -o pipefail - INSTRUCTION=$(cat $GITHUB_AW_PROMPT) - mkdir -p $CODEX_HOME/logs - codex exec --full-auto --skip-git-repo-check "$INSTRUCTION" 2>&1 | tee /tmp/gh-aw/agent-stdio.log + COPILOT_CLI_INSTRUCTION=$(cat /tmp/gh-aw/aw-prompts/prompt.txt) + copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/.copilot/logs/ --allow-tool 'github(download_workflow_run_artifact)' --allow-tool 'github(get_code_scanning_alert)' --allow-tool 'github(get_commit)' --allow-tool 'github(get_dependabot_alert)' --allow-tool 'github(get_discussion)' --allow-tool 'github(get_discussion_comments)' --allow-tool 'github(get_file_contents)' --allow-tool 'github(get_issue)' --allow-tool 'github(get_issue_comments)' --allow-tool 'github(get_job_logs)' --allow-tool 'github(get_label)' --allow-tool 'github(get_latest_release)' --allow-tool 'github(get_me)' --allow-tool 'github(get_notification_details)' --allow-tool 'github(get_pull_request)' --allow-tool 'github(get_pull_request_comments)' --allow-tool 'github(get_pull_request_diff)' --allow-tool 'github(get_pull_request_files)' --allow-tool 'github(get_pull_request_review_comments)' --allow-tool 'github(get_pull_request_reviews)' --allow-tool 'github(get_pull_request_status)' --allow-tool 'github(get_release_by_tag)' --allow-tool 'github(get_secret_scanning_alert)' --allow-tool 'github(get_tag)' --allow-tool 'github(get_workflow_run)' --allow-tool 'github(get_workflow_run_logs)' --allow-tool 'github(get_workflow_run_usage)' --allow-tool 'github(list_branches)' --allow-tool 'github(list_code_scanning_alerts)' --allow-tool 'github(list_commits)' --allow-tool 'github(list_dependabot_alerts)' --allow-tool 'github(list_discussion_categories)' --allow-tool 'github(list_discussions)' --allow-tool 'github(list_issue_types)' --allow-tool 'github(list_issues)' --allow-tool 'github(list_label)' --allow-tool 'github(list_notifications)' --allow-tool 'github(list_pull_requests)' --allow-tool 'github(list_releases)' --allow-tool 'github(list_secret_scanning_alerts)' --allow-tool 'github(list_starred_repositories)' --allow-tool 'github(list_sub_issues)' --allow-tool 'github(list_tags)' --allow-tool 'github(list_workflow_jobs)' --allow-tool 'github(list_workflow_run_artifacts)' --allow-tool 'github(list_workflow_runs)' --allow-tool 'github(list_workflows)' --allow-tool 'github(pull_request_read)' --allow-tool 'github(search_code)' --allow-tool 'github(search_issues)' --allow-tool 'github(search_orgs)' --allow-tool 'github(search_pull_requests)' --allow-tool 'github(search_repositories)' --allow-tool 'github(search_users)' --allow-tool safe_outputs --prompt "$COPILOT_CLI_INSTRUCTION" 2>&1 | tee /tmp/gh-aw/agent-stdio.log env: - CODEX_API_KEY: ${{ secrets.CODEX_API_KEY || secrets.OPENAI_API_KEY }} - CODEX_HOME: /tmp/gh-aw/mcp-config - GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} - GITHUB_AW_MCP_CONFIG: /tmp/gh-aw/mcp-config/config.toml + COPILOT_AGENT_RUNNER_TYPE: STANDALONE + GITHUB_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json GITHUB_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} GITHUB_AW_SAFE_OUTPUTS_STAGED: true GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }} - RUST_LOG: trace,hyper_util=info,mio=info,reqwest=info,os_info=info,codex_otel=warn,codex_core=debug,ocodex_exec=debug + GITHUB_TOKEN: ${{ secrets.COPILOT_CLI_TOKEN }} + XDG_CONFIG_HOME: /home/runner - name: Upload Safe Outputs if: always() uses: actions/upload-artifact@v4 @@ -1962,17 +2096,16 @@ jobs: await main(); env: - GITHUB_AW_SECRET_NAMES: 'CODEX_API_KEY,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN,OPENAI_API_KEY' - SECRET_CODEX_API_KEY: ${{ secrets.CODEX_API_KEY }} + GITHUB_AW_SECRET_NAMES: 'COPILOT_CLI_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN' + SECRET_COPILOT_CLI_TOKEN: ${{ secrets.COPILOT_CLI_TOKEN }} SECRET_GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - SECRET_OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - name: Upload engine output files uses: actions/upload-artifact@v4 with: name: agent_outputs path: | - /tmp/gh-aw/mcp-config/logs/ + /tmp/gh-aw/.copilot/logs/ if-no-files-found: ignore - name: Upload MCP logs if: always() @@ -1985,225 +2118,666 @@ jobs: if: always() uses: actions/github-script@v8 env: - GITHUB_AW_AGENT_OUTPUT: /tmp/gh-aw/agent-stdio.log + GITHUB_AW_AGENT_OUTPUT: /tmp/gh-aw/.copilot/logs/ with: script: | function main() { const fs = require("fs"); + const path = require("path"); try { - const logFile = process.env.GITHUB_AW_AGENT_OUTPUT; - if (!logFile) { + const logPath = process.env.GITHUB_AW_AGENT_OUTPUT; + if (!logPath) { core.info("No agent log file specified"); return; } - if (!fs.existsSync(logFile)) { - core.info(`Log file not found: ${logFile}`); + if (!fs.existsSync(logPath)) { + core.info(`Log path not found: ${logPath}`); return; } - const content = fs.readFileSync(logFile, "utf8"); - const parsedLog = parseCodexLog(content); + let content = ""; + const stat = fs.statSync(logPath); + if (stat.isDirectory()) { + const files = fs.readdirSync(logPath); + const logFiles = files.filter(file => file.endsWith(".log") || file.endsWith(".txt")); + if (logFiles.length === 0) { + core.info(`No log files found in directory: ${logPath}`); + return; + } + logFiles.sort(); + for (const file of logFiles) { + const filePath = path.join(logPath, file); + const fileContent = fs.readFileSync(filePath, "utf8"); + content += fileContent; + if (content.length > 0 && !content.endsWith("\n")) { + content += "\n"; + } + } + } else { + content = fs.readFileSync(logPath, "utf8"); + } + const parsedLog = parseCopilotLog(content); if (parsedLog) { core.info(parsedLog); core.summary.addRaw(parsedLog).write(); - core.info("Codex log parsed successfully"); + core.info("Copilot log parsed successfully"); } else { - core.error("Failed to parse Codex log"); + core.error("Failed to parse Copilot log"); } } catch (error) { core.setFailed(error instanceof Error ? error : String(error)); } } - function parseCodexLog(logContent) { + function parseCopilotLog(logContent) { try { - const lines = logContent.split("\n"); - const LOOKAHEAD_WINDOW = 50; + let logEntries; + try { + logEntries = JSON.parse(logContent); + if (!Array.isArray(logEntries)) { + throw new Error("Not a JSON array"); + } + } catch (jsonArrayError) { + const debugLogEntries = parseDebugLogFormat(logContent); + if (debugLogEntries && debugLogEntries.length > 0) { + logEntries = debugLogEntries; + } else { + logEntries = []; + const lines = logContent.split("\n"); + for (const line of lines) { + const trimmedLine = line.trim(); + if (trimmedLine === "") { + continue; + } + if (trimmedLine.startsWith("[{")) { + try { + const arrayEntries = JSON.parse(trimmedLine); + if (Array.isArray(arrayEntries)) { + logEntries.push(...arrayEntries); + continue; + } + } catch (arrayParseError) { + continue; + } + } + if (!trimmedLine.startsWith("{")) { + continue; + } + try { + const jsonEntry = JSON.parse(trimmedLine); + logEntries.push(jsonEntry); + } catch (jsonLineError) { + continue; + } + } + } + } + if (!Array.isArray(logEntries) || logEntries.length === 0) { + return "## Agent Log Summary\n\nLog format not recognized as Copilot JSON array or JSONL.\n"; + } + const toolUsePairs = new Map(); + for (const entry of logEntries) { + if (entry.type === "user" && entry.message?.content) { + for (const content of entry.message.content) { + if (content.type === "tool_result" && content.tool_use_id) { + toolUsePairs.set(content.tool_use_id, content); + } + } + } + } let markdown = ""; - markdown += "## 🤖 Reasoning\n\n"; - let inThinkingSection = false; - for (let i = 0; i < lines.length; i++) { - const line = lines[i]; - if ( - line.includes("OpenAI Codex") || - line.startsWith("--------") || - line.includes("workdir:") || - line.includes("model:") || - line.includes("provider:") || - line.includes("approval:") || - line.includes("sandbox:") || - line.includes("reasoning effort:") || - line.includes("reasoning summaries:") || - line.includes("tokens used:") || - line.includes("DEBUG codex") || - line.includes("INFO codex") || - line.match(/^\d{4}-\d{2}-\d{2}T[\d:.]+Z\s+(DEBUG|INFO|WARN|ERROR)/) - ) { - continue; + const initEntry = logEntries.find(entry => entry.type === "system" && entry.subtype === "init"); + if (initEntry) { + markdown += "## 🚀 Initialization\n\n"; + markdown += formatInitializationSummary(initEntry); + markdown += "\n"; + } + markdown += "\n## 🤖 Reasoning\n\n"; + for (const entry of logEntries) { + if (entry.type === "assistant" && entry.message?.content) { + for (const content of entry.message.content) { + if (content.type === "text" && content.text) { + const text = content.text.trim(); + if (text && text.length > 0) { + markdown += text + "\n\n"; + } + } else if (content.type === "tool_use") { + const toolResult = toolUsePairs.get(content.id); + const toolMarkdown = formatToolUseWithDetails(content, toolResult); + if (toolMarkdown) { + markdown += toolMarkdown; + } + } + } } - if (line.trim() === "thinking") { - inThinkingSection = true; - continue; + } + markdown += "## 🤖 Commands and Tools\n\n"; + const commandSummary = []; + for (const entry of logEntries) { + if (entry.type === "assistant" && entry.message?.content) { + for (const content of entry.message.content) { + if (content.type === "tool_use") { + const toolName = content.name; + const input = content.input || {}; + if (["Read", "Write", "Edit", "MultiEdit", "LS", "Grep", "Glob", "TodoWrite"].includes(toolName)) { + continue; + } + const toolResult = toolUsePairs.get(content.id); + let statusIcon = "❓"; + if (toolResult) { + statusIcon = toolResult.is_error === true ? "❌" : "✅"; + } + if (toolName === "Bash") { + const formattedCommand = formatBashCommand(input.command || ""); + commandSummary.push(`* ${statusIcon} \`${formattedCommand}\``); + } else if (toolName.startsWith("mcp__")) { + const mcpName = formatMcpName(toolName); + commandSummary.push(`* ${statusIcon} \`${mcpName}(...)\``); + } else { + commandSummary.push(`* ${statusIcon} ${toolName}`); + } + } + } } - const toolMatch = line.match(/^tool\s+(\w+)\.(\w+)\(/); - if (toolMatch) { - inThinkingSection = false; - const server = toolMatch[1]; - const toolName = toolMatch[2]; - let statusIcon = "❓"; - for (let j = i + 1; j < Math.min(i + LOOKAHEAD_WINDOW, lines.length); j++) { - const nextLine = lines[j]; - if (nextLine.includes(`${server}.${toolName}(`) && nextLine.includes("success in")) { - statusIcon = "✅"; - break; - } else if (nextLine.includes(`${server}.${toolName}(`) && (nextLine.includes("failed in") || nextLine.includes("error"))) { - statusIcon = "❌"; + } + if (commandSummary.length > 0) { + for (const cmd of commandSummary) { + markdown += `${cmd}\n`; + } + } else { + markdown += "No commands or tools used.\n"; + } + markdown += "\n## 📊 Information\n\n"; + const lastEntry = logEntries[logEntries.length - 1]; + if (lastEntry && (lastEntry.num_turns || lastEntry.duration_ms || lastEntry.total_cost_usd || lastEntry.usage)) { + if (lastEntry.num_turns) { + markdown += `**Turns:** ${lastEntry.num_turns}\n\n`; + } + if (lastEntry.duration_ms) { + const durationSec = Math.round(lastEntry.duration_ms / 1000); + const minutes = Math.floor(durationSec / 60); + const seconds = durationSec % 60; + markdown += `**Duration:** ${minutes}m ${seconds}s\n\n`; + } + if (lastEntry.total_cost_usd) { + markdown += `**Total Cost:** $${lastEntry.total_cost_usd.toFixed(4)}\n\n`; + } + const isPremiumModel = + initEntry && initEntry.model_info && initEntry.model_info.billing && initEntry.model_info.billing.is_premium === true; + if (isPremiumModel && lastEntry.num_turns) { + markdown += `**Premium Requests Consumed:** ${lastEntry.num_turns}\n\n`; + } + if (lastEntry.usage) { + const usage = lastEntry.usage; + if (usage.input_tokens || usage.output_tokens) { + markdown += `**Token Usage:**\n`; + if (usage.input_tokens) markdown += `- Input: ${usage.input_tokens.toLocaleString()}\n`; + if (usage.cache_creation_input_tokens) markdown += `- Cache Creation: ${usage.cache_creation_input_tokens.toLocaleString()}\n`; + if (usage.cache_read_input_tokens) markdown += `- Cache Read: ${usage.cache_read_input_tokens.toLocaleString()}\n`; + if (usage.output_tokens) markdown += `- Output: ${usage.output_tokens.toLocaleString()}\n`; + markdown += "\n"; + } + } + } + return markdown; + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + return `## Agent Log Summary\n\nError parsing Copilot log (tried both JSON array and JSONL formats): ${errorMessage}\n`; + } + } + function parseDebugLogFormat(logContent) { + const entries = []; + const lines = logContent.split("\n"); + let model = "unknown"; + let sessionId = null; + let modelInfo = null; + const modelMatch = logContent.match(/Starting Copilot CLI: ([\d.]+)/); + if (modelMatch) { + sessionId = `copilot-${modelMatch[1]}-${Date.now()}`; + } + const gotModelInfoIndex = logContent.indexOf("[DEBUG] Got model info: {"); + if (gotModelInfoIndex !== -1) { + const jsonStart = logContent.indexOf("{", gotModelInfoIndex); + if (jsonStart !== -1) { + let braceCount = 0; + let inString = false; + let escapeNext = false; + let jsonEnd = -1; + for (let i = jsonStart; i < logContent.length; i++) { + const char = logContent[i]; + if (escapeNext) { + escapeNext = false; + continue; + } + if (char === "\\") { + escapeNext = true; + continue; + } + if (char === '"' && !escapeNext) { + inString = !inString; + continue; + } + if (inString) continue; + if (char === "{") { + braceCount++; + } else if (char === "}") { + braceCount--; + if (braceCount === 0) { + jsonEnd = i + 1; break; } } - markdown += `${statusIcon} ${server}::${toolName}(...)\n\n`; - continue; } - if (inThinkingSection && line.trim().length > 20 && !line.match(/^\d{4}-\d{2}-\d{2}T/)) { - const trimmed = line.trim(); - markdown += `${trimmed}\n\n`; + if (jsonEnd !== -1) { + const modelInfoJson = logContent.substring(jsonStart, jsonEnd); + try { + modelInfo = JSON.parse(modelInfoJson); + } catch (e) { + } } } - markdown += "## 🤖 Commands and Tools\n\n"; - for (let i = 0; i < lines.length; i++) { - const line = lines[i]; - const toolMatch = line.match(/^\[.*?\]\s+tool\s+(\w+)\.(\w+)\((.+)\)/) || line.match(/ToolCall:\s+(\w+)__(\w+)\s+(\{.+\})/); - const bashMatch = line.match(/^\[.*?\]\s+exec\s+bash\s+-lc\s+'([^']+)'/); - if (toolMatch) { - const server = toolMatch[1]; - const toolName = toolMatch[2]; - const params = toolMatch[3]; - let statusIcon = "❓"; - let response = ""; - let isError = false; - for (let j = i + 1; j < Math.min(i + LOOKAHEAD_WINDOW, lines.length); j++) { - const nextLine = lines[j]; - if (nextLine.includes(`${server}.${toolName}(`) && (nextLine.includes("success in") || nextLine.includes("failed in"))) { - isError = nextLine.includes("failed in"); - statusIcon = isError ? "❌" : "✅"; - let jsonLines = []; - let braceCount = 0; - let inJson = false; - for (let k = j + 1; k < Math.min(j + 30, lines.length); k++) { - const respLine = lines[k]; - if (respLine.includes("tool ") || respLine.includes("ToolCall:") || respLine.includes("tokens used")) { - break; - } - for (const char of respLine) { - if (char === "{") { - braceCount++; - inJson = true; - } else if (char === "}") { - braceCount--; + } + let inDataBlock = false; + let currentJsonLines = []; + let turnCount = 0; + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + if (line.includes("[DEBUG] data:")) { + inDataBlock = true; + currentJsonLines = []; + continue; + } + if (inDataBlock) { + const hasTimestamp = line.match(/^\d{4}-\d{2}-\d{2}T[\d:.]+Z /); + const hasDebug = line.includes("[DEBUG]"); + if (hasTimestamp && !hasDebug) { + if (currentJsonLines.length > 0) { + try { + const jsonStr = currentJsonLines.join("\n"); + const jsonData = JSON.parse(jsonStr); + if (jsonData.model) { + model = jsonData.model; + } + if (jsonData.choices && Array.isArray(jsonData.choices)) { + for (const choice of jsonData.choices) { + if (choice.message) { + const message = choice.message; + const content = []; + const toolResults = []; + if (message.content && message.content.trim()) { + content.push({ + type: "text", + text: message.content, + }); + } + if (message.tool_calls && Array.isArray(message.tool_calls)) { + for (const toolCall of message.tool_calls) { + if (toolCall.function) { + let toolName = toolCall.function.name; + let args = {}; + if (toolName.startsWith("github-")) { + toolName = "mcp__github__" + toolName.substring(7); + } else if (toolName === "bash") { + toolName = "Bash"; + } + try { + args = JSON.parse(toolCall.function.arguments); + } catch (e) { + args = {}; + } + const toolId = toolCall.id || `tool_${Date.now()}_${Math.random()}`; + content.push({ + type: "tool_use", + id: toolId, + name: toolName, + input: args, + }); + toolResults.push({ + type: "tool_result", + tool_use_id: toolId, + content: "", + is_error: false, + }); + } + } + } + if (content.length > 0) { + entries.push({ + type: "assistant", + message: { content }, + }); + turnCount++; + if (toolResults.length > 0) { + entries.push({ + type: "user", + message: { content: toolResults }, + }); + } + } } } - if (inJson) { - jsonLines.push(respLine); - } - if (inJson && braceCount === 0) { - break; + if (jsonData.usage) { + const resultEntry = { + type: "result", + num_turns: turnCount, + usage: jsonData.usage, + }; + entries._lastResult = resultEntry; } } - response = jsonLines.join("\n"); - break; + } catch (e) { } } - markdown += formatCodexToolCall(server, toolName, params, response, statusIcon); - } else if (bashMatch) { - const command = bashMatch[1]; - let statusIcon = "❓"; - let response = ""; - let isError = false; - for (let j = i + 1; j < Math.min(i + LOOKAHEAD_WINDOW, lines.length); j++) { - const nextLine = lines[j]; - if (nextLine.includes("bash -lc") && (nextLine.includes("succeeded in") || nextLine.includes("failed in"))) { - isError = nextLine.includes("failed in"); - statusIcon = isError ? "❌" : "✅"; - let responseLines = []; - for (let k = j + 1; k < Math.min(j + 20, lines.length); k++) { - const respLine = lines[k]; - if ( - respLine.includes("tool ") || - respLine.includes("exec ") || - respLine.includes("ToolCall:") || - respLine.includes("tokens used") || - respLine.includes("thinking") - ) { - break; + inDataBlock = false; + currentJsonLines = []; + } else { + const cleanLine = line.replace(/^\d{4}-\d{2}-\d{2}T[\d:.]+Z \[DEBUG\] /, ""); + currentJsonLines.push(cleanLine); + } + } + } + if (inDataBlock && currentJsonLines.length > 0) { + try { + const jsonStr = currentJsonLines.join("\n"); + const jsonData = JSON.parse(jsonStr); + if (jsonData.model) { + model = jsonData.model; + } + if (jsonData.choices && Array.isArray(jsonData.choices)) { + for (const choice of jsonData.choices) { + if (choice.message) { + const message = choice.message; + const content = []; + const toolResults = []; + if (message.content && message.content.trim()) { + content.push({ + type: "text", + text: message.content, + }); + } + if (message.tool_calls && Array.isArray(message.tool_calls)) { + for (const toolCall of message.tool_calls) { + if (toolCall.function) { + let toolName = toolCall.function.name; + let args = {}; + if (toolName.startsWith("github-")) { + toolName = "mcp__github__" + toolName.substring(7); + } else if (toolName === "bash") { + toolName = "Bash"; + } + try { + args = JSON.parse(toolCall.function.arguments); + } catch (e) { + args = {}; + } + const toolId = toolCall.id || `tool_${Date.now()}_${Math.random()}`; + content.push({ + type: "tool_use", + id: toolId, + name: toolName, + input: args, + }); + toolResults.push({ + type: "tool_result", + tool_use_id: toolId, + content: "", + is_error: false, + }); + } + } + } + if (content.length > 0) { + entries.push({ + type: "assistant", + message: { content }, + }); + turnCount++; + if (toolResults.length > 0) { + entries.push({ + type: "user", + message: { content: toolResults }, + }); } - responseLines.push(respLine); } - response = responseLines.join("\n").trim(); - break; } } - markdown += formatCodexBashCall(command, response, statusIcon); + if (jsonData.usage) { + const resultEntry = { + type: "result", + num_turns: turnCount, + usage: jsonData.usage, + }; + entries._lastResult = resultEntry; + } } + } catch (e) { } - markdown += "\n## 📊 Information\n\n"; - let totalTokens = 0; - const tokenCountMatches = logContent.matchAll(/total_tokens:\s*(\d+)/g); - for (const match of tokenCountMatches) { - const tokens = parseInt(match[1]); - totalTokens = Math.max(totalTokens, tokens); + } + if (entries.length > 0) { + const initEntry = { + type: "system", + subtype: "init", + session_id: sessionId, + model: model, + tools: [], + }; + if (modelInfo) { + initEntry.model_info = modelInfo; } - const finalTokensMatch = logContent.match(/tokens used\n([\d,]+)/); - if (finalTokensMatch) { - totalTokens = parseInt(finalTokensMatch[1].replace(/,/g, "")); + entries.unshift(initEntry); + if (entries._lastResult) { + entries.push(entries._lastResult); + delete entries._lastResult; } - if (totalTokens > 0) { - markdown += `**Total Tokens Used:** ${totalTokens.toLocaleString()}\n\n`; + } + return entries; + } + function formatInitializationSummary(initEntry) { + let markdown = ""; + if (initEntry.model) { + markdown += `**Model:** ${initEntry.model}\n\n`; + } + if (initEntry.model_info) { + const modelInfo = initEntry.model_info; + if (modelInfo.name) { + markdown += `**Model Name:** ${modelInfo.name}`; + if (modelInfo.vendor) { + markdown += ` (${modelInfo.vendor})`; + } + markdown += "\n\n"; + } + if (modelInfo.billing) { + const billing = modelInfo.billing; + if (billing.is_premium === true) { + markdown += `**Premium Model:** Yes`; + if (billing.multiplier && billing.multiplier !== 1) { + markdown += ` (${billing.multiplier}x cost multiplier)`; + } + markdown += "\n"; + if (billing.restricted_to && Array.isArray(billing.restricted_to) && billing.restricted_to.length > 0) { + markdown += `**Required Plans:** ${billing.restricted_to.join(", ")}\n`; + } + markdown += "\n"; + } else if (billing.is_premium === false) { + markdown += `**Premium Model:** No\n\n`; + } } - const toolCalls = (logContent.match(/ToolCall:\s+\w+__\w+/g) || []).length; - if (toolCalls > 0) { - markdown += `**Tool Calls:** ${toolCalls}\n\n`; + } + if (initEntry.session_id) { + markdown += `**Session ID:** ${initEntry.session_id}\n\n`; + } + if (initEntry.cwd) { + const cleanCwd = initEntry.cwd.replace(/^\/home\/runner\/work\/[^\/]+\/[^\/]+/, "."); + markdown += `**Working Directory:** ${cleanCwd}\n\n`; + } + if (initEntry.mcp_servers && Array.isArray(initEntry.mcp_servers)) { + markdown += "**MCP Servers:**\n"; + for (const server of initEntry.mcp_servers) { + const statusIcon = server.status === "connected" ? "✅" : server.status === "failed" ? "❌" : "❓"; + markdown += `- ${statusIcon} ${server.name} (${server.status})\n`; } - return markdown; - } catch (error) { - core.error(`Error parsing Codex log: ${error}`); - return "## 🤖 Commands and Tools\n\nError parsing log content.\n\n## 🤖 Reasoning\n\nUnable to parse reasoning from log.\n\n"; + markdown += "\n"; + } + if (initEntry.tools && Array.isArray(initEntry.tools)) { + markdown += "**Available Tools:**\n"; + const categories = { + Core: [], + "File Operations": [], + "Git/GitHub": [], + MCP: [], + Other: [], + }; + for (const tool of initEntry.tools) { + if (["Task", "Bash", "BashOutput", "KillBash", "ExitPlanMode"].includes(tool)) { + categories["Core"].push(tool); + } else if (["Read", "Edit", "MultiEdit", "Write", "LS", "Grep", "Glob", "NotebookEdit"].includes(tool)) { + categories["File Operations"].push(tool); + } else if (tool.startsWith("mcp__github__")) { + categories["Git/GitHub"].push(formatMcpName(tool)); + } else if (tool.startsWith("mcp__") || ["ListMcpResourcesTool", "ReadMcpResourceTool"].includes(tool)) { + categories["MCP"].push(tool.startsWith("mcp__") ? formatMcpName(tool) : tool); + } else { + categories["Other"].push(tool); + } + } + for (const [category, tools] of Object.entries(categories)) { + if (tools.length > 0) { + markdown += `- **${category}:** ${tools.length} tools\n`; + if (tools.length <= 5) { + markdown += ` - ${tools.join(", ")}\n`; + } else { + markdown += ` - ${tools.slice(0, 3).join(", ")}, and ${tools.length - 3} more\n`; + } + } + } + markdown += "\n"; } + return markdown; } - function formatCodexToolCall(server, toolName, params, response, statusIcon) { - const summary = `${statusIcon} ${server}::${toolName}`; - if (!response || response.trim() === "") { - return `${summary}\n\n`; + function formatToolUseWithDetails(toolUse, toolResult) { + const toolName = toolUse.name; + const input = toolUse.input || {}; + if (toolName === "TodoWrite") { + return ""; + } + function getStatusIcon() { + if (toolResult) { + return toolResult.is_error === true ? "❌" : "✅"; + } + return "❓"; } + const statusIcon = getStatusIcon(); + let summary = ""; let details = ""; - if (params && params.trim()) { - details += "**Parameters:**\n\n"; - details += "``````json\n"; - details += params; - details += "\n``````\n\n"; - } - if (response && response.trim()) { - details += "**Response:**\n\n"; - details += "``````json\n"; - details += response; - details += "\n``````"; - } - return `
\n${summary}\n\n${details}\n
\n\n`; - } - function formatCodexBashCall(command, response, statusIcon) { - const summary = `${statusIcon} bash: ${truncateString(command, 60)}`; - if (!response || response.trim() === "") { + if (toolResult && toolResult.content) { + if (typeof toolResult.content === "string") { + details = toolResult.content; + } else if (Array.isArray(toolResult.content)) { + details = toolResult.content.map(c => (typeof c === "string" ? c : c.text || "")).join("\n"); + } + } + switch (toolName) { + case "Bash": + const command = input.command || ""; + const description = input.description || ""; + const formattedCommand = formatBashCommand(command); + if (description) { + summary = `${statusIcon} ${description}: ${formattedCommand}`; + } else { + summary = `${statusIcon} ${formattedCommand}`; + } + break; + case "Read": + const filePath = input.file_path || input.path || ""; + const relativePath = filePath.replace(/^\/[^\/]*\/[^\/]*\/[^\/]*\/[^\/]*\//, ""); + summary = `${statusIcon} Read ${relativePath}`; + break; + case "Write": + case "Edit": + case "MultiEdit": + const writeFilePath = input.file_path || input.path || ""; + const writeRelativePath = writeFilePath.replace(/^\/[^\/]*\/[^\/]*\/[^\/]*\/[^\/]*\//, ""); + summary = `${statusIcon} Write ${writeRelativePath}`; + break; + case "Grep": + case "Glob": + const query = input.query || input.pattern || ""; + summary = `${statusIcon} Search for ${truncateString(query, 80)}`; + break; + case "LS": + const lsPath = input.path || ""; + const lsRelativePath = lsPath.replace(/^\/[^\/]*\/[^\/]*\/[^\/]*\/[^\/]*\//, ""); + summary = `${statusIcon} LS: ${lsRelativePath || lsPath}`; + break; + default: + if (toolName.startsWith("mcp__")) { + const mcpName = formatMcpName(toolName); + const params = formatMcpParameters(input); + summary = `${statusIcon} ${mcpName}(${params})`; + } else { + const keys = Object.keys(input); + if (keys.length > 0) { + const mainParam = keys.find(k => ["query", "command", "path", "file_path", "content"].includes(k)) || keys[0]; + const value = String(input[mainParam] || ""); + if (value) { + summary = `${statusIcon} ${toolName}: ${truncateString(value, 100)}`; + } else { + summary = `${statusIcon} ${toolName}`; + } + } else { + summary = `${statusIcon} ${toolName}`; + } + } + } + if (details && details.trim()) { + let detailsContent = ""; + const inputKeys = Object.keys(input); + if (inputKeys.length > 0) { + detailsContent += "**Parameters:**\n\n"; + detailsContent += "``````json\n"; + detailsContent += JSON.stringify(input, null, 2); + detailsContent += "\n``````\n\n"; + } + detailsContent += "**Response:**\n\n"; + detailsContent += "``````\n"; + detailsContent += details; + detailsContent += "\n``````"; + return `
\n${summary}\n\n${detailsContent}\n
\n\n`; + } else { return `${summary}\n\n`; } - let details = ""; - details += "**Command:**\n\n"; - details += "``````bash\n"; - details += command; - details += "\n``````\n\n"; - if (response && response.trim()) { - details += "**Output:**\n\n"; - details += "``````\n"; - details += response; - details += "\n``````"; - } - return `
\n${summary}\n\n${details}\n
\n\n`; + } + function formatMcpName(toolName) { + if (toolName.startsWith("mcp__")) { + const parts = toolName.split("__"); + if (parts.length >= 3) { + const provider = parts[1]; + const method = parts.slice(2).join("_"); + return `${provider}::${method}`; + } + } + return toolName; + } + function formatMcpParameters(input) { + const keys = Object.keys(input); + if (keys.length === 0) return ""; + const paramStrs = []; + for (const key of keys.slice(0, 4)) { + const value = String(input[key] || ""); + paramStrs.push(`${key}: ${truncateString(value, 40)}`); + } + if (keys.length > 4) { + paramStrs.push("..."); + } + return paramStrs.join(", "); + } + function formatBashCommand(command) { + if (!command) return ""; + let formatted = command.replace(/\n/g, " ").replace(/\r/g, " ").replace(/\t/g, " ").replace(/\s+/g, " ").trim(); + formatted = formatted.replace(/`/g, "\\`"); + const maxLength = 80; + if (formatted.length > maxLength) { + formatted = formatted.substring(0, maxLength) + "..."; + } + return formatted; } function truncateString(str, maxLength) { if (!str) return ""; @@ -2211,7 +2785,15 @@ jobs: return str.substring(0, maxLength) + "..."; } if (typeof module !== "undefined" && module.exports) { - module.exports = { parseCodexLog, formatCodexToolCall, formatCodexBashCall, truncateString }; + module.exports = { + parseCopilotLog, + formatInitializationSummary, + formatToolUseWithDetails, + formatBashCommand, + truncateString, + formatMcpName, + formatMcpParameters, + }; } main(); - name: Upload Agent Stdio @@ -2225,8 +2807,8 @@ jobs: if: always() uses: actions/github-script@v8 env: - GITHUB_AW_AGENT_OUTPUT: /tmp/gh-aw/agent-stdio.log - GITHUB_AW_ERROR_PATTERNS: "[{\"pattern\":\"::(error)(?:\\\\s+[^:]*)?::(.+)\",\"level_group\":1,\"message_group\":2,\"description\":\"GitHub Actions workflow command - error\"},{\"pattern\":\"::(warning)(?:\\\\s+[^:]*)?::(.+)\",\"level_group\":1,\"message_group\":2,\"description\":\"GitHub Actions workflow command - warning\"},{\"pattern\":\"::(notice)(?:\\\\s+[^:]*)?::(.+)\",\"level_group\":1,\"message_group\":2,\"description\":\"GitHub Actions workflow command - notice\"},{\"pattern\":\"(\\\\d{4}-\\\\d{2}-\\\\d{2}T[\\\\d:.]+Z)\\\\s+(ERROR)\\\\s+(.+)\",\"level_group\":2,\"message_group\":3,\"description\":\"Codex ERROR messages with timestamp\"},{\"pattern\":\"(\\\\d{4}-\\\\d{2}-\\\\d{2}T[\\\\d:.]+Z)\\\\s+(WARN|WARNING)\\\\s+(.+)\",\"level_group\":2,\"message_group\":3,\"description\":\"Codex warning messages with timestamp\"},{\"pattern\":\"access denied.*only authorized.*can trigger.*workflow\",\"level_group\":0,\"message_group\":0,\"description\":\"Permission denied - workflow access restriction\"},{\"pattern\":\"access denied.*user.*not authorized\",\"level_group\":0,\"message_group\":0,\"description\":\"Permission denied - user not authorized\"},{\"pattern\":\"repository permission check failed\",\"level_group\":0,\"message_group\":0,\"description\":\"Repository permission check failure\"},{\"pattern\":\"configuration error.*required permissions not specified\",\"level_group\":0,\"message_group\":0,\"description\":\"Configuration error - missing permissions\"},{\"pattern\":\"\\\\berror\\\\b.*permission.*denied\",\"level_group\":0,\"message_group\":0,\"description\":\"Permission denied error (requires error context)\"},{\"pattern\":\"\\\\berror\\\\b.*unauthorized\",\"level_group\":0,\"message_group\":0,\"description\":\"Unauthorized error (requires error context)\"},{\"pattern\":\"\\\\berror\\\\b.*forbidden\",\"level_group\":0,\"message_group\":0,\"description\":\"Forbidden error (requires error context)\"},{\"pattern\":\"\\\\berror\\\\b.*access.*restricted\",\"level_group\":0,\"message_group\":0,\"description\":\"Access restricted error (requires error context)\"},{\"pattern\":\"\\\\berror\\\\b.*insufficient.*permission\",\"level_group\":0,\"message_group\":0,\"description\":\"Insufficient permissions error (requires error context)\"},{\"pattern\":\"failed in.*permission\",\"level_group\":0,\"message_group\":0,\"description\":\"Codex tool failed due to permission error\"},{\"pattern\":\"\\\\berror\\\\b in.*permission\",\"level_group\":0,\"message_group\":0,\"description\":\"Codex tool error due to permission issue\"}]" + GITHUB_AW_AGENT_OUTPUT: /tmp/gh-aw/.copilot/logs/ + GITHUB_AW_ERROR_PATTERNS: "[{\"pattern\":\"::(error)(?:\\\\s+[^:]*)?::(.+)\",\"level_group\":1,\"message_group\":2,\"description\":\"GitHub Actions workflow command - error\"},{\"pattern\":\"::(warning)(?:\\\\s+[^:]*)?::(.+)\",\"level_group\":1,\"message_group\":2,\"description\":\"GitHub Actions workflow command - warning\"},{\"pattern\":\"::(notice)(?:\\\\s+[^:]*)?::(.+)\",\"level_group\":1,\"message_group\":2,\"description\":\"GitHub Actions workflow command - notice\"},{\"pattern\":\"(\\\\d{4}-\\\\d{2}-\\\\d{2}T\\\\d{2}:\\\\d{2}:\\\\d{2}\\\\.\\\\d{3}Z)\\\\s+\\\\[(ERROR)\\\\]\\\\s+(.+)\",\"level_group\":2,\"message_group\":3,\"description\":\"Copilot CLI timestamped ERROR messages\"},{\"pattern\":\"(\\\\d{4}-\\\\d{2}-\\\\d{2}T\\\\d{2}:\\\\d{2}:\\\\d{2}\\\\.\\\\d{3}Z)\\\\s+\\\\[(WARN|WARNING)\\\\]\\\\s+(.+)\",\"level_group\":2,\"message_group\":3,\"description\":\"Copilot CLI timestamped WARNING messages\"},{\"pattern\":\"\\\\[(\\\\d{4}-\\\\d{2}-\\\\d{2}T\\\\d{2}:\\\\d{2}:\\\\d{2}\\\\.\\\\d{3}Z)\\\\]\\\\s+(CRITICAL|ERROR):\\\\s+(.+)\",\"level_group\":2,\"message_group\":3,\"description\":\"Copilot CLI bracketed critical/error messages with timestamp\"},{\"pattern\":\"\\\\[(\\\\d{4}-\\\\d{2}-\\\\d{2}T\\\\d{2}:\\\\d{2}:\\\\d{2}\\\\.\\\\d{3}Z)\\\\]\\\\s+(WARNING):\\\\s+(.+)\",\"level_group\":2,\"message_group\":3,\"description\":\"Copilot CLI bracketed warning messages with timestamp\"},{\"pattern\":\"(Error):\\\\s+(.+)\",\"level_group\":1,\"message_group\":2,\"description\":\"Generic error messages from Copilot CLI or Node.js\"},{\"pattern\":\"npm ERR!\\\\s+(.+)\",\"level_group\":0,\"message_group\":1,\"description\":\"NPM error messages during Copilot CLI installation or execution\"},{\"pattern\":\"(Warning):\\\\s+(.+)\",\"level_group\":1,\"message_group\":2,\"description\":\"Generic warning messages from Copilot CLI\"},{\"pattern\":\"(Fatal error):\\\\s+(.+)\",\"level_group\":1,\"message_group\":2,\"description\":\"Fatal error messages from Copilot CLI\"},{\"pattern\":\"copilot:\\\\s+(error):\\\\s+(.+)\",\"level_group\":1,\"message_group\":2,\"description\":\"Copilot CLI command-level error messages\"},{\"pattern\":\"access denied.*only authorized.*can trigger.*workflow\",\"level_group\":0,\"message_group\":0,\"description\":\"Permission denied - workflow access restriction\"},{\"pattern\":\"access denied.*user.*not authorized\",\"level_group\":0,\"message_group\":0,\"description\":\"Permission denied - user not authorized\"},{\"pattern\":\"repository permission check failed\",\"level_group\":0,\"message_group\":0,\"description\":\"Repository permission check failure\"},{\"pattern\":\"configuration error.*required permissions not specified\",\"level_group\":0,\"message_group\":0,\"description\":\"Configuration error - missing permissions\"},{\"pattern\":\"\\\\berror\\\\b.*permission.*denied\",\"level_group\":0,\"message_group\":0,\"description\":\"Permission denied error (requires error context)\"},{\"pattern\":\"\\\\berror\\\\b.*unauthorized\",\"level_group\":0,\"message_group\":0,\"description\":\"Unauthorized error (requires error context)\"},{\"pattern\":\"\\\\berror\\\\b.*forbidden\",\"level_group\":0,\"message_group\":0,\"description\":\"Forbidden error (requires error context)\"},{\"pattern\":\"\\\\berror\\\\b.*access.*restricted\",\"level_group\":0,\"message_group\":0,\"description\":\"Access restricted error (requires error context)\"},{\"pattern\":\"\\\\berror\\\\b.*insufficient.*permission\",\"level_group\":0,\"message_group\":0,\"description\":\"Insufficient permissions error (requires error context)\"},{\"pattern\":\"authentication failed\",\"level_group\":0,\"message_group\":0,\"description\":\"Authentication failure with Copilot CLI\"},{\"pattern\":\"\\\\berror\\\\b.*token.*invalid\",\"level_group\":0,\"message_group\":0,\"description\":\"Invalid token error with Copilot CLI (requires error context)\"},{\"pattern\":\"not authorized.*copilot\",\"level_group\":0,\"message_group\":0,\"description\":\"Not authorized for Copilot CLI access\"},{\"pattern\":\"command not found:\\\\s*(.+)\",\"level_group\":0,\"message_group\":1,\"description\":\"Shell command not found error\"},{\"pattern\":\"(.+):\\\\s*command not found\",\"level_group\":0,\"message_group\":1,\"description\":\"Shell command not found error (alternate format)\"},{\"pattern\":\"sh:\\\\s*\\\\d+:\\\\s*(.+):\\\\s*not found\",\"level_group\":0,\"message_group\":1,\"description\":\"Shell command not found error (sh format)\"},{\"pattern\":\"bash:\\\\s*(.+):\\\\s*command not found\",\"level_group\":0,\"message_group\":1,\"description\":\"Bash command not found error\"},{\"pattern\":\"permission denied and could not request permission from user\",\"level_group\":0,\"message_group\":0,\"description\":\"Copilot CLI permission denied warning (user interaction required)\"},{\"pattern\":\"✗\\\\s+(.+)\",\"level_group\":0,\"message_group\":1,\"description\":\"Copilot CLI failed command indicator\"},{\"pattern\":\"Error:\\\\s*Cannot find module\\\\s*'(.+)'\",\"level_group\":0,\"message_group\":1,\"description\":\"Node.js module not found error\"},{\"pattern\":\"sh:\\\\s*\\\\d+:\\\\s*(.+):\\\\s*Permission denied\",\"level_group\":0,\"message_group\":1,\"description\":\"Shell permission denied error\"},{\"pattern\":\"(rate limit|too many requests)\",\"level_group\":0,\"message_group\":0,\"description\":\"Rate limit exceeded error\"},{\"pattern\":\"(429|HTTP.*429)\",\"level_group\":0,\"message_group\":0,\"description\":\"HTTP 429 Too Many Requests status code\"},{\"pattern\":\"error.*quota.*exceeded\",\"level_group\":0,\"message_group\":0,\"description\":\"Quota exceeded error\"},{\"pattern\":\"error.*(timeout|timed out|deadline exceeded)\",\"level_group\":0,\"message_group\":0,\"description\":\"Timeout or deadline exceeded error\"},{\"pattern\":\"(connection refused|connection failed|ECONNREFUSED)\",\"level_group\":0,\"message_group\":0,\"description\":\"Network connection error\"},{\"pattern\":\"(ETIMEDOUT|ENOTFOUND)\",\"level_group\":0,\"message_group\":0,\"description\":\"Network timeout or DNS resolution error\"},{\"pattern\":\"error.*token.*expired\",\"level_group\":0,\"message_group\":0,\"description\":\"Token expired error\"},{\"pattern\":\"(maximum call stack size exceeded|heap out of memory|spawn ENOMEM)\",\"level_group\":0,\"message_group\":0,\"description\":\"Memory or resource exhaustion error\"}]" with: script: | function main() { @@ -2418,6 +3000,8 @@ jobs: needs: agent runs-on: ubuntu-latest permissions: read-all + concurrency: + group: "gh-aw-copilot" timeout-minutes: 10 steps: - name: Download agent output artifact @@ -2541,22 +3125,29 @@ jobs: uses: actions/setup-node@v4 with: node-version: '24' - - name: Install Codex - run: npm install -g @openai/codex@0.46.0 - - name: Run Codex + - name: Install GitHub Copilot CLI + run: npm install -g @github/copilot@0.0.340 + - name: Execute GitHub Copilot CLI + id: agentic_execution + # Copilot CLI tool arguments (sorted): + # --allow-tool shell(cat) + # --allow-tool shell(grep) + # --allow-tool shell(head) + # --allow-tool shell(jq) + # --allow-tool shell(ls) + # --allow-tool shell(tail) + # --allow-tool shell(wc) + timeout-minutes: 20 run: | set -o pipefail - INSTRUCTION=$(cat $GITHUB_AW_PROMPT) - mkdir -p $CODEX_HOME/logs - codex exec --full-auto --skip-git-repo-check "$INSTRUCTION" 2>&1 | tee /tmp/gh-aw/threat-detection/detection.log + COPILOT_CLI_INSTRUCTION=$(cat /tmp/gh-aw/aw-prompts/prompt.txt) + copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/.copilot/logs/ --allow-tool 'shell(cat)' --allow-tool 'shell(grep)' --allow-tool 'shell(head)' --allow-tool 'shell(jq)' --allow-tool 'shell(ls)' --allow-tool 'shell(tail)' --allow-tool 'shell(wc)' --prompt "$COPILOT_CLI_INSTRUCTION" 2>&1 | tee /tmp/gh-aw/threat-detection/detection.log env: - CODEX_API_KEY: ${{ secrets.CODEX_API_KEY || secrets.OPENAI_API_KEY }} - CODEX_HOME: /tmp/gh-aw/mcp-config - GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} - GITHUB_AW_MCP_CONFIG: /tmp/gh-aw/mcp-config/config.toml + COPILOT_AGENT_RUNNER_TYPE: STANDALONE GITHUB_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }} - RUST_LOG: trace,hyper_util=info,mio=info,reqwest=info,os_info=info,codex_otel=warn,codex_core=debug,ocodex_exec=debug + GITHUB_TOKEN: ${{ secrets.COPILOT_CLI_TOKEN }} + XDG_CONFIG_HOME: /home/runner - name: Parse threat detection results uses: actions/github-script@v8 with: @@ -2621,8 +3212,6 @@ jobs: env: GITHUB_AW_AGENT_OUTPUT: ${{ needs.agent.outputs.output }} GITHUB_AW_WORKFLOW_NAME: "Dev" - GITHUB_AW_ISSUE_TITLE_PREFIX: "[dev] " - GITHUB_AW_ISSUE_LABELS: "dev,sub-task,poetry" GITHUB_AW_SAFE_OUTPUTS_STAGED: "true" with: script: | diff --git a/.github/workflows/dev.md b/.github/workflows/dev.md index b5fd61c7a26..3fab759c359 100644 --- a/.github/workflows/dev.md +++ b/.github/workflows/dev.md @@ -1,24 +1,14 @@ --- on: workflow_dispatch: - push: - paths: - - '.github/workflows/dev.lock.yml' name: Dev -engine: codex +engine: copilot permissions: contents: read actions: read -tools: - github: - mode: "remote" - toolset: - - "pull_requests" safe-outputs: staged: true create-issue: - title-prefix: "[dev] " - labels: [dev, sub-task, poetry] --- Write a poem about the last 3 pull requests and publish an issue. From 5ce59f29020a706f93af7f9ec600bfebe83ecf41 Mon Sep 17 00:00:00 2001 From: Peli de Halleux Date: Tue, 14 Oct 2025 00:32:25 +0000 Subject: [PATCH 6/9] Refactor GitHub Actions workflows to streamline environment variable setup for safe outputs - Removed redundant environment variable declarations in multiple workflow files. - Consolidated the setup of `GITHUB_PERSONAL_ACCESS_TOKEN` and `GITHUB_AW_SAFE_OUTPUTS_CONFIG` to improve maintainability. - Introduced a new function `generateSafeOutputsConfig` to handle safe outputs configuration generation, replacing the previous inline logic. - Updated the `applySafeOutputEnvToMap` function to utilize the new safe outputs configuration generation method. - Ensured consistent handling of safe outputs across various workflows, enhancing clarity and reducing duplication. --- .github/workflows/artifacts-summary.lock.yml | 6 +- .github/workflows/audit-workflows.lock.yml | 4 - .github/workflows/brave.lock.yml | 6 +- .../workflows/changeset-generator.lock.yml | 4 - .github/workflows/ci-doctor.lock.yml | 6 +- .../workflows/cli-version-checker.lock.yml | 4 - .github/workflows/daily-news.lock.yml | 6 +- .github/workflows/dev.lock.yml | 6 +- .../duplicate-code-detector.lock.yml | 5 +- .../github-mcp-tools-report.lock.yml | 4 - .../workflows/go-pattern-detector.lock.yml | 4 - .github/workflows/issue-classifier.lock.yml | 5 +- .github/workflows/lockfile-stats.lock.yml | 4 - .../workflows/notion-issue-summary.lock.yml | 4 - .github/workflows/pdf-summary.lock.yml | 6 +- .github/workflows/plan.lock.yml | 6 +- .github/workflows/poem-bot.lock.yml | 9 +- .github/workflows/q.lock.yml | 6 +- .github/workflows/repo-tree-map.lock.yml | 6 +- .github/workflows/scout.lock.yml | 6 +- .github/workflows/security-fix-pr.lock.yml | 4 - .github/workflows/smoke-claude.lock.yml | 4 - .github/workflows/smoke-codex.lock.yml | 5 +- .github/workflows/smoke-copilot.lock.yml | 6 +- .github/workflows/smoke-genaiscript.lock.yml | 10 +- .github/workflows/smoke-opencode.lock.yml | 8 +- .../workflows/technical-doc-writer.lock.yml | 7 - .github/workflows/tidy.lock.yml | 6 +- .github/workflows/unbloat-docs.lock.yml | 4 - pkg/workflow/compiler.go | 177 +----------------- pkg/workflow/copilot_engine.go | 12 +- pkg/workflow/mcps.go | 33 +--- pkg/workflow/safe_output_helpers.go | 176 +++++++++++++++++ 33 files changed, 228 insertions(+), 331 deletions(-) diff --git a/.github/workflows/artifacts-summary.lock.yml b/.github/workflows/artifacts-summary.lock.yml index 28869cf1005..3d89a1833ce 100644 --- a/.github/workflows/artifacts-summary.lock.yml +++ b/.github/workflows/artifacts-summary.lock.yml @@ -872,10 +872,6 @@ jobs: chmod +x /tmp/gh-aw/safe-outputs/mcp-server.cjs - name: Setup MCPs - env: - GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} - GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-discussion\":{\"max\":1},\"missing-tool\":{}}" run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot @@ -1226,6 +1222,8 @@ jobs: GITHUB_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json GITHUB_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} + GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-discussion\":{\"max\":1},\"missing-tool\":{}}" + GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }} GITHUB_TOKEN: ${{ secrets.COPILOT_CLI_TOKEN }} XDG_CONFIG_HOME: /home/runner diff --git a/.github/workflows/audit-workflows.lock.yml b/.github/workflows/audit-workflows.lock.yml index 72bf79ee02a..d24f70efa2a 100644 --- a/.github/workflows/audit-workflows.lock.yml +++ b/.github/workflows/audit-workflows.lock.yml @@ -1017,10 +1017,6 @@ jobs: chmod +x /tmp/gh-aw/safe-outputs/mcp-server.cjs - name: Setup MCPs - env: - GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} - GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-discussion\":{\"max\":1},\"missing-tool\":{}}" run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << EOF diff --git a/.github/workflows/brave.lock.yml b/.github/workflows/brave.lock.yml index 56c797b4153..b98fdfaf97b 100644 --- a/.github/workflows/brave.lock.yml +++ b/.github/workflows/brave.lock.yml @@ -1481,10 +1481,6 @@ jobs: chmod +x /tmp/gh-aw/safe-outputs/mcp-server.cjs - name: Setup MCPs - env: - GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} - GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"max\":1},\"missing-tool\":{}}" run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot @@ -1926,6 +1922,8 @@ jobs: GITHUB_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json GITHUB_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} + GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"max\":1},\"missing-tool\":{}}" + GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }} GITHUB_TOKEN: ${{ secrets.COPILOT_CLI_TOKEN }} XDG_CONFIG_HOME: /home/runner diff --git a/.github/workflows/changeset-generator.lock.yml b/.github/workflows/changeset-generator.lock.yml index 3cf7342d108..8227972fc5c 100644 --- a/.github/workflows/changeset-generator.lock.yml +++ b/.github/workflows/changeset-generator.lock.yml @@ -1454,10 +1454,6 @@ jobs: chmod +x /tmp/gh-aw/safe-outputs/mcp-server.cjs - name: Setup MCPs - env: - GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} - GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"missing-tool\":{},\"push-to-pull-request-branch\":{}}" run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << EOF diff --git a/.github/workflows/ci-doctor.lock.yml b/.github/workflows/ci-doctor.lock.yml index 177a58f5ea3..82c6031aa77 100644 --- a/.github/workflows/ci-doctor.lock.yml +++ b/.github/workflows/ci-doctor.lock.yml @@ -843,10 +843,6 @@ jobs: chmod +x /tmp/gh-aw/safe-outputs/mcp-server.cjs - name: Setup MCPs - env: - GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} - GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"max\":1},\"create-issue\":{\"max\":1},\"missing-tool\":{}}" run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot @@ -1338,6 +1334,8 @@ jobs: GITHUB_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json GITHUB_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} + GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"max\":1},\"create-issue\":{\"max\":1},\"missing-tool\":{}}" + GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }} GITHUB_TOKEN: ${{ secrets.COPILOT_CLI_TOKEN }} XDG_CONFIG_HOME: /home/runner diff --git a/.github/workflows/cli-version-checker.lock.yml b/.github/workflows/cli-version-checker.lock.yml index 238478f48dc..8ac40e6059c 100644 --- a/.github/workflows/cli-version-checker.lock.yml +++ b/.github/workflows/cli-version-checker.lock.yml @@ -979,10 +979,6 @@ jobs: chmod +x /tmp/gh-aw/safe-outputs/mcp-server.cjs - name: Setup MCPs - env: - GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} - GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-pull-request\":{},\"missing-tool\":{}}" run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << EOF diff --git a/.github/workflows/daily-news.lock.yml b/.github/workflows/daily-news.lock.yml index 1390e00a5b1..6775622e56c 100644 --- a/.github/workflows/daily-news.lock.yml +++ b/.github/workflows/daily-news.lock.yml @@ -874,10 +874,6 @@ jobs: chmod +x /tmp/gh-aw/safe-outputs/mcp-server.cjs - name: Setup MCPs - env: - GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} - GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-discussion\":{\"max\":1},\"missing-tool\":{}}" run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot @@ -1232,6 +1228,8 @@ jobs: GITHUB_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json GITHUB_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} + GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-discussion\":{\"max\":1},\"missing-tool\":{}}" + GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }} GITHUB_TOKEN: ${{ secrets.COPILOT_CLI_TOKEN }} XDG_CONFIG_HOME: /home/runner diff --git a/.github/workflows/dev.lock.yml b/.github/workflows/dev.lock.yml index 4905b72b6fd..924e048cb89 100644 --- a/.github/workflows/dev.lock.yml +++ b/.github/workflows/dev.lock.yml @@ -870,10 +870,6 @@ jobs: chmod +x /tmp/gh-aw/safe-outputs/mcp-server.cjs - name: Setup MCPs - env: - GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} - GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-issue\":{\"max\":1},\"missing-tool\":{}}" run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot @@ -1173,7 +1169,9 @@ jobs: GITHUB_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json GITHUB_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} + GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-issue\":{\"max\":1},\"missing-tool\":{}}" GITHUB_AW_SAFE_OUTPUTS_STAGED: true + GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }} GITHUB_TOKEN: ${{ secrets.COPILOT_CLI_TOKEN }} XDG_CONFIG_HOME: /home/runner diff --git a/.github/workflows/duplicate-code-detector.lock.yml b/.github/workflows/duplicate-code-detector.lock.yml index a34ec36f7b8..2322599e8f2 100644 --- a/.github/workflows/duplicate-code-detector.lock.yml +++ b/.github/workflows/duplicate-code-detector.lock.yml @@ -892,10 +892,6 @@ jobs: chmod +x /tmp/gh-aw/safe-outputs/mcp-server.cjs - name: Setup MCPs - env: - GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} - GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-issue\":{\"max\":1},\"missing-tool\":{}}" run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/config.toml << EOF @@ -1299,6 +1295,7 @@ jobs: GITHUB_AW_MCP_CONFIG: /tmp/gh-aw/mcp-config/config.toml GITHUB_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} + GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-issue\":{\"max\":1},\"missing-tool\":{}}" GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }} RUST_LOG: trace,hyper_util=info,mio=info,reqwest=info,os_info=info,codex_otel=warn,codex_core=debug,ocodex_exec=debug - name: Upload Safe Outputs diff --git a/.github/workflows/github-mcp-tools-report.lock.yml b/.github/workflows/github-mcp-tools-report.lock.yml index b49e4abe5d0..7cfb216f602 100644 --- a/.github/workflows/github-mcp-tools-report.lock.yml +++ b/.github/workflows/github-mcp-tools-report.lock.yml @@ -999,10 +999,6 @@ jobs: chmod +x /tmp/gh-aw/safe-outputs/mcp-server.cjs - name: Setup MCPs - env: - GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} - GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-discussion\":{\"max\":1},\"missing-tool\":{}}" run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << EOF diff --git a/.github/workflows/go-pattern-detector.lock.yml b/.github/workflows/go-pattern-detector.lock.yml index f5a3a525812..74ed737e138 100644 --- a/.github/workflows/go-pattern-detector.lock.yml +++ b/.github/workflows/go-pattern-detector.lock.yml @@ -986,10 +986,6 @@ jobs: chmod +x /tmp/gh-aw/safe-outputs/mcp-server.cjs - name: Setup MCPs - env: - GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} - GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-issue\":{\"max\":1},\"missing-tool\":{}}" run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << EOF diff --git a/.github/workflows/issue-classifier.lock.yml b/.github/workflows/issue-classifier.lock.yml index 73a97a1f38d..4a098fdfa95 100644 --- a/.github/workflows/issue-classifier.lock.yml +++ b/.github/workflows/issue-classifier.lock.yml @@ -1341,10 +1341,6 @@ jobs: chmod +x /tmp/gh-aw/safe-outputs/mcp-server.cjs - name: Setup MCPs - env: - GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} - GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-labels\":{\"allowed\":[\"bug\",\"feature\",\"enhancement\",\"documentation\"],\"max\":1},\"missing-tool\":{}}" run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << EOF @@ -1548,6 +1544,7 @@ jobs: GITHUB_AW_MCP_CONFIG: /tmp/gh-aw/mcp-config/mcp-servers.json GITHUB_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} + GITHUB_AW_SAFE_OUTPUTS_CONFIG: "\"{\\\"add-labels\\\":{\\\"allowed\\\":[\\\"bug\\\",\\\"feature\\\",\\\"enhancement\\\",\\\"documentation\\\"],\\\"max\\\":1},\\\"missing-tool\\\":{}}\"" with: enable-github-mcp: ${{ secrets.GITHUB_MCP_TOKEN != '' }} github-mcp-token: ${{ secrets.GITHUB_MCP_TOKEN }} diff --git a/.github/workflows/lockfile-stats.lock.yml b/.github/workflows/lockfile-stats.lock.yml index 527c75c6d6a..bac322bafff 100644 --- a/.github/workflows/lockfile-stats.lock.yml +++ b/.github/workflows/lockfile-stats.lock.yml @@ -999,10 +999,6 @@ jobs: chmod +x /tmp/gh-aw/safe-outputs/mcp-server.cjs - name: Setup MCPs - env: - GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} - GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-discussion\":{\"max\":1},\"missing-tool\":{}}" run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << EOF diff --git a/.github/workflows/notion-issue-summary.lock.yml b/.github/workflows/notion-issue-summary.lock.yml index 2203458a3b5..b49103d5880 100644 --- a/.github/workflows/notion-issue-summary.lock.yml +++ b/.github/workflows/notion-issue-summary.lock.yml @@ -1153,10 +1153,6 @@ jobs: chmod +x /tmp/gh-aw/safe-outputs/mcp-server.cjs - name: Setup MCPs - env: - GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} - GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"notion-add-comment\":{\"description\":\"Add a comment to a Notion page\",\"inputs\":{\"comment\":{\"description\":\"The comment text to add\",\"required\":true,\"type\":\"string\"},\"page_id\":{\"description\":\"The Notion page ID to add a comment to\",\"required\":true,\"type\":\"string\"}},\"output\":\"Comment added to Notion successfully!\"}}" run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << EOF diff --git a/.github/workflows/pdf-summary.lock.yml b/.github/workflows/pdf-summary.lock.yml index 7b8ed8f517e..042aebe702d 100644 --- a/.github/workflows/pdf-summary.lock.yml +++ b/.github/workflows/pdf-summary.lock.yml @@ -1395,10 +1395,6 @@ jobs: chmod +x /tmp/gh-aw/safe-outputs/mcp-server.cjs - name: Setup MCPs - env: - GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} - GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"max\":1},\"missing-tool\":{}}" run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot @@ -1903,6 +1899,8 @@ jobs: GITHUB_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json GITHUB_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} + GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"max\":1},\"missing-tool\":{}}" + GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }} GITHUB_TOKEN: ${{ secrets.COPILOT_CLI_TOKEN }} XDG_CONFIG_HOME: /home/runner diff --git a/.github/workflows/plan.lock.yml b/.github/workflows/plan.lock.yml index f57223191a9..e9e5b881479 100644 --- a/.github/workflows/plan.lock.yml +++ b/.github/workflows/plan.lock.yml @@ -1350,10 +1350,6 @@ jobs: chmod +x /tmp/gh-aw/safe-outputs/mcp-server.cjs - name: Setup MCPs - env: - GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} - GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-issue\":{\"max\":5},\"missing-tool\":{}}" run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot @@ -1786,6 +1782,8 @@ jobs: GITHUB_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json GITHUB_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} + GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-issue\":{\"max\":5},\"missing-tool\":{}}" + GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }} GITHUB_TOKEN: ${{ secrets.COPILOT_CLI_TOKEN }} XDG_CONFIG_HOME: /home/runner diff --git a/.github/workflows/poem-bot.lock.yml b/.github/workflows/poem-bot.lock.yml index 6ef4821157d..04608a5421f 100644 --- a/.github/workflows/poem-bot.lock.yml +++ b/.github/workflows/poem-bot.lock.yml @@ -1380,13 +1380,6 @@ jobs: chmod +x /tmp/gh-aw/safe-outputs/mcp-server.cjs - name: Setup MCPs - env: - GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} - GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"max\":3,\"target\":\"*\"},\"add-labels\":{\"allowed\":[\"poetry\",\"creative\",\"automation\",\"ai-generated\",\"epic\",\"haiku\",\"sonnet\",\"limerick\"],\"max\":5},\"create-issue\":{\"max\":2},\"create-pull-request\":{},\"create-pull-request-review-comment\":{\"max\":2},\"missing-tool\":{},\"push-to-pull-request-branch\":{},\"update-issue\":{\"max\":2},\"upload-asset\":{}}" - GITHUB_AW_ASSETS_BRANCH: "assets/${{ github.workflow }}" - GITHUB_AW_ASSETS_MAX_SIZE_KB: 10240 - GITHUB_AW_ASSETS_ALLOWED_EXTS: ".png,.jpg,.jpeg" run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot @@ -1878,7 +1871,9 @@ jobs: GITHUB_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json GITHUB_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} + GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"max\":3,\"target\":\"*\"},\"add-labels\":{\"allowed\":[\"poetry\",\"creative\",\"automation\",\"ai-generated\",\"epic\",\"haiku\",\"sonnet\",\"limerick\"],\"max\":5},\"create-issue\":{\"max\":2},\"create-pull-request\":{},\"create-pull-request-review-comment\":{\"max\":2},\"missing-tool\":{},\"push-to-pull-request-branch\":{},\"update-issue\":{\"max\":2},\"upload-asset\":{}}" GITHUB_AW_SAFE_OUTPUTS_STAGED: true + GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }} GITHUB_TOKEN: ${{ secrets.COPILOT_CLI_TOKEN }} XDG_CONFIG_HOME: /home/runner diff --git a/.github/workflows/q.lock.yml b/.github/workflows/q.lock.yml index ad71cb1d0b2..edd86459f67 100644 --- a/.github/workflows/q.lock.yml +++ b/.github/workflows/q.lock.yml @@ -1439,10 +1439,6 @@ jobs: chmod +x /tmp/gh-aw/safe-outputs/mcp-server.cjs - name: Setup MCPs - env: - GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} - GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-pull-request\":{},\"missing-tool\":{}}" run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot @@ -2173,6 +2169,8 @@ jobs: GITHUB_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json GITHUB_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} + GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-pull-request\":{},\"missing-tool\":{}}" + GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }} GITHUB_TOKEN: ${{ secrets.COPILOT_CLI_TOKEN }} XDG_CONFIG_HOME: /home/runner diff --git a/.github/workflows/repo-tree-map.lock.yml b/.github/workflows/repo-tree-map.lock.yml index 8c947c4758d..f3468266c31 100644 --- a/.github/workflows/repo-tree-map.lock.yml +++ b/.github/workflows/repo-tree-map.lock.yml @@ -870,10 +870,6 @@ jobs: chmod +x /tmp/gh-aw/safe-outputs/mcp-server.cjs - name: Setup MCPs - env: - GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} - GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-discussion\":{\"max\":1},\"missing-tool\":{}}" run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot @@ -1283,6 +1279,8 @@ jobs: GITHUB_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json GITHUB_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} + GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-discussion\":{\"max\":1},\"missing-tool\":{}}" + GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }} GITHUB_TOKEN: ${{ secrets.COPILOT_CLI_TOKEN }} XDG_CONFIG_HOME: /home/runner diff --git a/.github/workflows/scout.lock.yml b/.github/workflows/scout.lock.yml index 03e393ded35..1c587345c49 100644 --- a/.github/workflows/scout.lock.yml +++ b/.github/workflows/scout.lock.yml @@ -1677,10 +1677,6 @@ jobs: chmod +x /tmp/gh-aw/safe-outputs/mcp-server.cjs - name: Setup MCPs - env: - GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} - GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"max\":1},\"missing-tool\":{}}" run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot @@ -2228,6 +2224,8 @@ jobs: GITHUB_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json GITHUB_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} + GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"max\":1},\"missing-tool\":{}}" + GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }} GITHUB_TOKEN: ${{ secrets.COPILOT_CLI_TOKEN }} XDG_CONFIG_HOME: /home/runner diff --git a/.github/workflows/security-fix-pr.lock.yml b/.github/workflows/security-fix-pr.lock.yml index 0c3fcffed6b..63cb1724282 100644 --- a/.github/workflows/security-fix-pr.lock.yml +++ b/.github/workflows/security-fix-pr.lock.yml @@ -998,10 +998,6 @@ jobs: chmod +x /tmp/gh-aw/safe-outputs/mcp-server.cjs - name: Setup MCPs - env: - GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} - GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-pull-request\":{},\"missing-tool\":{}}" run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << EOF diff --git a/.github/workflows/smoke-claude.lock.yml b/.github/workflows/smoke-claude.lock.yml index e6e08b5e25a..3216e7c5b6c 100644 --- a/.github/workflows/smoke-claude.lock.yml +++ b/.github/workflows/smoke-claude.lock.yml @@ -975,10 +975,6 @@ jobs: chmod +x /tmp/gh-aw/safe-outputs/mcp-server.cjs - name: Setup MCPs - env: - GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} - GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-issue\":{\"max\":1,\"min\":1},\"missing-tool\":{}}" run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << EOF diff --git a/.github/workflows/smoke-codex.lock.yml b/.github/workflows/smoke-codex.lock.yml index 5db74659ae7..5f58b41dea2 100644 --- a/.github/workflows/smoke-codex.lock.yml +++ b/.github/workflows/smoke-codex.lock.yml @@ -866,10 +866,6 @@ jobs: chmod +x /tmp/gh-aw/safe-outputs/mcp-server.cjs - name: Setup MCPs - env: - GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} - GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-issue\":{\"max\":1,\"min\":1},\"missing-tool\":{}}" run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/config.toml << EOF @@ -1047,6 +1043,7 @@ jobs: GITHUB_AW_MCP_CONFIG: /tmp/gh-aw/mcp-config/config.toml GITHUB_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} + GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-issue\":{\"max\":1,\"min\":1},\"missing-tool\":{}}" GITHUB_AW_SAFE_OUTPUTS_STAGED: true GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }} RUST_LOG: trace,hyper_util=info,mio=info,reqwest=info,os_info=info,codex_otel=warn,codex_core=debug,ocodex_exec=debug diff --git a/.github/workflows/smoke-copilot.lock.yml b/.github/workflows/smoke-copilot.lock.yml index 086e414aecc..e359429e160 100644 --- a/.github/workflows/smoke-copilot.lock.yml +++ b/.github/workflows/smoke-copilot.lock.yml @@ -868,10 +868,6 @@ jobs: chmod +x /tmp/gh-aw/safe-outputs/mcp-server.cjs - name: Setup MCPs - env: - GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} - GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-issue\":{\"max\":1,\"min\":1},\"missing-tool\":{}}" run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot @@ -1171,7 +1167,9 @@ jobs: GITHUB_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json GITHUB_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} + GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-issue\":{\"max\":1,\"min\":1},\"missing-tool\":{}}" GITHUB_AW_SAFE_OUTPUTS_STAGED: true + GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }} GITHUB_TOKEN: ${{ secrets.COPILOT_CLI_TOKEN }} XDG_CONFIG_HOME: /home/runner diff --git a/.github/workflows/smoke-genaiscript.lock.yml b/.github/workflows/smoke-genaiscript.lock.yml index ce6c94ed192..02a42c6815a 100644 --- a/.github/workflows/smoke-genaiscript.lock.yml +++ b/.github/workflows/smoke-genaiscript.lock.yml @@ -868,10 +868,6 @@ jobs: chmod +x /tmp/gh-aw/safe-outputs/mcp-server.cjs - name: Setup MCPs - env: - GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} - GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-issue\":{\"max\":1,\"min\":1},\"missing-tool\":{}}" run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << EOF @@ -1010,6 +1006,7 @@ jobs: GITHUB_AW_MCP_CONFIG: /tmp/gh-aw/mcp-config/mcp-servers.json GITHUB_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} + GITHUB_AW_SAFE_OUTPUTS_CONFIG: "\"{\\\"create-issue\\\":{\\\"max\\\":1,\\\"min\\\":1},\\\"missing-tool\\\":{}}\"" GITHUB_AW_SAFE_OUTPUTS_STAGED: "true" - name: Convert prompt to GenAI format run: | @@ -1027,6 +1024,7 @@ jobs: GITHUB_AW_MCP_CONFIG: /tmp/gh-aw/mcp-config/mcp-servers.json GITHUB_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} + GITHUB_AW_SAFE_OUTPUTS_CONFIG: "\"{\\\"create-issue\\\":{\\\"max\\\":1,\\\"min\\\":1},\\\"missing-tool\\\":{}}\"" GITHUB_AW_SAFE_OUTPUTS_STAGED: "true" - name: Run GenAIScript id: genaiscript @@ -1038,6 +1036,7 @@ jobs: GITHUB_AW_MCP_CONFIG: /tmp/gh-aw/mcp-config/mcp-servers.json GITHUB_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} + GITHUB_AW_SAFE_OUTPUTS_CONFIG: "\"{\\\"create-issue\\\":{\\\"max\\\":1,\\\"min\\\":1},\\\"missing-tool\\\":{}}\"" GITHUB_AW_SAFE_OUTPUTS_STAGED: "true" GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Ensure log file exists @@ -1954,6 +1953,7 @@ jobs: GITHUB_AW_MCP_CONFIG: /tmp/gh-aw/mcp-config/mcp-servers.json GITHUB_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} + GITHUB_AW_SAFE_OUTPUTS_CONFIG: "\"{\\\"create-issue\\\":{\\\"max\\\":1,\\\"min\\\":1},\\\"missing-tool\\\":{}}\"" GITHUB_AW_SAFE_OUTPUTS_STAGED: "true" - name: Convert prompt to GenAI format run: | @@ -1971,6 +1971,7 @@ jobs: GITHUB_AW_MCP_CONFIG: /tmp/gh-aw/mcp-config/mcp-servers.json GITHUB_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} + GITHUB_AW_SAFE_OUTPUTS_CONFIG: "\"{\\\"create-issue\\\":{\\\"max\\\":1,\\\"min\\\":1},\\\"missing-tool\\\":{}}\"" GITHUB_AW_SAFE_OUTPUTS_STAGED: "true" - name: Run GenAIScript id: genaiscript @@ -1982,6 +1983,7 @@ jobs: GITHUB_AW_MCP_CONFIG: /tmp/gh-aw/mcp-config/mcp-servers.json GITHUB_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} + GITHUB_AW_SAFE_OUTPUTS_CONFIG: "\"{\\\"create-issue\\\":{\\\"max\\\":1,\\\"min\\\":1},\\\"missing-tool\\\":{}}\"" GITHUB_AW_SAFE_OUTPUTS_STAGED: "true" GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Ensure log file exists diff --git a/.github/workflows/smoke-opencode.lock.yml b/.github/workflows/smoke-opencode.lock.yml index f953c01cf21..b46a9e29f15 100644 --- a/.github/workflows/smoke-opencode.lock.yml +++ b/.github/workflows/smoke-opencode.lock.yml @@ -868,10 +868,6 @@ jobs: chmod +x /tmp/gh-aw/safe-outputs/mcp-server.cjs - name: Setup MCPs - env: - GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} - GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-issue\":{\"max\":1,\"min\":1},\"missing-tool\":{}}" run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << EOF @@ -1010,6 +1006,7 @@ jobs: GITHUB_AW_MCP_CONFIG: /tmp/gh-aw/mcp-config/mcp-servers.json GITHUB_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} + GITHUB_AW_SAFE_OUTPUTS_CONFIG: "\"{\\\"create-issue\\\":{\\\"max\\\":1,\\\"min\\\":1},\\\"missing-tool\\\":{}}\"" GITHUB_AW_SAFE_OUTPUTS_STAGED: "true" - name: Run OpenCode id: opencode @@ -1022,6 +1019,7 @@ jobs: GITHUB_AW_MCP_CONFIG: /tmp/gh-aw/mcp-config/mcp-servers.json GITHUB_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} + GITHUB_AW_SAFE_OUTPUTS_CONFIG: "\"{\\\"create-issue\\\":{\\\"max\\\":1,\\\"min\\\":1},\\\"missing-tool\\\":{}}\"" GITHUB_AW_SAFE_OUTPUTS_STAGED: "true" GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} @@ -1939,6 +1937,7 @@ jobs: GITHUB_AW_MCP_CONFIG: /tmp/gh-aw/mcp-config/mcp-servers.json GITHUB_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} + GITHUB_AW_SAFE_OUTPUTS_CONFIG: "\"{\\\"create-issue\\\":{\\\"max\\\":1,\\\"min\\\":1},\\\"missing-tool\\\":{}}\"" GITHUB_AW_SAFE_OUTPUTS_STAGED: "true" - name: Run OpenCode id: opencode @@ -1951,6 +1950,7 @@ jobs: GITHUB_AW_MCP_CONFIG: /tmp/gh-aw/mcp-config/mcp-servers.json GITHUB_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} + GITHUB_AW_SAFE_OUTPUTS_CONFIG: "\"{\\\"create-issue\\\":{\\\"max\\\":1,\\\"min\\\":1},\\\"missing-tool\\\":{}}\"" GITHUB_AW_SAFE_OUTPUTS_STAGED: "true" GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} diff --git a/.github/workflows/technical-doc-writer.lock.yml b/.github/workflows/technical-doc-writer.lock.yml index 19984c977c3..3480af0b2cf 100644 --- a/.github/workflows/technical-doc-writer.lock.yml +++ b/.github/workflows/technical-doc-writer.lock.yml @@ -1014,13 +1014,6 @@ jobs: chmod +x /tmp/gh-aw/safe-outputs/mcp-server.cjs - name: Setup MCPs - env: - GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} - GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"max\":1},\"create-pull-request\":{},\"missing-tool\":{},\"upload-asset\":{}}" - GITHUB_AW_ASSETS_BRANCH: "assets/${{ github.workflow }}" - GITHUB_AW_ASSETS_MAX_SIZE_KB: 10240 - GITHUB_AW_ASSETS_ALLOWED_EXTS: ".png,.jpg,.jpeg" run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << EOF diff --git a/.github/workflows/tidy.lock.yml b/.github/workflows/tidy.lock.yml index 91c6a74e38f..8f2983a4263 100644 --- a/.github/workflows/tidy.lock.yml +++ b/.github/workflows/tidy.lock.yml @@ -1211,10 +1211,6 @@ jobs: chmod +x /tmp/gh-aw/safe-outputs/mcp-server.cjs - name: Setup MCPs - env: - GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} - GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-pull-request\":{},\"missing-tool\":{},\"push-to-pull-request-branch\":{}}" run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot @@ -1637,6 +1633,8 @@ jobs: GITHUB_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json GITHUB_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} + GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"create-pull-request\":{},\"missing-tool\":{},\"push-to-pull-request-branch\":{}}" + GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }} GITHUB_TOKEN: ${{ secrets.COPILOT_CLI_TOKEN }} XDG_CONFIG_HOME: /home/runner diff --git a/.github/workflows/unbloat-docs.lock.yml b/.github/workflows/unbloat-docs.lock.yml index f355183f19a..fed7e383030 100644 --- a/.github/workflows/unbloat-docs.lock.yml +++ b/.github/workflows/unbloat-docs.lock.yml @@ -1319,10 +1319,6 @@ jobs: chmod +x /tmp/gh-aw/safe-outputs/mcp-server.cjs - name: Setup MCPs - env: - GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }} - GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"max\":1},\"create-pull-request\":{},\"missing-tool\":{}}" run: | mkdir -p /tmp/gh-aw/mcp-config cat > /tmp/gh-aw/mcp-config/mcp-servers.json << EOF diff --git a/pkg/workflow/compiler.go b/pkg/workflow/compiler.go index 026de29766b..e6f88716dce 100644 --- a/pkg/workflow/compiler.go +++ b/pkg/workflow/compiler.go @@ -2111,7 +2111,7 @@ func (c *Compiler) buildMainJob(data *WorkflowData, activationJobCreated bool) ( env["GITHUB_AW_SAFE_OUTPUTS"] = "/tmp/gh-aw/safe-outputs/outputs.jsonl" // Set GITHUB_AW_SAFE_OUTPUTS_CONFIG with the safe outputs configuration - safeOutputConfig := c.generateSafeOutputsConfig(data) + safeOutputConfig := generateSafeOutputsConfig(data) if safeOutputConfig != "" { // The JSON string needs to be properly quoted for YAML env["GITHUB_AW_SAFE_OUTPUTS_CONFIG"] = fmt.Sprintf("%q", safeOutputConfig) @@ -2867,179 +2867,6 @@ func (c *Compiler) generateCreateAwInfo(yaml *strings.Builder, data *WorkflowDat yaml.WriteString(" console.log(JSON.stringify(awInfo, null, 2));\n") } -func (c *Compiler) generateSafeOutputsConfig(data *WorkflowData) string { - // Pass the safe-outputs configuration for validation - if data.SafeOutputs == nil { - return "" - } - // Create a simplified config object for validation - safeOutputsConfig := make(map[string]any) - - // Handle safe-outputs configuration if present - if data.SafeOutputs != nil { - if data.SafeOutputs.CreateIssues != nil { - issueConfig := map[string]any{} - if data.SafeOutputs.CreateIssues.Max > 0 { - issueConfig["max"] = data.SafeOutputs.CreateIssues.Max - } - if data.SafeOutputs.CreateIssues.Min > 0 { - issueConfig["min"] = data.SafeOutputs.CreateIssues.Min - } - safeOutputsConfig["create-issue"] = issueConfig - } - if data.SafeOutputs.AddComments != nil { - commentConfig := map[string]any{} - if data.SafeOutputs.AddComments.Target != "" { - commentConfig["target"] = data.SafeOutputs.AddComments.Target - } - if data.SafeOutputs.AddComments.Max > 0 { - commentConfig["max"] = data.SafeOutputs.AddComments.Max - } - if data.SafeOutputs.AddComments.Min > 0 { - commentConfig["min"] = data.SafeOutputs.AddComments.Min - } - safeOutputsConfig["add-comment"] = commentConfig - } - if data.SafeOutputs.CreateDiscussions != nil { - discussionConfig := map[string]any{} - if data.SafeOutputs.CreateDiscussions.Max > 0 { - discussionConfig["max"] = data.SafeOutputs.CreateDiscussions.Max - } - if data.SafeOutputs.CreateDiscussions.Min > 0 { - discussionConfig["min"] = data.SafeOutputs.CreateDiscussions.Min - } - safeOutputsConfig["create-discussion"] = discussionConfig - } - if data.SafeOutputs.CreatePullRequests != nil { - prConfig := map[string]any{} - // Note: max is always 1 for pull requests, not configurable - if data.SafeOutputs.CreatePullRequests.Min > 0 { - prConfig["min"] = data.SafeOutputs.CreatePullRequests.Min - } - safeOutputsConfig["create-pull-request"] = prConfig - } - if data.SafeOutputs.CreatePullRequestReviewComments != nil { - prReviewCommentConfig := map[string]any{} - if data.SafeOutputs.CreatePullRequestReviewComments.Max > 0 { - prReviewCommentConfig["max"] = data.SafeOutputs.CreatePullRequestReviewComments.Max - } - if data.SafeOutputs.CreatePullRequestReviewComments.Min > 0 { - prReviewCommentConfig["min"] = data.SafeOutputs.CreatePullRequestReviewComments.Min - } - safeOutputsConfig["create-pull-request-review-comment"] = prReviewCommentConfig - } - if data.SafeOutputs.CreateCodeScanningAlerts != nil { - // Security reports typically have unlimited max, but check if configured - securityReportConfig := map[string]any{} - if data.SafeOutputs.CreateCodeScanningAlerts.Max > 0 { - securityReportConfig["max"] = data.SafeOutputs.CreateCodeScanningAlerts.Max - } - if data.SafeOutputs.CreateCodeScanningAlerts.Min > 0 { - securityReportConfig["min"] = data.SafeOutputs.CreateCodeScanningAlerts.Min - } - safeOutputsConfig["create-code-scanning-alert"] = securityReportConfig - } - if data.SafeOutputs.AddLabels != nil { - labelConfig := map[string]any{} - if data.SafeOutputs.AddLabels.Max > 0 { - labelConfig["max"] = data.SafeOutputs.AddLabels.Max - } - if data.SafeOutputs.AddLabels.Min > 0 { - labelConfig["min"] = data.SafeOutputs.AddLabels.Min - } - if len(data.SafeOutputs.AddLabels.Allowed) > 0 { - labelConfig["allowed"] = data.SafeOutputs.AddLabels.Allowed - } - safeOutputsConfig["add-labels"] = labelConfig - } - if data.SafeOutputs.UpdateIssues != nil { - updateConfig := map[string]any{} - if data.SafeOutputs.UpdateIssues.Max > 0 { - updateConfig["max"] = data.SafeOutputs.UpdateIssues.Max - } - if data.SafeOutputs.UpdateIssues.Min > 0 { - updateConfig["min"] = data.SafeOutputs.UpdateIssues.Min - } - safeOutputsConfig["update-issue"] = updateConfig - } - if data.SafeOutputs.PushToPullRequestBranch != nil { - pushToBranchConfig := map[string]any{} - if data.SafeOutputs.PushToPullRequestBranch.Target != "" { - pushToBranchConfig["target"] = data.SafeOutputs.PushToPullRequestBranch.Target - } - if data.SafeOutputs.PushToPullRequestBranch.Max > 0 { - pushToBranchConfig["max"] = data.SafeOutputs.PushToPullRequestBranch.Max - } - if data.SafeOutputs.PushToPullRequestBranch.Min > 0 { - pushToBranchConfig["min"] = data.SafeOutputs.PushToPullRequestBranch.Min - } - safeOutputsConfig["push-to-pull-request-branch"] = pushToBranchConfig - } - if data.SafeOutputs.UploadAssets != nil { - uploadConfig := map[string]any{} - if data.SafeOutputs.UploadAssets.Max > 0 { - uploadConfig["max"] = data.SafeOutputs.UploadAssets.Max - } - if data.SafeOutputs.UploadAssets.Min > 0 { - uploadConfig["min"] = data.SafeOutputs.UploadAssets.Min - } - safeOutputsConfig["upload-asset"] = uploadConfig - } - if data.SafeOutputs.MissingTool != nil { - missingToolConfig := map[string]any{} - if data.SafeOutputs.MissingTool.Max > 0 { - missingToolConfig["max"] = data.SafeOutputs.MissingTool.Max - } - if data.SafeOutputs.MissingTool.Min > 0 { - missingToolConfig["min"] = data.SafeOutputs.MissingTool.Min - } - safeOutputsConfig["missing-tool"] = missingToolConfig - } - } - - // Add safe-jobs configuration from SafeOutputs.Jobs - if len(data.SafeOutputs.Jobs) > 0 { - for jobName, jobConfig := range data.SafeOutputs.Jobs { - safeJobConfig := map[string]any{} - - // Add description if present - if jobConfig.Description != "" { - safeJobConfig["description"] = jobConfig.Description - } - - // Add output if present - if jobConfig.Output != "" { - safeJobConfig["output"] = jobConfig.Output - } - - // Add inputs information - if len(jobConfig.Inputs) > 0 { - inputsConfig := make(map[string]any) - for inputName, inputDef := range jobConfig.Inputs { - inputConfig := map[string]any{ - "type": inputDef.Type, - "description": inputDef.Description, - "required": inputDef.Required, - } - if inputDef.Default != "" { - inputConfig["default"] = inputDef.Default - } - if len(inputDef.Options) > 0 { - inputConfig["options"] = inputDef.Options - } - inputsConfig[inputName] = inputConfig - } - safeJobConfig["inputs"] = inputsConfig - } - - safeOutputsConfig[jobName] = safeJobConfig - } - } - - configJSON, _ := json.Marshal(safeOutputsConfig) - return string(configJSON) -} - // generateOutputCollectionStep generates a step that reads the output file and sets it as a GitHub Actions output func (c *Compiler) generateOutputCollectionStep(yaml *strings.Builder, data *WorkflowData) { yaml.WriteString(" - name: Upload Safe Outputs\n") @@ -3059,7 +2886,7 @@ func (c *Compiler) generateOutputCollectionStep(yaml *strings.Builder, data *Wor yaml.WriteString(" GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }}\n") // Pass the safe-outputs configuration for validation - safeOutputConfig := c.generateSafeOutputsConfig(data) + safeOutputConfig := generateSafeOutputsConfig(data) if safeOutputConfig != "" { fmt.Fprintf(yaml, " GITHUB_AW_SAFE_OUTPUTS_CONFIG: %q\n", safeOutputConfig) } diff --git a/pkg/workflow/copilot_engine.go b/pkg/workflow/copilot_engine.go index 4159a409097..df8130bd5ed 100644 --- a/pkg/workflow/copilot_engine.go +++ b/pkg/workflow/copilot_engine.go @@ -104,6 +104,16 @@ copilot %s 2>&1 | tee %s`, shellJoinArgs(copilotArgs), logFile) env["GITHUB_AW_MCP_CONFIG"] = "/home/runner/.copilot/mcp-config.json" } + if hasGitHubTool(workflowData.Tools) { + githubTool := workflowData.Tools["github"] + customGitHubToken := getGitHubToken(githubTool) + if customGitHubToken != "" { + env["GITHUB_PERSONAL_ACCESS_TOKEN"] = customGitHubToken + } else { + env["GITHUB_PERSONAL_ACCESS_TOKEN"] = "${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}" + } + } + // Add GITHUB_AW_SAFE_OUTPUTS if output is needed applySafeOutputEnvToMap(env, workflowData) @@ -236,8 +246,6 @@ func (e *CopilotEngine) RenderMCPConfig(yaml *strings.Builder, tools map[string] //GITHUB_COPILOT_CLI_MODE yaml.WriteString(" echo \"HOME: $HOME\"\n") yaml.WriteString(" echo \"GITHUB_COPILOT_CLI_MODE: $GITHUB_COPILOT_CLI_MODE\"\n") - //yaml.WriteString(" echo \"GITHUB_AW_SAFE_OUTPUTS_CONFIG: ${{ toJSON(env.GITHUB_AW_SAFE_OUTPUTS_CONFIG) }}\"") - } // renderGitHubCopilotMCPConfig generates the GitHub MCP server configuration for Copilot CLI diff --git a/pkg/workflow/mcps.go b/pkg/workflow/mcps.go index 83921ff065f..df0a9e9287f 100644 --- a/pkg/workflow/mcps.go +++ b/pkg/workflow/mcps.go @@ -74,7 +74,7 @@ func (c *Compiler) generateMCPSetup(yaml *strings.Builder, tools map[string]any, // Generate safe-outputs configuration once to avoid duplicate computation var safeOutputConfig string if HasSafeOutputsEnabled(workflowData.SafeOutputs) { - safeOutputConfig = c.generateSafeOutputsConfig(workflowData) + safeOutputConfig = generateSafeOutputsConfig(workflowData) } // Sort tools to ensure stable code generation @@ -161,37 +161,6 @@ func (c *Compiler) generateMCPSetup(yaml *strings.Builder, tools map[string]any, // Use the engine's RenderMCPConfig method yaml.WriteString(" - name: Setup MCPs\n") - - // Check if we need to add environment variables - needsEnv := HasSafeOutputsEnabled(workflowData.SafeOutputs) || hasGitHubTool(tools) - - if needsEnv { - yaml.WriteString(" env:\n") - - // Add GITHUB_PERSONAL_ACCESS_TOKEN for GitHub MCP server - if hasGitHubTool(tools) { - githubTool := tools["github"] - customGitHubToken := getGitHubToken(githubTool) - if customGitHubToken != "" { - fmt.Fprintf(yaml, " GITHUB_PERSONAL_ACCESS_TOKEN: %s\n", customGitHubToken) - } else { - yaml.WriteString(" GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}\n") - } - } - - // Add safe outputs environment variables - if HasSafeOutputsEnabled(workflowData.SafeOutputs) { - if safeOutputConfig != "" { - fmt.Fprintf(yaml, " GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }}\n") - fmt.Fprintf(yaml, " GITHUB_AW_SAFE_OUTPUTS_CONFIG: %q\n", safeOutputConfig) - if workflowData.SafeOutputs != nil && workflowData.SafeOutputs.UploadAssets != nil { - fmt.Fprintf(yaml, " GITHUB_AW_ASSETS_BRANCH: %q\n", workflowData.SafeOutputs.UploadAssets.BranchName) - fmt.Fprintf(yaml, " GITHUB_AW_ASSETS_MAX_SIZE_KB: %d\n", workflowData.SafeOutputs.UploadAssets.MaxSizeKB) - fmt.Fprintf(yaml, " GITHUB_AW_ASSETS_ALLOWED_EXTS: %q\n", strings.Join(workflowData.SafeOutputs.UploadAssets.AllowedExts, ",")) - } - } - } - } yaml.WriteString(" run: |\n") yaml.WriteString(" mkdir -p /tmp/gh-aw/mcp-config\n") engine.RenderMCPConfig(yaml, tools, mcpTools, workflowData) diff --git a/pkg/workflow/safe_output_helpers.go b/pkg/workflow/safe_output_helpers.go index 3c2ea016e60..e99058c6b57 100644 --- a/pkg/workflow/safe_output_helpers.go +++ b/pkg/workflow/safe_output_helpers.go @@ -63,6 +63,179 @@ func (c *Compiler) buildGitHubScriptStep(data *WorkflowData, config GitHubScript return steps } +func generateSafeOutputsConfig(data *WorkflowData) string { + // Pass the safe-outputs configuration for validation + if data.SafeOutputs == nil { + return "" + } + // Create a simplified config object for validation + safeOutputsConfig := make(map[string]any) + + // Handle safe-outputs configuration if present + if data.SafeOutputs != nil { + if data.SafeOutputs.CreateIssues != nil { + issueConfig := map[string]any{} + if data.SafeOutputs.CreateIssues.Max > 0 { + issueConfig["max"] = data.SafeOutputs.CreateIssues.Max + } + if data.SafeOutputs.CreateIssues.Min > 0 { + issueConfig["min"] = data.SafeOutputs.CreateIssues.Min + } + safeOutputsConfig["create-issue"] = issueConfig + } + if data.SafeOutputs.AddComments != nil { + commentConfig := map[string]any{} + if data.SafeOutputs.AddComments.Target != "" { + commentConfig["target"] = data.SafeOutputs.AddComments.Target + } + if data.SafeOutputs.AddComments.Max > 0 { + commentConfig["max"] = data.SafeOutputs.AddComments.Max + } + if data.SafeOutputs.AddComments.Min > 0 { + commentConfig["min"] = data.SafeOutputs.AddComments.Min + } + safeOutputsConfig["add-comment"] = commentConfig + } + if data.SafeOutputs.CreateDiscussions != nil { + discussionConfig := map[string]any{} + if data.SafeOutputs.CreateDiscussions.Max > 0 { + discussionConfig["max"] = data.SafeOutputs.CreateDiscussions.Max + } + if data.SafeOutputs.CreateDiscussions.Min > 0 { + discussionConfig["min"] = data.SafeOutputs.CreateDiscussions.Min + } + safeOutputsConfig["create-discussion"] = discussionConfig + } + if data.SafeOutputs.CreatePullRequests != nil { + prConfig := map[string]any{} + // Note: max is always 1 for pull requests, not configurable + if data.SafeOutputs.CreatePullRequests.Min > 0 { + prConfig["min"] = data.SafeOutputs.CreatePullRequests.Min + } + safeOutputsConfig["create-pull-request"] = prConfig + } + if data.SafeOutputs.CreatePullRequestReviewComments != nil { + prReviewCommentConfig := map[string]any{} + if data.SafeOutputs.CreatePullRequestReviewComments.Max > 0 { + prReviewCommentConfig["max"] = data.SafeOutputs.CreatePullRequestReviewComments.Max + } + if data.SafeOutputs.CreatePullRequestReviewComments.Min > 0 { + prReviewCommentConfig["min"] = data.SafeOutputs.CreatePullRequestReviewComments.Min + } + safeOutputsConfig["create-pull-request-review-comment"] = prReviewCommentConfig + } + if data.SafeOutputs.CreateCodeScanningAlerts != nil { + // Security reports typically have unlimited max, but check if configured + securityReportConfig := map[string]any{} + if data.SafeOutputs.CreateCodeScanningAlerts.Max > 0 { + securityReportConfig["max"] = data.SafeOutputs.CreateCodeScanningAlerts.Max + } + if data.SafeOutputs.CreateCodeScanningAlerts.Min > 0 { + securityReportConfig["min"] = data.SafeOutputs.CreateCodeScanningAlerts.Min + } + safeOutputsConfig["create-code-scanning-alert"] = securityReportConfig + } + if data.SafeOutputs.AddLabels != nil { + labelConfig := map[string]any{} + if data.SafeOutputs.AddLabels.Max > 0 { + labelConfig["max"] = data.SafeOutputs.AddLabels.Max + } + if data.SafeOutputs.AddLabels.Min > 0 { + labelConfig["min"] = data.SafeOutputs.AddLabels.Min + } + if len(data.SafeOutputs.AddLabels.Allowed) > 0 { + labelConfig["allowed"] = data.SafeOutputs.AddLabels.Allowed + } + safeOutputsConfig["add-labels"] = labelConfig + } + if data.SafeOutputs.UpdateIssues != nil { + updateConfig := map[string]any{} + if data.SafeOutputs.UpdateIssues.Max > 0 { + updateConfig["max"] = data.SafeOutputs.UpdateIssues.Max + } + if data.SafeOutputs.UpdateIssues.Min > 0 { + updateConfig["min"] = data.SafeOutputs.UpdateIssues.Min + } + safeOutputsConfig["update-issue"] = updateConfig + } + if data.SafeOutputs.PushToPullRequestBranch != nil { + pushToBranchConfig := map[string]any{} + if data.SafeOutputs.PushToPullRequestBranch.Target != "" { + pushToBranchConfig["target"] = data.SafeOutputs.PushToPullRequestBranch.Target + } + if data.SafeOutputs.PushToPullRequestBranch.Max > 0 { + pushToBranchConfig["max"] = data.SafeOutputs.PushToPullRequestBranch.Max + } + if data.SafeOutputs.PushToPullRequestBranch.Min > 0 { + pushToBranchConfig["min"] = data.SafeOutputs.PushToPullRequestBranch.Min + } + safeOutputsConfig["push-to-pull-request-branch"] = pushToBranchConfig + } + if data.SafeOutputs.UploadAssets != nil { + uploadConfig := map[string]any{} + if data.SafeOutputs.UploadAssets.Max > 0 { + uploadConfig["max"] = data.SafeOutputs.UploadAssets.Max + } + if data.SafeOutputs.UploadAssets.Min > 0 { + uploadConfig["min"] = data.SafeOutputs.UploadAssets.Min + } + safeOutputsConfig["upload-asset"] = uploadConfig + } + if data.SafeOutputs.MissingTool != nil { + missingToolConfig := map[string]any{} + if data.SafeOutputs.MissingTool.Max > 0 { + missingToolConfig["max"] = data.SafeOutputs.MissingTool.Max + } + if data.SafeOutputs.MissingTool.Min > 0 { + missingToolConfig["min"] = data.SafeOutputs.MissingTool.Min + } + safeOutputsConfig["missing-tool"] = missingToolConfig + } + } + + // Add safe-jobs configuration from SafeOutputs.Jobs + if len(data.SafeOutputs.Jobs) > 0 { + for jobName, jobConfig := range data.SafeOutputs.Jobs { + safeJobConfig := map[string]any{} + + // Add description if present + if jobConfig.Description != "" { + safeJobConfig["description"] = jobConfig.Description + } + + // Add output if present + if jobConfig.Output != "" { + safeJobConfig["output"] = jobConfig.Output + } + + // Add inputs information + if len(jobConfig.Inputs) > 0 { + inputsConfig := make(map[string]any) + for inputName, inputDef := range jobConfig.Inputs { + inputConfig := map[string]any{ + "type": inputDef.Type, + "description": inputDef.Description, + "required": inputDef.Required, + } + if inputDef.Default != "" { + inputConfig["default"] = inputDef.Default + } + if len(inputDef.Options) > 0 { + inputConfig["options"] = inputDef.Options + } + inputsConfig[inputName] = inputConfig + } + safeJobConfig["inputs"] = inputsConfig + } + + safeOutputsConfig[jobName] = safeJobConfig + } + } + + configJSON, _ := json.Marshal(safeOutputsConfig) + return string(configJSON) +} + // applySafeOutputEnvToMap adds safe-output related environment variables to an env map // This extracts the duplicated safe-output env setup logic across all engines (copilot, codex, claude, custom) func applySafeOutputEnvToMap(env map[string]string, workflowData *WorkflowData) { @@ -72,6 +245,9 @@ func applySafeOutputEnvToMap(env map[string]string, workflowData *WorkflowData) env["GITHUB_AW_SAFE_OUTPUTS"] = "${{ env.GITHUB_AW_SAFE_OUTPUTS }}" + safeOutputConfig := generateSafeOutputsConfig(workflowData) + env["GITHUB_AW_SAFE_OUTPUTS_CONFIG"] = fmt.Sprintf("%q", safeOutputConfig) + // Add staged flag if specified if workflowData.TrialMode || workflowData.SafeOutputs.Staged { env["GITHUB_AW_SAFE_OUTPUTS_STAGED"] = "true" From 2e69babb06e9c8cac2ae5edbc61f23e69ce530de Mon Sep 17 00:00:00 2001 From: Peli de Halleux Date: Tue, 14 Oct 2025 00:40:57 +0000 Subject: [PATCH 7/9] Refactor TestSafeJobsInSafeOutputsConfig to remove unnecessary compiler instantiation --- .github/workflows/brave.lock.yml | 4 +++- .github/workflows/changeset-generator.lock.yml | 4 +++- .github/workflows/issue-classifier.lock.yml | 4 +++- .github/workflows/pdf-summary.lock.yml | 4 +++- .github/workflows/plan.lock.yml | 4 +++- .github/workflows/poem-bot.lock.yml | 4 +++- .github/workflows/q.lock.yml | 4 +++- .github/workflows/scout.lock.yml | 4 +++- .github/workflows/tidy.lock.yml | 4 +++- .github/workflows/unbloat-docs.lock.yml | 4 +++- Makefile | 6 +++++- pkg/workflow/js/add_reaction_and_edit_comment.cjs | 4 +++- pkg/workflow/safe_jobs_test.go | 4 +--- 13 files changed, 39 insertions(+), 15 deletions(-) diff --git a/.github/workflows/brave.lock.yml b/.github/workflows/brave.lock.yml index b98fdfaf97b..fd6c3a84131 100644 --- a/.github/workflows/brave.lock.yml +++ b/.github/workflows/brave.lock.yml @@ -509,11 +509,13 @@ jobs: } async function getDiscussionCommentId(owner, repo, discussionNumber, commentId) { const discussion = await getDiscussionId(owner, repo, discussionNumber); + if (!discussion) + throw new Error(`Discussion #${discussionNumber} not found in ${owner}/${repo}`); const nodeId = context.payload?.comment?.node_id; if (nodeId) { return { id: nodeId, - url: context.payload.comment.html_url || discussion.url, + url: context.payload.comment?.html_url || discussion?.url, }; } throw new Error(`Discussion comment node ID not found in event payload for comment ${commentId}`); diff --git a/.github/workflows/changeset-generator.lock.yml b/.github/workflows/changeset-generator.lock.yml index 8227972fc5c..a5e6bc4c7c6 100644 --- a/.github/workflows/changeset-generator.lock.yml +++ b/.github/workflows/changeset-generator.lock.yml @@ -506,11 +506,13 @@ jobs: } async function getDiscussionCommentId(owner, repo, discussionNumber, commentId) { const discussion = await getDiscussionId(owner, repo, discussionNumber); + if (!discussion) + throw new Error(`Discussion #${discussionNumber} not found in ${owner}/${repo}`); const nodeId = context.payload?.comment?.node_id; if (nodeId) { return { id: nodeId, - url: context.payload.comment.html_url || discussion.url, + url: context.payload.comment?.html_url || discussion?.url, }; } throw new Error(`Discussion comment node ID not found in event payload for comment ${commentId}`); diff --git a/.github/workflows/issue-classifier.lock.yml b/.github/workflows/issue-classifier.lock.yml index 4a098fdfa95..c1f4e129724 100644 --- a/.github/workflows/issue-classifier.lock.yml +++ b/.github/workflows/issue-classifier.lock.yml @@ -507,11 +507,13 @@ jobs: } async function getDiscussionCommentId(owner, repo, discussionNumber, commentId) { const discussion = await getDiscussionId(owner, repo, discussionNumber); + if (!discussion) + throw new Error(`Discussion #${discussionNumber} not found in ${owner}/${repo}`); const nodeId = context.payload?.comment?.node_id; if (nodeId) { return { id: nodeId, - url: context.payload.comment.html_url || discussion.url, + url: context.payload.comment?.html_url || discussion?.url, }; } throw new Error(`Discussion comment node ID not found in event payload for comment ${commentId}`); diff --git a/.github/workflows/pdf-summary.lock.yml b/.github/workflows/pdf-summary.lock.yml index 042aebe702d..3a0da035243 100644 --- a/.github/workflows/pdf-summary.lock.yml +++ b/.github/workflows/pdf-summary.lock.yml @@ -534,11 +534,13 @@ jobs: } async function getDiscussionCommentId(owner, repo, discussionNumber, commentId) { const discussion = await getDiscussionId(owner, repo, discussionNumber); + if (!discussion) + throw new Error(`Discussion #${discussionNumber} not found in ${owner}/${repo}`); const nodeId = context.payload?.comment?.node_id; if (nodeId) { return { id: nodeId, - url: context.payload.comment.html_url || discussion.url, + url: context.payload.comment?.html_url || discussion?.url, }; } throw new Error(`Discussion comment node ID not found in event payload for comment ${commentId}`); diff --git a/.github/workflows/plan.lock.yml b/.github/workflows/plan.lock.yml index e9e5b881479..ecbad958342 100644 --- a/.github/workflows/plan.lock.yml +++ b/.github/workflows/plan.lock.yml @@ -509,11 +509,13 @@ jobs: } async function getDiscussionCommentId(owner, repo, discussionNumber, commentId) { const discussion = await getDiscussionId(owner, repo, discussionNumber); + if (!discussion) + throw new Error(`Discussion #${discussionNumber} not found in ${owner}/${repo}`); const nodeId = context.payload?.comment?.node_id; if (nodeId) { return { id: nodeId, - url: context.payload.comment.html_url || discussion.url, + url: context.payload.comment?.html_url || discussion?.url, }; } throw new Error(`Discussion comment node ID not found in event payload for comment ${commentId}`); diff --git a/.github/workflows/poem-bot.lock.yml b/.github/workflows/poem-bot.lock.yml index 04608a5421f..9a6de9706c2 100644 --- a/.github/workflows/poem-bot.lock.yml +++ b/.github/workflows/poem-bot.lock.yml @@ -517,11 +517,13 @@ jobs: } async function getDiscussionCommentId(owner, repo, discussionNumber, commentId) { const discussion = await getDiscussionId(owner, repo, discussionNumber); + if (!discussion) + throw new Error(`Discussion #${discussionNumber} not found in ${owner}/${repo}`); const nodeId = context.payload?.comment?.node_id; if (nodeId) { return { id: nodeId, - url: context.payload.comment.html_url || discussion.url, + url: context.payload.comment?.html_url || discussion?.url, }; } throw new Error(`Discussion comment node ID not found in event payload for comment ${commentId}`); diff --git a/.github/workflows/q.lock.yml b/.github/workflows/q.lock.yml index edd86459f67..23852b7b27a 100644 --- a/.github/workflows/q.lock.yml +++ b/.github/workflows/q.lock.yml @@ -552,11 +552,13 @@ jobs: } async function getDiscussionCommentId(owner, repo, discussionNumber, commentId) { const discussion = await getDiscussionId(owner, repo, discussionNumber); + if (!discussion) + throw new Error(`Discussion #${discussionNumber} not found in ${owner}/${repo}`); const nodeId = context.payload?.comment?.node_id; if (nodeId) { return { id: nodeId, - url: context.payload.comment.html_url || discussion.url, + url: context.payload.comment?.html_url || discussion?.url, }; } throw new Error(`Discussion comment node ID not found in event payload for comment ${commentId}`); diff --git a/.github/workflows/scout.lock.yml b/.github/workflows/scout.lock.yml index 1c587345c49..be116cdfe2b 100644 --- a/.github/workflows/scout.lock.yml +++ b/.github/workflows/scout.lock.yml @@ -567,11 +567,13 @@ jobs: } async function getDiscussionCommentId(owner, repo, discussionNumber, commentId) { const discussion = await getDiscussionId(owner, repo, discussionNumber); + if (!discussion) + throw new Error(`Discussion #${discussionNumber} not found in ${owner}/${repo}`); const nodeId = context.payload?.comment?.node_id; if (nodeId) { return { id: nodeId, - url: context.payload.comment.html_url || discussion.url, + url: context.payload.comment?.html_url || discussion?.url, }; } throw new Error(`Discussion comment node ID not found in event payload for comment ${commentId}`); diff --git a/.github/workflows/tidy.lock.yml b/.github/workflows/tidy.lock.yml index 8f2983a4263..61f863884ec 100644 --- a/.github/workflows/tidy.lock.yml +++ b/.github/workflows/tidy.lock.yml @@ -357,11 +357,13 @@ jobs: } async function getDiscussionCommentId(owner, repo, discussionNumber, commentId) { const discussion = await getDiscussionId(owner, repo, discussionNumber); + if (!discussion) + throw new Error(`Discussion #${discussionNumber} not found in ${owner}/${repo}`); const nodeId = context.payload?.comment?.node_id; if (nodeId) { return { id: nodeId, - url: context.payload.comment.html_url || discussion.url, + url: context.payload.comment?.html_url || discussion?.url, }; } throw new Error(`Discussion comment node ID not found in event payload for comment ${commentId}`); diff --git a/.github/workflows/unbloat-docs.lock.yml b/.github/workflows/unbloat-docs.lock.yml index fed7e383030..5a4d9e15ee5 100644 --- a/.github/workflows/unbloat-docs.lock.yml +++ b/.github/workflows/unbloat-docs.lock.yml @@ -350,11 +350,13 @@ jobs: } async function getDiscussionCommentId(owner, repo, discussionNumber, commentId) { const discussion = await getDiscussionId(owner, repo, discussionNumber); + if (!discussion) + throw new Error(`Discussion #${discussionNumber} not found in ${owner}/${repo}`); const nodeId = context.payload?.comment?.node_id; if (nodeId) { return { id: nodeId, - url: context.payload.comment.html_url || discussion.url, + url: context.payload.comment?.html_url || discussion?.url, }; } throw new Error(`Discussion comment node ID not found in event payload for comment ${commentId}`); diff --git a/Makefile b/Makefile index c1b76f9f864..723171f44e5 100644 --- a/Makefile +++ b/Makefile @@ -65,9 +65,13 @@ test-perf: # Test JavaScript files .PHONY: test-js -test-js: +test-js: build-js cd pkg/workflow/js && npm run test:js +.PHONY: build-js +build-js: + cd pkg/workflow/js && npm run typecheck + # Test all code (Go and JavaScript) .PHONY: test-all test-all: test test-js diff --git a/pkg/workflow/js/add_reaction_and_edit_comment.cjs b/pkg/workflow/js/add_reaction_and_edit_comment.cjs index 8d06786228b..b06c35f37a5 100644 --- a/pkg/workflow/js/add_reaction_and_edit_comment.cjs +++ b/pkg/workflow/js/add_reaction_and_edit_comment.cjs @@ -252,6 +252,8 @@ async function getDiscussionId(owner, repo, discussionNumber) { async function getDiscussionCommentId(owner, repo, discussionNumber, commentId) { // First, get the discussion ID const discussion = await getDiscussionId(owner, repo, discussionNumber); + if (!discussion) + throw new Error(`Discussion #${discussionNumber} not found in ${owner}/${repo}`); // Then fetch the comment by traversing discussion comments // Note: GitHub's GraphQL API doesn't provide a direct way to query comment by database ID @@ -264,7 +266,7 @@ async function getDiscussionCommentId(owner, repo, discussionNumber, commentId) if (nodeId) { return { id: nodeId, - url: context.payload.comment.html_url || discussion.url, + url: context.payload.comment?.html_url || discussion?.url, }; } diff --git a/pkg/workflow/safe_jobs_test.go b/pkg/workflow/safe_jobs_test.go index 67226c75c54..905fe41a1eb 100644 --- a/pkg/workflow/safe_jobs_test.go +++ b/pkg/workflow/safe_jobs_test.go @@ -285,8 +285,6 @@ func TestBuildSafeJobsWithNoConfiguration(t *testing.T) { } func TestSafeJobsInSafeOutputsConfig(t *testing.T) { - c := NewCompiler(false, "", "test") - workflowData := &WorkflowData{ SafeOutputs: &SafeOutputsConfig{ Jobs: map[string]*SafeJobConfig{ @@ -314,7 +312,7 @@ func TestSafeJobsInSafeOutputsConfig(t *testing.T) { }, } - configJSON := c.generateSafeOutputsConfig(workflowData) + configJSON := generateSafeOutputsConfig(workflowData) if configJSON == "" { t.Fatal("Expected safe-outputs config JSON to be generated") From e90c590e3f9feea7f1bab9b881144512d314ff38 Mon Sep 17 00:00:00 2001 From: Peli de Halleux Date: Tue, 14 Oct 2025 00:53:24 +0000 Subject: [PATCH 8/9] Enhance documentation structure for shared agentic workflows and clarify cache configurations --- .../github-agentic-workflows.instructions.md | 46 +++-- .../create-shared-agentic-workflow.prompt.md | 185 +++--------------- .../create-shared-agentic-workflow.prompt.md | 185 +++--------------- 3 files changed, 86 insertions(+), 330 deletions(-) diff --git a/.github/instructions/github-agentic-workflows.instructions.md b/.github/instructions/github-agentic-workflows.instructions.md index e2326c9f15e..39ef9d41e86 100644 --- a/.github/instructions/github-agentic-workflows.instructions.md +++ b/.github/instructions/github-agentic-workflows.instructions.md @@ -250,20 +250,47 @@ tools: key: custom-memory-${{ github.run_id }} ``` +**Multiple Caches (Array Notation):** +```yaml +tools: + cache-memory: + - id: default + key: memory-default + - id: session + key: memory-session + - id: logs +``` + **How It Works:** -- Mounts a memory MCP server at `/tmp/gh-aw/cache-memory/` that persists across workflow runs +- **Single Cache**: Mounts a memory MCP server at `/tmp/gh-aw/cache-memory/` that persists across workflow runs +- **Multiple Caches**: Each cache mounts at `/tmp/gh-aw/cache-memory/{id}/` with its own persistence - Uses `actions/cache` with resolution field so the last cache wins - Automatically adds the memory MCP server to available tools - Cache steps are automatically added to the workflow job - Restore keys are automatically generated by splitting the cache key on '-' **Supported Parameters:** + +For single cache (object notation): - `key:` - Custom cache key (defaults to `memory-${{ github.workflow }}-${{ github.run_id }}`) +For multiple caches (array notation): +- `id:` - Cache identifier (required for array notation, defaults to "default" if omitted) +- `key:` - Custom cache key (defaults to `memory-{id}-${{ github.workflow }}-${{ github.run_id }}`) +- `retention-days:` - Number of days to retain artifacts (1-90 days) + **Restore Key Generation:** The system automatically generates restore keys by progressively splitting the cache key on '-': - Key: `custom-memory-project-v1-123` → Restore keys: `custom-memory-project-v1-`, `custom-memory-project-`, `custom-memory-` +**Prompt Injection:** +When cache-memory is enabled, the agent receives instructions about available cache folders: +- Single cache: Information about `/tmp/gh-aw/cache-memory/` +- Multiple caches: List of all cache folders with their IDs and paths + +**Import Support:** +Cache-memory configurations can be imported from shared agentic workflows using the `imports:` field. + The memory MCP server is automatically configured when `cache-memory` is enabled and works with both Claude and Custom engines. ## Output Processing and Issue Creation @@ -476,23 +503,6 @@ tools: - "git status" ``` -### GitHub Tools - -The GitHub MCP server provides tools for repository operations. As of v0.18.0, pull request tools have been consolidated into a single `pull_request_read` tool with a method parameter: - -```yaml -tools: - github: - allowed: - - pull_request_read # Consolidated PR tool (replaces get_pull_request, get_pull_request_diff, etc.) - - get_issue # Issue operations - - list_commits # Repository operations - - get_label # Label management (new in v0.18.0) - - list_label # Label listing (new in v0.18.0) -``` - -**Note**: The `pull_request_read` tool replaces multiple individual PR tools (get_pull_request, get_pull_request_comments, get_pull_request_diff, get_pull_request_files, get_pull_request_reviews, get_pull_request_status, get_pull_request_review_comments) with a unified interface using method parameters (get, get_diff, get_status, get_files, get_review_comments, get_reviews). - ### Custom MCP Tools ```yaml mcp-servers: diff --git a/.github/prompts/create-shared-agentic-workflow.prompt.md b/.github/prompts/create-shared-agentic-workflow.prompt.md index fe16811e4ca..a8c955bf604 100644 --- a/.github/prompts/create-shared-agentic-workflow.prompt.md +++ b/.github/prompts/create-shared-agentic-workflow.prompt.md @@ -37,11 +37,14 @@ You are a conversational chat agent that interacts with the user to design secur - Common safe outputs: `create-issue`, `add-comment`, `create-pull-request`, `update-issue` - Let consuming workflows decide which safe outputs to enable -## Workflow Component Structure +**Documentation** +- Place documentation as a XML comment in the markdown body +- Avoid adding comments to the front matter itself +- Provide links to all sources of informations (URL docs) used to generate the component -### Shared Component Format +## Workflow Component Structure -Shared components are frontmatter-only files: +The shared workflow file is a markdown file with frontmatter. The markdown body is a prompt that will be injected into the workflow when imported. ```yaml --- @@ -55,6 +58,10 @@ mcp-servers: - read_tool_1 - read_tool_2 --- + +This text will be in the final prompt. ``` ### Container Configuration Patterns @@ -76,7 +83,7 @@ mcp-servers: serena: container: "ghcr.io/oraios/serena" version: "latest" - args: + args: # args come before the docker image argument - "-v" - "${{ github.workspace }}:/workspace:ro" - "-w" @@ -94,16 +101,7 @@ mcp-servers: allowed: ["read_wiki_structure", "read_wiki_contents", "ask_question"] ``` -### Read-Only Tool Patterns - -**GitHub Read-Only**: -```yaml -tools: - github: - read-only: true -``` - -**Selective Tool Allowlist**: +### Selective Tool Allowlist ```yaml mcp-servers: custom-api: @@ -150,102 +148,29 @@ Based on the MCP server: ### Step 4: Document Usage Create a comment header explaining: -```yaml +```markdown --- -# DeepWiki MCP Server -# Provides read-only access to GitHub repository documentation -# -# Required secrets: None (public service) -# Available tools: -# - read_wiki_structure: List documentation topics -# - read_wiki_contents: View documentation -# - ask_question: AI-powered Q&A -# -# Usage in workflows: -# imports: -# - shared/mcp/deepwiki.md - mcp-servers: deepwiki: url: "https://mcp.deepwiki.com/sse" allowed: ["*"] --- + ``` -## Example: DeepWiki Shared Component - -Based on https://docs.devin.ai/work-with-devin/deepwiki-mcp: - -```yaml ---- -# DeepWiki MCP Server -# Remote HTTP MCP server for GitHub repository documentation and search -# -# No authentication required - public service -# Documentation: https://mcp.deepwiki.com/ -# -# Available tools: -# - read_wiki_structure: Retrieves documentation topics for a repo -# - read_wiki_contents: Views documentation about a repo -# - ask_question: AI-powered Q&A about a repo -# -# Usage: -# imports: -# - shared/mcp/deepwiki.md - -mcp-servers: - deepwiki: - url: "https://mcp.deepwiki.com/sse" - allowed: - - read_wiki_structure - - read_wiki_contents - - ask_question ---- -``` - -## Importing Shared Components - -In main workflows: - -```yaml ---- -on: workflow_dispatch -permissions: - contents: read -engine: copilot -imports: - - shared/mcp/deepwiki.md - - shared/mcp/tavily.md -safe-outputs: - add-comment: - max: 1 ---- - -# Research Agent - -Use DeepWiki to research repository documentation... -``` - -## Security Best Practices - -### Container Security -- Pin specific version tags, avoid `latest` in production -- Use official container registries -- Mount volumes as read-only (`:ro`) when possible -- Limit network access with `network:` configuration - -### Secret Management -- Always use GitHub secrets for API keys: `${{ secrets.NAME }}` -- Document required secrets in component comments -- Never hardcode credentials -- Use meaningful secret names (e.g., `TAVILY_API_KEY`, not `API_KEY`) - -### Permission Isolation -- Keep shared components read-only by default -- Use `safe-outputs:` in consuming workflows for write operations -- Document which safe outputs are recommended -- Never include `permissions:` in shared components - ## Docker Container Best Practices ### Version Pinning @@ -282,62 +207,10 @@ env: DEBUG: "false" ``` -## Common Shared Components - -### Research & Documentation -- DeepWiki: Repository documentation and Q&A -- Microsoft Docs: Microsoft documentation search -- Tavily: Web search and research - -### Development Tools -- AST-grep: Code structure analysis -- Playwright: Browser automation -- GitHub: Repository operations (read-only) - -### Data & APIs -- Notion: Workspace integration (read-only) -- Google Drive: File access (read-only) -- Slack: Message search (read-only) - -## Conversation Flow - -1. **Understand Requirements** - - Ask: "Which MCP server would you like to wrap as a shared component?" - - Get documentation URL or server description - -2. **Analyze Server Capabilities** - - Review available tools - - Identify read vs write operations - - Check for official Docker container - - Note authentication requirements - -3. **Design Component** - - Choose transport (container vs HTTP) - - Create read-only tool allowlist - - Configure environment variables - - Add custom args if needed - -4. **Create Shared File** - - Generate `.github/workflows/shared/-mcp.md` - - Add documentation comments - - Test compilation: `gh aw compile` - -5. **Provide Usage Example** - - Show how to import in workflows - - Suggest appropriate safe-outputs - - Document required secrets - ## Testing Shared Components ```bash -# Inspect the MCP server configuration -gh aw mcp inspect workflow-name --server server-name --verbose - -# Compile workflow to validate -gh aw compile workflow-name - -# Test with simple workflow -gh aw compile test-workflow --strict +gh aw compile workflow-name --strict ``` ## Guidelines @@ -347,7 +220,7 @@ gh aw compile test-workflow --strict - Default to read-only tool configurations - Move write operations to `safe-outputs:` in consuming workflows - Document required secrets and tool capabilities clearly -- Use semantic naming: `-mcp.md` +- Use semantic naming: `.github/workflows/shared/mcp/.md` - Keep shared components focused on a single MCP server - Test compilation after creating shared components - Follow security best practices for secrets and permissions diff --git a/pkg/cli/templates/create-shared-agentic-workflow.prompt.md b/pkg/cli/templates/create-shared-agentic-workflow.prompt.md index fe16811e4ca..a8c955bf604 100644 --- a/pkg/cli/templates/create-shared-agentic-workflow.prompt.md +++ b/pkg/cli/templates/create-shared-agentic-workflow.prompt.md @@ -37,11 +37,14 @@ You are a conversational chat agent that interacts with the user to design secur - Common safe outputs: `create-issue`, `add-comment`, `create-pull-request`, `update-issue` - Let consuming workflows decide which safe outputs to enable -## Workflow Component Structure +**Documentation** +- Place documentation as a XML comment in the markdown body +- Avoid adding comments to the front matter itself +- Provide links to all sources of informations (URL docs) used to generate the component -### Shared Component Format +## Workflow Component Structure -Shared components are frontmatter-only files: +The shared workflow file is a markdown file with frontmatter. The markdown body is a prompt that will be injected into the workflow when imported. ```yaml --- @@ -55,6 +58,10 @@ mcp-servers: - read_tool_1 - read_tool_2 --- + +This text will be in the final prompt. ``` ### Container Configuration Patterns @@ -76,7 +83,7 @@ mcp-servers: serena: container: "ghcr.io/oraios/serena" version: "latest" - args: + args: # args come before the docker image argument - "-v" - "${{ github.workspace }}:/workspace:ro" - "-w" @@ -94,16 +101,7 @@ mcp-servers: allowed: ["read_wiki_structure", "read_wiki_contents", "ask_question"] ``` -### Read-Only Tool Patterns - -**GitHub Read-Only**: -```yaml -tools: - github: - read-only: true -``` - -**Selective Tool Allowlist**: +### Selective Tool Allowlist ```yaml mcp-servers: custom-api: @@ -150,102 +148,29 @@ Based on the MCP server: ### Step 4: Document Usage Create a comment header explaining: -```yaml +```markdown --- -# DeepWiki MCP Server -# Provides read-only access to GitHub repository documentation -# -# Required secrets: None (public service) -# Available tools: -# - read_wiki_structure: List documentation topics -# - read_wiki_contents: View documentation -# - ask_question: AI-powered Q&A -# -# Usage in workflows: -# imports: -# - shared/mcp/deepwiki.md - mcp-servers: deepwiki: url: "https://mcp.deepwiki.com/sse" allowed: ["*"] --- + ``` -## Example: DeepWiki Shared Component - -Based on https://docs.devin.ai/work-with-devin/deepwiki-mcp: - -```yaml ---- -# DeepWiki MCP Server -# Remote HTTP MCP server for GitHub repository documentation and search -# -# No authentication required - public service -# Documentation: https://mcp.deepwiki.com/ -# -# Available tools: -# - read_wiki_structure: Retrieves documentation topics for a repo -# - read_wiki_contents: Views documentation about a repo -# - ask_question: AI-powered Q&A about a repo -# -# Usage: -# imports: -# - shared/mcp/deepwiki.md - -mcp-servers: - deepwiki: - url: "https://mcp.deepwiki.com/sse" - allowed: - - read_wiki_structure - - read_wiki_contents - - ask_question ---- -``` - -## Importing Shared Components - -In main workflows: - -```yaml ---- -on: workflow_dispatch -permissions: - contents: read -engine: copilot -imports: - - shared/mcp/deepwiki.md - - shared/mcp/tavily.md -safe-outputs: - add-comment: - max: 1 ---- - -# Research Agent - -Use DeepWiki to research repository documentation... -``` - -## Security Best Practices - -### Container Security -- Pin specific version tags, avoid `latest` in production -- Use official container registries -- Mount volumes as read-only (`:ro`) when possible -- Limit network access with `network:` configuration - -### Secret Management -- Always use GitHub secrets for API keys: `${{ secrets.NAME }}` -- Document required secrets in component comments -- Never hardcode credentials -- Use meaningful secret names (e.g., `TAVILY_API_KEY`, not `API_KEY`) - -### Permission Isolation -- Keep shared components read-only by default -- Use `safe-outputs:` in consuming workflows for write operations -- Document which safe outputs are recommended -- Never include `permissions:` in shared components - ## Docker Container Best Practices ### Version Pinning @@ -282,62 +207,10 @@ env: DEBUG: "false" ``` -## Common Shared Components - -### Research & Documentation -- DeepWiki: Repository documentation and Q&A -- Microsoft Docs: Microsoft documentation search -- Tavily: Web search and research - -### Development Tools -- AST-grep: Code structure analysis -- Playwright: Browser automation -- GitHub: Repository operations (read-only) - -### Data & APIs -- Notion: Workspace integration (read-only) -- Google Drive: File access (read-only) -- Slack: Message search (read-only) - -## Conversation Flow - -1. **Understand Requirements** - - Ask: "Which MCP server would you like to wrap as a shared component?" - - Get documentation URL or server description - -2. **Analyze Server Capabilities** - - Review available tools - - Identify read vs write operations - - Check for official Docker container - - Note authentication requirements - -3. **Design Component** - - Choose transport (container vs HTTP) - - Create read-only tool allowlist - - Configure environment variables - - Add custom args if needed - -4. **Create Shared File** - - Generate `.github/workflows/shared/-mcp.md` - - Add documentation comments - - Test compilation: `gh aw compile` - -5. **Provide Usage Example** - - Show how to import in workflows - - Suggest appropriate safe-outputs - - Document required secrets - ## Testing Shared Components ```bash -# Inspect the MCP server configuration -gh aw mcp inspect workflow-name --server server-name --verbose - -# Compile workflow to validate -gh aw compile workflow-name - -# Test with simple workflow -gh aw compile test-workflow --strict +gh aw compile workflow-name --strict ``` ## Guidelines @@ -347,7 +220,7 @@ gh aw compile test-workflow --strict - Default to read-only tool configurations - Move write operations to `safe-outputs:` in consuming workflows - Document required secrets and tool capabilities clearly -- Use semantic naming: `-mcp.md` +- Use semantic naming: `.github/workflows/shared/mcp/.md` - Keep shared components focused on a single MCP server - Test compilation after creating shared components - Follow security best practices for secrets and permissions From 0d85f9b19f6957e57ca1ce8bf1854adefa034ba9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 14 Oct 2025 00:54:09 +0000 Subject: [PATCH 9/9] Fix test expectations for GITHUB_AW_SAFE_OUTPUTS_CONFIG env var Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/workflow/js/add_reaction_and_edit_comment.cjs | 3 +-- pkg/workflow/safe_output_helpers_test.go | 6 +++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/pkg/workflow/js/add_reaction_and_edit_comment.cjs b/pkg/workflow/js/add_reaction_and_edit_comment.cjs index b06c35f37a5..8ca7588d4cf 100644 --- a/pkg/workflow/js/add_reaction_and_edit_comment.cjs +++ b/pkg/workflow/js/add_reaction_and_edit_comment.cjs @@ -252,8 +252,7 @@ async function getDiscussionId(owner, repo, discussionNumber) { async function getDiscussionCommentId(owner, repo, discussionNumber, commentId) { // First, get the discussion ID const discussion = await getDiscussionId(owner, repo, discussionNumber); - if (!discussion) - throw new Error(`Discussion #${discussionNumber} not found in ${owner}/${repo}`); + if (!discussion) throw new Error(`Discussion #${discussionNumber} not found in ${owner}/${repo}`); // Then fetch the comment by traversing discussion comments // Note: GitHub's GraphQL API doesn't provide a direct way to query comment by database ID diff --git a/pkg/workflow/safe_output_helpers_test.go b/pkg/workflow/safe_output_helpers_test.go index abc44bedf08..fb4c5952626 100644 --- a/pkg/workflow/safe_output_helpers_test.go +++ b/pkg/workflow/safe_output_helpers_test.go @@ -220,7 +220,8 @@ func TestApplySafeOutputEnvToMap(t *testing.T) { SafeOutputs: &SafeOutputsConfig{}, }, expected: map[string]string{ - "GITHUB_AW_SAFE_OUTPUTS": "${{ env.GITHUB_AW_SAFE_OUTPUTS }}", + "GITHUB_AW_SAFE_OUTPUTS": "${{ env.GITHUB_AW_SAFE_OUTPUTS }}", + "GITHUB_AW_SAFE_OUTPUTS_CONFIG": "\"{}\"", }, }, { @@ -232,6 +233,7 @@ func TestApplySafeOutputEnvToMap(t *testing.T) { }, expected: map[string]string{ "GITHUB_AW_SAFE_OUTPUTS": "${{ env.GITHUB_AW_SAFE_OUTPUTS }}", + "GITHUB_AW_SAFE_OUTPUTS_CONFIG": "\"{}\"", "GITHUB_AW_SAFE_OUTPUTS_STAGED": "true", }, }, @@ -244,6 +246,7 @@ func TestApplySafeOutputEnvToMap(t *testing.T) { }, expected: map[string]string{ "GITHUB_AW_SAFE_OUTPUTS": "${{ env.GITHUB_AW_SAFE_OUTPUTS }}", + "GITHUB_AW_SAFE_OUTPUTS_CONFIG": "\"{}\"", "GITHUB_AW_SAFE_OUTPUTS_STAGED": "true", "GITHUB_AW_TARGET_REPO_SLUG": "owner/repo", }, @@ -261,6 +264,7 @@ func TestApplySafeOutputEnvToMap(t *testing.T) { }, expected: map[string]string{ "GITHUB_AW_SAFE_OUTPUTS": "${{ env.GITHUB_AW_SAFE_OUTPUTS }}", + "GITHUB_AW_SAFE_OUTPUTS_CONFIG": "\"{\\\"upload-asset\\\":{}}\"", "GITHUB_AW_ASSETS_BRANCH": "\"gh-aw-assets\"", "GITHUB_AW_ASSETS_MAX_SIZE_KB": "10240", "GITHUB_AW_ASSETS_ALLOWED_EXTS": "\".png,.jpg,.jpeg\"",