Skip to content
Merged
Show file tree
Hide file tree
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
2 changes: 1 addition & 1 deletion pkg/cli/audit_report_render_guard.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func renderGuardPolicySummary(summary *GuardPolicySummary) {

// Most frequently blocked tools
if len(summary.BlockedToolCounts) > 0 {
toolNames := sliceutil.MapToSlice(summary.BlockedToolCounts)
toolNames := sliceutil.MapKeys(summary.BlockedToolCounts)
sort.Slice(toolNames, func(i, j int) bool {
return summary.BlockedToolCounts[toolNames[i]] > summary.BlockedToolCounts[toolNames[j]]
})
Expand Down
4 changes: 2 additions & 2 deletions pkg/cli/firewall_log.go
Original file line number Diff line number Diff line change
Expand Up @@ -354,8 +354,8 @@ func parseFirewallLog(logPath string, verbose bool) (*FirewallAnalysis, error) {
}

// Convert sets to sorted slices
analysis.AllowedDomains = sliceutil.MapToSlice(allowedDomainsSet)
analysis.BlockedDomains = sliceutil.MapToSlice(blockedDomainsSet)
analysis.AllowedDomains = sliceutil.MapKeys(allowedDomainsSet)
analysis.BlockedDomains = sliceutil.MapKeys(blockedDomainsSet)

sort.Strings(analysis.AllowedDomains)
sort.Strings(analysis.BlockedDomains)
Expand Down
4 changes: 2 additions & 2 deletions pkg/cli/gateway_logs_render.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ func renderGatewayMetricsTable(metrics *GatewayMetrics, verbose bool) string {
}

// Sort tools by call count
toolNames := sliceutil.MapToSlice(server.Tools)
toolNames := sliceutil.MapKeys(server.Tools)
sort.Slice(toolNames, func(i, j int) bool {
return server.Tools[toolNames[i]].CallCount > server.Tools[toolNames[j]].CallCount
})
Expand Down Expand Up @@ -186,7 +186,7 @@ func renderGatewayMetricsTable(metrics *GatewayMetrics, verbose bool) string {

// getSortedServerNames returns server names sorted by request count
func getSortedServerNames(metrics *GatewayMetrics) []string {
names := sliceutil.MapToSlice(metrics.Servers)
names := sliceutil.MapKeys(metrics.Servers)
sort.Slice(names, func(i, j int) bool {
return metrics.Servers[names[i]].RequestCount > metrics.Servers[names[j]].RequestCount
})
Expand Down
2 changes: 1 addition & 1 deletion pkg/cli/run_push.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ func collectWorkflowFiles(ctx context.Context, workflowPath string, verbose bool
runPushLog.Printf("Import collection completed")

// Convert map to slice
result := sliceutil.MapToSlice(files)
result := sliceutil.MapKeys(files)

// Sort files for stable output
sort.Strings(result)
Expand Down
2 changes: 1 addition & 1 deletion pkg/sliceutil/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ All functions in this package are pure: they never modify their input. They are
|----------|-----------|-------------|
| `Filter` | `func[T any](slice []T, predicate func(T) bool) []T` | Returns a new slice containing only elements for which `predicate` returns `true` |
| `Map` | `func[T, U any](slice []T, transform func(T) U) []U` | Applies `transform` to every element and returns the results as a new slice |
| `MapToSlice` | `func[K comparable, V any](m map[K]V) []K` | Converts the keys of a map into a slice; order is not guaranteed |
| `MapKeys` | `func[K comparable, V any](m map[K]V) []K` | Converts the keys of a map into a slice; order is not guaranteed |
| `FilterMapKeys` | `func[K comparable, V any](m map[K]V, predicate func(K, V) bool) []K` | Returns map keys for which `predicate(key, value)` is `true`; order is not guaranteed |
| `Any` | `func[T any](slice []T, predicate func(T) bool) bool` | Returns `true` if at least one element satisfies `predicate`; returns `false` for nil or empty slices |
| `Deduplicate` | `func[T comparable](slice []T) []T` | Returns a new slice with duplicate elements removed, preserving order of first occurrence |
Expand Down
4 changes: 2 additions & 2 deletions pkg/sliceutil/sliceutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ func Map[T, U any](slice []T, transform func(T) U) []U {
return result
}

// MapToSlice converts a map's keys to a slice.
// MapKeys converts a map's keys to a slice.
// The order of elements is not guaranteed as map iteration order is undefined.
// This is a pure function that does not modify the input map.
func MapToSlice[K comparable, V any](m map[K]V) []K {
func MapKeys[K comparable, V any](m map[K]V) []K {
result := make([]K, 0, len(m))
Comment on lines +30 to 34
for key := range m {
result = append(result, key)
Expand Down
18 changes: 9 additions & 9 deletions pkg/sliceutil/sliceutil_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,29 +148,29 @@ func TestDeduplicate(t *testing.T) {
}
}

func TestMapToSlice(t *testing.T) {
func TestMapKeys(t *testing.T) {
t.Run("nil map returns empty slice", func(t *testing.T) {
result := MapToSlice[string, int](nil)
assert.Empty(t, result, "MapToSlice should return empty slice for nil map")
result := MapKeys[string, int](nil)
assert.Empty(t, result, "MapKeys should return empty slice for nil map")
})

t.Run("empty map returns empty slice", func(t *testing.T) {
result := MapToSlice(map[string]int{})
assert.Empty(t, result, "MapToSlice should return empty slice for empty map")
result := MapKeys(map[string]int{})
assert.Empty(t, result, "MapKeys should return empty slice for empty map")
})

t.Run("returns all keys in any order", func(t *testing.T) {
m := map[string]int{"apple": 1, "banana": 2, "cherry": 3}
result := MapToSlice(m)
result := MapKeys(m)
assert.ElementsMatch(t, []string{"apple", "banana", "cherry"}, result,
"MapToSlice should return all keys from map")
"MapKeys should return all keys from map")
})

t.Run("single entry map", func(t *testing.T) {
m := map[string]bool{"only": true}
result := MapToSlice(m)
result := MapKeys(m)
assert.Equal(t, []string{"only"}, result,
"MapToSlice should return the single key from a one-entry map")
"MapKeys should return the single key from a one-entry map")
})
}

Expand Down
12 changes: 6 additions & 6 deletions pkg/sliceutil/spec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,19 +45,19 @@ func TestSpec_PublicAPI_Map(t *testing.T) {
})
}

// TestSpec_PublicAPI_MapToSlice validates the documented behavior of MapToSlice
// TestSpec_PublicAPI_MapKeys validates the documented behavior of MapKeys
// as described in the sliceutil README.md specification.
func TestSpec_PublicAPI_MapToSlice(t *testing.T) {
func TestSpec_PublicAPI_MapKeys(t *testing.T) {
t.Run("returns all map keys as a slice", func(t *testing.T) {
m := map[string]int{"a": 1, "b": 2}
keys := MapToSlice(m)
assert.ElementsMatch(t, []string{"a", "b"}, keys, "MapToSlice should return all map keys (order not guaranteed)")
keys := MapKeys(m)
assert.ElementsMatch(t, []string{"a", "b"}, keys, "MapKeys should return all map keys (order not guaranteed)")
})

t.Run("returns empty slice for empty map", func(t *testing.T) {
m := map[string]int{}
keys := MapToSlice(m)
assert.Empty(t, keys, "MapToSlice of empty map should return empty slice")
keys := MapKeys(m)
assert.Empty(t, keys, "MapKeys of empty map should return empty slice")
})
}

Expand Down
6 changes: 3 additions & 3 deletions pkg/workflow/mcp_config_custom.go
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ func renderSharedMCPConfig(yaml *strings.Builder, toolName string, toolConfig ma
if renderer.Format == "toml" {
fmt.Fprintf(yaml, "%senv = { ", renderer.IndentLevel)
// Using functional helper to extract map keys
envKeys := sliceutil.MapToSlice(mcpConfig.Env)
envKeys := sliceutil.MapKeys(mcpConfig.Env)
sort.Strings(envKeys)
for i, envKey := range envKeys {
if i > 0 {
Expand Down Expand Up @@ -454,7 +454,7 @@ func renderSharedMCPConfig(yaml *strings.Builder, toolName string, toolConfig ma
if len(mcpConfig.Headers) > 0 {
fmt.Fprintf(yaml, "%shttp_headers = { ", renderer.IndentLevel)
// Using functional helper to extract map keys
headerKeys := sliceutil.MapToSlice(mcpConfig.Headers)
headerKeys := sliceutil.MapKeys(mcpConfig.Headers)
sort.Strings(headerKeys)
for i, headerKey := range headerKeys {
if i > 0 {
Expand All @@ -471,7 +471,7 @@ func renderSharedMCPConfig(yaml *strings.Builder, toolName string, toolConfig ma
}
fmt.Fprintf(yaml, "%s\"headers\": {\n", renderer.IndentLevel)
// Using functional helper to extract map keys
headerKeys := sliceutil.MapToSlice(mcpConfig.Headers)
headerKeys := sliceutil.MapKeys(mcpConfig.Headers)
sort.Strings(headerKeys)
for headerIndex, headerKey := range headerKeys {
headerComma := ","
Expand Down
10 changes: 5 additions & 5 deletions pkg/workflow/mcp_setup_generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,7 @@ func generateMCPScriptsSetup(yaml *strings.Builder, workflowData *WorkflowData)

yaml.WriteString(" - name: Write MCP Scripts Tool Files\n")
yaml.WriteString(" run: |\n")
mcpScriptToolNames := sliceutil.MapToSlice(workflowData.MCPScripts.Tools)
mcpScriptToolNames := sliceutil.MapKeys(workflowData.MCPScripts.Tools)
sort.Strings(mcpScriptToolNames)
for _, toolName := range mcpScriptToolNames {
toolConfig := workflowData.MCPScripts.Tools[toolName]
Expand Down Expand Up @@ -436,7 +436,7 @@ func generateMCPScriptsSetup(yaml *strings.Builder, workflowData *WorkflowData)
yaml.WriteString(" GH_AW_MCP_SCRIPTS_API_KEY: ${{ steps.mcp-scripts-config.outputs.mcp_scripts_api_key }}\n")
mcpScriptsSecrets := collectMCPScriptsSecrets(workflowData.MCPScripts)
if len(mcpScriptsSecrets) > 0 {
envVarNames := sliceutil.MapToSlice(mcpScriptsSecrets)
envVarNames := sliceutil.MapKeys(mcpScriptsSecrets)
sort.Strings(envVarNames)
for _, envVarName := range envVarNames {
secretExpr := mcpScriptsSecrets[envVarName]
Expand Down Expand Up @@ -551,7 +551,7 @@ func writeMCPGatewayStepEnv(yaml *strings.Builder, mcpEnvVars map[string]string)
return
}
yaml.WriteString(" env:\n")
envVarNames := sliceutil.MapToSlice(mcpEnvVars)
envVarNames := sliceutil.MapKeys(mcpEnvVars)
sort.Strings(envVarNames)
for _, envVarName := range envVarNames {
fmt.Fprintf(yaml, " %s: %s\n", envVarName, mcpEnvVars[envVarName])
Expand Down Expand Up @@ -624,7 +624,7 @@ func writeMCPGatewayExports(yaml *strings.Builder, tools map[string]any, engine
yaml.WriteString(" export GITHUB_PERSONAL_ACCESS_TOKEN=\"$GITHUB_MCP_SERVER_TOKEN\"\n")
}
if len(gatewayConfig.Env) > 0 {
envVarNames := sliceutil.MapToSlice(gatewayConfig.Env)
envVarNames := sliceutil.MapKeys(gatewayConfig.Env)
sort.Strings(envVarNames)
for _, envVarName := range envVarNames {
fmt.Fprintf(yaml, " export %s=%s\n", envVarName, gatewayConfig.Env[envVarName])
Expand Down Expand Up @@ -745,7 +745,7 @@ func appendMCPGatewayConditionalEnvFlags(containerCmd *strings.Builder, workflow

func appendMCPGatewayCustomAndHTTPEnvFlags(containerCmd *strings.Builder, workflowData *WorkflowData, gatewayConfig *MCPGatewayRuntimeConfig, mcpEnvVars map[string]string, hasGitHub bool, githubTool any, tools map[string]any, engine CodingAgentEngine) {
if len(gatewayConfig.Env) > 0 {
envVarNames := sliceutil.MapToSlice(gatewayConfig.Env)
envVarNames := sliceutil.MapKeys(gatewayConfig.Env)
sort.Strings(envVarNames)
for _, envVarName := range envVarNames {
containerCmd.WriteString(" -e " + envVarName)
Expand Down
2 changes: 1 addition & 1 deletion pkg/workflow/yaml_env_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func writeHeadersToYAML(yaml *strings.Builder, headers map[string]string, indent
envLog.Printf("Writing %d headers to YAML", len(headers))

// Sort keys for deterministic output - using functional helper
keys := sliceutil.MapToSlice(headers)
keys := sliceutil.MapKeys(headers)
sort.Strings(keys)

// Write each header with proper comma placement
Expand Down