Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 10 additions & 12 deletions pkg/workflow/gemini_tools.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import (
"encoding/json"
"fmt"
"sort"
"strings"

"github.com/github/gh-aw/pkg/logger"
)
Expand Down Expand Up @@ -140,34 +139,33 @@ func (e *GeminiEngine) generateGeminiSettingsStep(workflowData *WorkflowData) Gi
configJSON = []byte(`{"context":{"includeDirectories":["/tmp/"]},"tools":{"core":[]}}`)
}

// Escape any single quotes in the JSON. The JSON we generate only contains tool
// names like "run_shell_command(grep)" and paths like "/tmp/", so in practice no
// single quotes will appear. The shell escape pattern '"'"' works by: ending the
// single-quoted string ('), adding a double-quoted single quote ("'"), and reopening
// the single-quoted string (').
jsonStr := strings.ReplaceAll(string(configJSON), "'", `'"'"'`)

// Generate a shell script that:
// - Creates the .gemini directory if needed
// - Merges settings into an existing settings.json (from MCP gateway setup), or
// - Creates a new settings.json when no MCP servers are configured
//
// The JSON config is passed via the GH_AW_GEMINI_BASE_CONFIG environment variable
// to avoid any shell quoting issues with special characters in the JSON.
//
// jq merge: '$existing * $base' means the RIGHT operand ($base) overrides the LEFT
// operand ($existing) for conflicting keys. Non-conflicting keys from $existing
// (e.g. mcpServers written by convert_gateway_config_gemini.sh) are preserved.
command := fmt.Sprintf(`mkdir -p "$GITHUB_WORKSPACE/.gemini"
command := `mkdir -p "$GITHUB_WORKSPACE/.gemini"
SETTINGS="$GITHUB_WORKSPACE/.gemini/settings.json"
BASE_CONFIG='%s'
BASE_CONFIG="$GH_AW_GEMINI_BASE_CONFIG"
if [ -f "$SETTINGS" ]; then
MERGED=$(jq -n --argjson base "$BASE_CONFIG" --argjson existing "$(cat "$SETTINGS")" '$existing * $base')
echo "$MERGED" > "$SETTINGS"
else
echo "$BASE_CONFIG" > "$SETTINGS"
fi`, jsonStr)
fi`

stepLines := []string{
" - name: Write Gemini settings",
}
stepLines = FormatStepWithCommandAndEnv(stepLines, command, nil)
env := map[string]string{
"GH_AW_GEMINI_BASE_CONFIG": string(configJSON),
}
stepLines = FormatStepWithCommandAndEnv(stepLines, command, env)
Comment on lines +166 to +169
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GH_AW_GEMINI_BASE_CONFIG is being set to raw JSON, but FormatStepWithCommandAndEnv currently renders env entries as KEY: <value> without YAML quoting/escaping. Because the value contains {}, :, and quotes, the generated workflow YAML will be parsed as a YAML object (or otherwise fail schema validation) instead of a string, so the env var may not be set and this step can break. Please ensure the env value is emitted as a YAML string scalar (e.g., by quoting/escaping in the step formatter, or by encoding the JSON for transport and decoding it in the script).

Copilot uses AI. Check for mistakes.
return GitHubActionStep(stepLines)
}
Loading