From b786989b90b4be28848413d7525bf6d504b2c30f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Apr 2026 04:39:05 +0000 Subject: [PATCH 1/4] Initial plan From 7c55e8371f107322edd3c547df0335da3929bb62 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Apr 2026 05:22:13 +0000 Subject: [PATCH 2/4] fix: mirror Gemini API key to GOOGLE_API_KEY for AWF proxy routing Agent-Logs-Url: https://github.com/github/gh-aw/sessions/65da78ce-391a-4e0c-9b23-71eb231b4992 Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/workflow/gemini_engine.go | 19 ++++++++++++++----- pkg/workflow/gemini_engine_test.go | 24 ++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/pkg/workflow/gemini_engine.go b/pkg/workflow/gemini_engine.go index 05d9d5b676b..0aed12a09f9 100644 --- a/pkg/workflow/gemini_engine.go +++ b/pkg/workflow/gemini_engine.go @@ -38,12 +38,13 @@ func (e *GeminiEngine) GetModelEnvVarName() string { return constants.GeminiCLIModelEnvVar } -// GetRequiredSecretNames returns the list of secrets required by the Gemini engine -// This includes GEMINI_API_KEY and optionally MCP_GATEWAY_API_KEY, GITHUB_MCP_SERVER_TOKEN, -// HTTP MCP header secrets, and mcp-scripts secrets +// GetRequiredSecretNames returns the list of secrets required by the Gemini engine. +// This includes GEMINI_API_KEY, the GOOGLE_API_KEY compatibility env key (used by +// AWF proxy Gemini routing), and optionally MCP_GATEWAY_API_KEY, +// GITHUB_MCP_SERVER_TOKEN, HTTP MCP header secrets, and mcp-scripts secrets. func (e *GeminiEngine) GetRequiredSecretNames(workflowData *WorkflowData) []string { geminiLog.Print("Collecting required secrets for Gemini engine") - secrets := []string{"GEMINI_API_KEY"} + secrets := []string{"GEMINI_API_KEY", "GOOGLE_API_KEY"} // Add common MCP secrets (MCP_GATEWAY_API_KEY if MCP servers present, mcp-scripts secrets) secrets = append(secrets, collectCommonMCPSecrets(workflowData)...) @@ -218,7 +219,7 @@ func (e *GeminiEngine) GetExecutionSteps(workflowData *WorkflowData, logFile str PathSetup: "touch " + AgentStepSummaryPath, // Exclude every env var whose step-env value is a secret so the agent // cannot read raw token values via bash tools (env / printenv). - ExcludeEnvVarNames: ComputeAWFExcludeEnvVarNames(workflowData, []string{"GEMINI_API_KEY"}), + ExcludeEnvVarNames: ComputeAWFExcludeEnvVarNames(workflowData, []string{"GEMINI_API_KEY", "GOOGLE_API_KEY"}), }) } else { command = fmt.Sprintf(`set -o pipefail @@ -301,6 +302,14 @@ touch %s geminiLog.Printf("Added %d custom env vars from agent config", len(agentConfig.Env)) } + // Mirror GEMINI_API_KEY to GOOGLE_API_KEY unless explicitly overridden. + // Gemini CLI reads GEMINI_API_KEY, while AWF's Gemini proxy handler reads GOOGLE_API_KEY. + if _, hasGoogleAPIKey := env["GOOGLE_API_KEY"]; !hasGoogleAPIKey { + if geminiAPIKey, hasGeminiAPIKey := env["GEMINI_API_KEY"]; hasGeminiAPIKey { + env["GOOGLE_API_KEY"] = geminiAPIKey + } + } + // Generate the execution step stepLines := []string{ " - name: Execute Gemini CLI", diff --git a/pkg/workflow/gemini_engine_test.go b/pkg/workflow/gemini_engine_test.go index 30dce4c488a..91955bc8dab 100644 --- a/pkg/workflow/gemini_engine_test.go +++ b/pkg/workflow/gemini_engine_test.go @@ -34,6 +34,7 @@ func TestGeminiEngine(t *testing.T) { } secrets := engine.GetRequiredSecretNames(workflowData) assert.Contains(t, secrets, "GEMINI_API_KEY", "Should require GEMINI_API_KEY") + assert.Contains(t, secrets, "GOOGLE_API_KEY", "Should include GOOGLE_API_KEY compatibility env key") }) t.Run("required secrets with MCP servers", func(t *testing.T) { @@ -48,6 +49,7 @@ func TestGeminiEngine(t *testing.T) { } secrets := engine.GetRequiredSecretNames(workflowData) assert.Contains(t, secrets, "GEMINI_API_KEY", "Should require GEMINI_API_KEY") + assert.Contains(t, secrets, "GOOGLE_API_KEY", "Should include GOOGLE_API_KEY compatibility env key") assert.Contains(t, secrets, "MCP_GATEWAY_API_KEY", "Should require MCP_GATEWAY_API_KEY when MCP servers present") assert.Contains(t, secrets, "GITHUB_MCP_SERVER_TOKEN", "Should require GITHUB_MCP_SERVER_TOKEN for GitHub tool") }) @@ -160,6 +162,7 @@ func TestGeminiEngineExecution(t *testing.T) { assert.Contains(t, stepContent, `--prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"`, "Should include prompt argument with correct shell quoting") assert.Contains(t, stepContent, "/tmp/test.log", "Should include log file") assert.Contains(t, stepContent, "GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}", "Should set GEMINI_API_KEY env var") + assert.Contains(t, stepContent, "GOOGLE_API_KEY: ${{ secrets.GEMINI_API_KEY }}", "Should set GOOGLE_API_KEY compatibility env var") }) t.Run("with model", func(t *testing.T) { @@ -279,6 +282,26 @@ func TestGeminiEngineExecution(t *testing.T) { // The user-provided value should override the default token expression assert.Contains(t, stepContent, "GEMINI_API_KEY: ${{ secrets.MY_ORG_GEMINI_KEY }}", "engine.env should override the default GEMINI_API_KEY expression") assert.NotContains(t, stepContent, "GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}", "Default GEMINI_API_KEY expression should be replaced by engine.env") + assert.Contains(t, stepContent, "GOOGLE_API_KEY: ${{ secrets.MY_ORG_GEMINI_KEY }}", "GOOGLE_API_KEY should mirror overridden GEMINI_API_KEY") + }) + + t.Run("engine env can override GOOGLE_API_KEY explicitly", func(t *testing.T) { + workflowData := &WorkflowData{ + Name: "test-workflow", + EngineConfig: &EngineConfig{ + Env: map[string]string{ + "GEMINI_API_KEY": "${{ secrets.MY_ORG_GEMINI_KEY }}", + "GOOGLE_API_KEY": "${{ secrets.MY_PROXY_GOOGLE_KEY }}", + }, + }, + } + + steps := engine.GetExecutionSteps(workflowData, "/tmp/test.log") + require.Len(t, steps, 2, "Should generate settings step and execution step") + + stepContent := strings.Join(steps[1], "\n") + assert.Contains(t, stepContent, "GEMINI_API_KEY: ${{ secrets.MY_ORG_GEMINI_KEY }}", "GEMINI_API_KEY should use configured override") + assert.Contains(t, stepContent, "GOOGLE_API_KEY: ${{ secrets.MY_PROXY_GOOGLE_KEY }}", "GOOGLE_API_KEY should use explicit override when provided") }) t.Run("engine env adds custom non-secret env vars", func(t *testing.T) { @@ -340,6 +363,7 @@ func TestGeminiEngineFirewallIntegration(t *testing.T) { assert.Contains(t, stepContent, "awf", "Should use AWF when firewall is enabled") assert.Contains(t, stepContent, "--allow-domains", "Should include allow-domains flag") assert.Contains(t, stepContent, "--enable-api-proxy", "Should include --enable-api-proxy flag") + assert.Contains(t, stepContent, "--exclude-env GOOGLE_API_KEY", "Should exclude GOOGLE_API_KEY from agent container env") assert.Contains(t, stepContent, "GEMINI_API_BASE_URL: http://host.docker.internal:10003", "Should set GEMINI_API_BASE_URL to LLM gateway URL") }) From 0fab7f56bb93fc7dfcf46697eca08d6d651fbfd2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Apr 2026 06:36:05 +0000 Subject: [PATCH 3/4] revert: remove unsupported GOOGLE_API_KEY mapping for Gemini AWF proxy Agent-Logs-Url: https://github.com/github/gh-aw/sessions/8de9e6c1-7d1f-48b0-8f7f-df4574244374 Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/workflow/gemini_engine.go | 19 +++++-------------- pkg/workflow/gemini_engine_test.go | 24 ------------------------ 2 files changed, 5 insertions(+), 38 deletions(-) diff --git a/pkg/workflow/gemini_engine.go b/pkg/workflow/gemini_engine.go index 0aed12a09f9..05d9d5b676b 100644 --- a/pkg/workflow/gemini_engine.go +++ b/pkg/workflow/gemini_engine.go @@ -38,13 +38,12 @@ func (e *GeminiEngine) GetModelEnvVarName() string { return constants.GeminiCLIModelEnvVar } -// GetRequiredSecretNames returns the list of secrets required by the Gemini engine. -// This includes GEMINI_API_KEY, the GOOGLE_API_KEY compatibility env key (used by -// AWF proxy Gemini routing), and optionally MCP_GATEWAY_API_KEY, -// GITHUB_MCP_SERVER_TOKEN, HTTP MCP header secrets, and mcp-scripts secrets. +// GetRequiredSecretNames returns the list of secrets required by the Gemini engine +// This includes GEMINI_API_KEY and optionally MCP_GATEWAY_API_KEY, GITHUB_MCP_SERVER_TOKEN, +// HTTP MCP header secrets, and mcp-scripts secrets func (e *GeminiEngine) GetRequiredSecretNames(workflowData *WorkflowData) []string { geminiLog.Print("Collecting required secrets for Gemini engine") - secrets := []string{"GEMINI_API_KEY", "GOOGLE_API_KEY"} + secrets := []string{"GEMINI_API_KEY"} // Add common MCP secrets (MCP_GATEWAY_API_KEY if MCP servers present, mcp-scripts secrets) secrets = append(secrets, collectCommonMCPSecrets(workflowData)...) @@ -219,7 +218,7 @@ func (e *GeminiEngine) GetExecutionSteps(workflowData *WorkflowData, logFile str PathSetup: "touch " + AgentStepSummaryPath, // Exclude every env var whose step-env value is a secret so the agent // cannot read raw token values via bash tools (env / printenv). - ExcludeEnvVarNames: ComputeAWFExcludeEnvVarNames(workflowData, []string{"GEMINI_API_KEY", "GOOGLE_API_KEY"}), + ExcludeEnvVarNames: ComputeAWFExcludeEnvVarNames(workflowData, []string{"GEMINI_API_KEY"}), }) } else { command = fmt.Sprintf(`set -o pipefail @@ -302,14 +301,6 @@ touch %s geminiLog.Printf("Added %d custom env vars from agent config", len(agentConfig.Env)) } - // Mirror GEMINI_API_KEY to GOOGLE_API_KEY unless explicitly overridden. - // Gemini CLI reads GEMINI_API_KEY, while AWF's Gemini proxy handler reads GOOGLE_API_KEY. - if _, hasGoogleAPIKey := env["GOOGLE_API_KEY"]; !hasGoogleAPIKey { - if geminiAPIKey, hasGeminiAPIKey := env["GEMINI_API_KEY"]; hasGeminiAPIKey { - env["GOOGLE_API_KEY"] = geminiAPIKey - } - } - // Generate the execution step stepLines := []string{ " - name: Execute Gemini CLI", diff --git a/pkg/workflow/gemini_engine_test.go b/pkg/workflow/gemini_engine_test.go index 91955bc8dab..30dce4c488a 100644 --- a/pkg/workflow/gemini_engine_test.go +++ b/pkg/workflow/gemini_engine_test.go @@ -34,7 +34,6 @@ func TestGeminiEngine(t *testing.T) { } secrets := engine.GetRequiredSecretNames(workflowData) assert.Contains(t, secrets, "GEMINI_API_KEY", "Should require GEMINI_API_KEY") - assert.Contains(t, secrets, "GOOGLE_API_KEY", "Should include GOOGLE_API_KEY compatibility env key") }) t.Run("required secrets with MCP servers", func(t *testing.T) { @@ -49,7 +48,6 @@ func TestGeminiEngine(t *testing.T) { } secrets := engine.GetRequiredSecretNames(workflowData) assert.Contains(t, secrets, "GEMINI_API_KEY", "Should require GEMINI_API_KEY") - assert.Contains(t, secrets, "GOOGLE_API_KEY", "Should include GOOGLE_API_KEY compatibility env key") assert.Contains(t, secrets, "MCP_GATEWAY_API_KEY", "Should require MCP_GATEWAY_API_KEY when MCP servers present") assert.Contains(t, secrets, "GITHUB_MCP_SERVER_TOKEN", "Should require GITHUB_MCP_SERVER_TOKEN for GitHub tool") }) @@ -162,7 +160,6 @@ func TestGeminiEngineExecution(t *testing.T) { assert.Contains(t, stepContent, `--prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"`, "Should include prompt argument with correct shell quoting") assert.Contains(t, stepContent, "/tmp/test.log", "Should include log file") assert.Contains(t, stepContent, "GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}", "Should set GEMINI_API_KEY env var") - assert.Contains(t, stepContent, "GOOGLE_API_KEY: ${{ secrets.GEMINI_API_KEY }}", "Should set GOOGLE_API_KEY compatibility env var") }) t.Run("with model", func(t *testing.T) { @@ -282,26 +279,6 @@ func TestGeminiEngineExecution(t *testing.T) { // The user-provided value should override the default token expression assert.Contains(t, stepContent, "GEMINI_API_KEY: ${{ secrets.MY_ORG_GEMINI_KEY }}", "engine.env should override the default GEMINI_API_KEY expression") assert.NotContains(t, stepContent, "GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}", "Default GEMINI_API_KEY expression should be replaced by engine.env") - assert.Contains(t, stepContent, "GOOGLE_API_KEY: ${{ secrets.MY_ORG_GEMINI_KEY }}", "GOOGLE_API_KEY should mirror overridden GEMINI_API_KEY") - }) - - t.Run("engine env can override GOOGLE_API_KEY explicitly", func(t *testing.T) { - workflowData := &WorkflowData{ - Name: "test-workflow", - EngineConfig: &EngineConfig{ - Env: map[string]string{ - "GEMINI_API_KEY": "${{ secrets.MY_ORG_GEMINI_KEY }}", - "GOOGLE_API_KEY": "${{ secrets.MY_PROXY_GOOGLE_KEY }}", - }, - }, - } - - steps := engine.GetExecutionSteps(workflowData, "/tmp/test.log") - require.Len(t, steps, 2, "Should generate settings step and execution step") - - stepContent := strings.Join(steps[1], "\n") - assert.Contains(t, stepContent, "GEMINI_API_KEY: ${{ secrets.MY_ORG_GEMINI_KEY }}", "GEMINI_API_KEY should use configured override") - assert.Contains(t, stepContent, "GOOGLE_API_KEY: ${{ secrets.MY_PROXY_GOOGLE_KEY }}", "GOOGLE_API_KEY should use explicit override when provided") }) t.Run("engine env adds custom non-secret env vars", func(t *testing.T) { @@ -363,7 +340,6 @@ func TestGeminiEngineFirewallIntegration(t *testing.T) { assert.Contains(t, stepContent, "awf", "Should use AWF when firewall is enabled") assert.Contains(t, stepContent, "--allow-domains", "Should include allow-domains flag") assert.Contains(t, stepContent, "--enable-api-proxy", "Should include --enable-api-proxy flag") - assert.Contains(t, stepContent, "--exclude-env GOOGLE_API_KEY", "Should exclude GOOGLE_API_KEY from agent container env") assert.Contains(t, stepContent, "GEMINI_API_BASE_URL: http://host.docker.internal:10003", "Should set GEMINI_API_BASE_URL to LLM gateway URL") }) From 3f055126277a5b84224bd03623920c0e992abcd6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Apr 2026 06:37:07 +0000 Subject: [PATCH 4/4] chore: fix gemini engine comment punctuation Agent-Logs-Url: https://github.com/github/gh-aw/sessions/8de9e6c1-7d1f-48b0-8f7f-df4574244374 Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/workflow/gemini_engine.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/workflow/gemini_engine.go b/pkg/workflow/gemini_engine.go index 05d9d5b676b..cfcad2a1497 100644 --- a/pkg/workflow/gemini_engine.go +++ b/pkg/workflow/gemini_engine.go @@ -38,7 +38,7 @@ func (e *GeminiEngine) GetModelEnvVarName() string { return constants.GeminiCLIModelEnvVar } -// GetRequiredSecretNames returns the list of secrets required by the Gemini engine +// GetRequiredSecretNames returns the list of secrets required by the Gemini engine. // This includes GEMINI_API_KEY and optionally MCP_GATEWAY_API_KEY, GITHUB_MCP_SERVER_TOKEN, // HTTP MCP header secrets, and mcp-scripts secrets func (e *GeminiEngine) GetRequiredSecretNames(workflowData *WorkflowData) []string {