Skip to content

Commit 2e9c90e

Browse files
Copilotpelikhan
andauthored
Fix bash tool parser to merge default commands with custom arrays (#1094)
* Initial plan * Initial analysis: Understand bash tool permissions issue Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> * Add make:* commands to DefaultBashTools Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> * Final validation: Code review passed, all tests successful Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> * Fix parser to merge default bash tools with custom commands Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> * Apply code formatting (gofmt) Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
1 parent dacb052 commit 2e9c90e

5 files changed

Lines changed: 146 additions & 5 deletions

File tree

.github/workflows/tidy.lock.yml

Lines changed: 12 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/workflow/bash_defaults_consistency_test.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,15 @@ func TestBashDefaultsConsistency(t *testing.T) {
6565
},
6666
safeOutputs: nil,
6767
},
68+
{
69+
name: "bash with make array and create-pull-request (tidy.md config)",
70+
tools: map[string]any{
71+
"bash": []any{"make:*"},
72+
},
73+
safeOutputs: &SafeOutputsConfig{
74+
CreatePullRequests: &CreatePullRequestsConfig{},
75+
},
76+
},
6877
}
6978

7079
for _, tt := range tests {

pkg/workflow/bash_merge_test.go

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package workflow
2+
3+
import (
4+
"testing"
5+
)
6+
7+
// TestBashToolsMergeCustomWithDefaults tests that custom bash tools get merged with defaults
8+
func TestBashToolsMergeCustomWithDefaults(t *testing.T) {
9+
compiler := NewCompiler(false, "", "test")
10+
11+
tests := []struct {
12+
name string
13+
tools map[string]any
14+
safeOutputs *SafeOutputsConfig
15+
expected []string
16+
}{
17+
{
18+
name: "bash with make commands should include defaults + make",
19+
tools: map[string]any{
20+
"bash": []any{"make:*"},
21+
},
22+
safeOutputs: nil,
23+
expected: []string{"echo", "ls", "pwd", "cat", "head", "tail", "grep", "wc", "sort", "uniq", "date", "make:*"},
24+
},
25+
{
26+
name: "bash with multiple commands should include defaults + custom",
27+
tools: map[string]any{
28+
"bash": []any{"make:*", "npm:*"},
29+
},
30+
safeOutputs: nil,
31+
expected: []string{"echo", "ls", "pwd", "cat", "head", "tail", "grep", "wc", "sort", "uniq", "date", "make:*", "npm:*"},
32+
},
33+
{
34+
name: "bash with empty array should remain empty",
35+
tools: map[string]any{
36+
"bash": []any{},
37+
},
38+
safeOutputs: nil,
39+
expected: []string{},
40+
},
41+
{
42+
name: "bash with make commands and safe outputs should include defaults + make + git",
43+
tools: map[string]any{
44+
"bash": []any{"make:*"},
45+
},
46+
safeOutputs: &SafeOutputsConfig{
47+
CreatePullRequests: &CreatePullRequestsConfig{},
48+
},
49+
expected: []string{"echo", "ls", "pwd", "cat", "head", "tail", "grep", "wc", "sort", "uniq", "date", "make:*", "git checkout:*", "git branch:*", "git switch:*", "git add:*", "git rm:*", "git commit:*", "git merge:*"},
50+
},
51+
}
52+
53+
for _, tt := range tests {
54+
t.Run(tt.name, func(t *testing.T) {
55+
// Apply default tools
56+
result := compiler.applyDefaultTools(tt.tools, tt.safeOutputs)
57+
58+
// Check the bash tools
59+
bashTools, exists := result["bash"]
60+
if !exists {
61+
t.Fatalf("Expected bash tools to exist")
62+
}
63+
64+
bashArray, ok := bashTools.([]any)
65+
if !ok {
66+
t.Fatalf("Expected bash tools to be an array, got %T", bashTools)
67+
}
68+
69+
// Convert to string slice
70+
actual := make([]string, len(bashArray))
71+
for i, tool := range bashArray {
72+
actual[i] = tool.(string)
73+
}
74+
75+
// Debug: print actual tools
76+
t.Logf("Actual tools: %v", actual)
77+
t.Logf("Expected tools: %v", tt.expected)
78+
79+
// Check length
80+
if len(actual) != len(tt.expected) {
81+
t.Errorf("Expected %d tools, got %d. Expected: %v, Actual: %v", len(tt.expected), len(actual), tt.expected, actual)
82+
return
83+
}
84+
85+
// Check all expected tools are present
86+
actualMap := make(map[string]bool)
87+
for _, tool := range actual {
88+
actualMap[tool] = true
89+
}
90+
91+
for _, expected := range tt.expected {
92+
if !actualMap[expected] {
93+
t.Errorf("Expected tool '%s' not found in actual tools: %v", expected, actual)
94+
}
95+
}
96+
})
97+
}
98+
}

pkg/workflow/compiler.go

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1069,7 +1069,7 @@ func (c *Compiler) applyDefaultTools(tools map[string]any, safeOutputs *SafeOutp
10691069
// Behavior:
10701070
// - bash: true or bash: nil → Add default commands
10711071
// - bash: [] → No commands (empty array means no tools allowed)
1072-
// - bash: ["cmd1", "cmd2"] → Keep specific commands as-is
1072+
// - bash: ["cmd1", "cmd2"] → Add default commands + specific commands
10731073
if bashTool, exists := tools["bash"]; exists {
10741074
// Check if bash was left as nil or true after git processing
10751075
if bashTool == nil {
@@ -1089,8 +1089,31 @@ func (c *Compiler) applyDefaultTools(tools map[string]any, safeOutputs *SafeOutp
10891089
defaultCommands[i] = cmd
10901090
}
10911091
tools["bash"] = defaultCommands
1092+
} else if bashArray, ok := bashTool.([]any); ok {
1093+
// bash is an array - merge default commands with custom commands
1094+
if len(bashArray) > 0 {
1095+
// Create a set to track existing commands to avoid duplicates
1096+
existingCommands := make(map[string]bool)
1097+
for _, cmd := range bashArray {
1098+
if cmdStr, ok := cmd.(string); ok {
1099+
existingCommands[cmdStr] = true
1100+
}
1101+
}
1102+
1103+
// Start with default commands
1104+
mergedCommands := make([]any, 0, len(constants.DefaultBashTools)+len(bashArray))
1105+
for _, cmd := range constants.DefaultBashTools {
1106+
if !existingCommands[cmd] {
1107+
mergedCommands = append(mergedCommands, cmd)
1108+
}
1109+
}
1110+
1111+
// Add the custom commands
1112+
mergedCommands = append(mergedCommands, bashArray...)
1113+
tools["bash"] = mergedCommands
1114+
}
1115+
// Note: bash with empty array (bash: []) means "no bash tools allowed" and is left as-is
10921116
}
1093-
// Note: bash with empty array (bash: []) means "no bash tools allowed" and is left as-is
10941117
}
10951118

10961119
return tools

pkg/workflow/js/validate_errors.test.cjs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,10 +151,10 @@ Some normal log content
151151

152152
// Should handle invalid patterns gracefully, not throw
153153
const hasErrors = validateErrors(logContent, patterns);
154-
154+
155155
// Should return false since no valid patterns matched
156156
expect(hasErrors).toBe(false);
157-
157+
158158
// Should log an error about the invalid pattern
159159
expect(global.core.error).toHaveBeenCalledWith("invalid error regex pattern: [invalid regex");
160160
});

0 commit comments

Comments
 (0)