Skip to content

Commit 9fd947e

Browse files
authored
Merge pull request #2389 from dgageot/board/fix-docker-agent-issue-2320-4561e8c6
Add support for shell expansions (~, env vars) in config paths
2 parents 3de0148 + 3850532 commit 9fd947e

File tree

3 files changed

+126
-21
lines changed

3 files changed

+126
-21
lines changed

pkg/path/expand.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package path
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
"strings"
7+
)
8+
9+
// ExpandPath expands shell-like patterns in a file path:
10+
// - ~ or ~/ at the start is replaced with the user's home directory
11+
// - Environment variables like ${HOME} or $HOME are expanded
12+
func ExpandPath(p string) string {
13+
if p == "" {
14+
return p
15+
}
16+
17+
// Expand environment variables
18+
p = os.ExpandEnv(p)
19+
20+
// Expand tilde to home directory
21+
if p == "~" || strings.HasPrefix(p, "~/") || strings.HasPrefix(p, "~"+string(filepath.Separator)) {
22+
if home, err := os.UserHomeDir(); err == nil {
23+
if p == "~" {
24+
return home
25+
}
26+
return filepath.Join(home, p[2:])
27+
}
28+
}
29+
30+
return p
31+
}

pkg/path/expand_test.go

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package path
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
"testing"
7+
)
8+
9+
func TestExpandPath(t *testing.T) {
10+
home, err := os.UserHomeDir()
11+
if err != nil {
12+
t.Fatal(err)
13+
}
14+
15+
tests := []struct {
16+
name string
17+
input string
18+
envSetup map[string]string
19+
expected string
20+
}{
21+
{
22+
name: "empty path",
23+
input: "",
24+
expected: "",
25+
},
26+
{
27+
name: "tilde only",
28+
input: "~",
29+
expected: home,
30+
},
31+
{
32+
name: "tilde with subpath",
33+
input: "~/data/memory.db",
34+
expected: filepath.Join(home, "data", "memory.db"),
35+
},
36+
{
37+
name: "env var",
38+
input: "${HOME}/.data/memory.db",
39+
expected: filepath.Join(home, ".data", "memory.db"),
40+
},
41+
{
42+
name: "custom env var",
43+
input: "${MY_TEST_DATA_DIR}/memory.db",
44+
envSetup: map[string]string{"MY_TEST_DATA_DIR": "/tmp/testdata"},
45+
expected: "/tmp/testdata/memory.db",
46+
},
47+
{
48+
name: "absolute path unchanged",
49+
input: "/absolute/path/memory.db",
50+
expected: "/absolute/path/memory.db",
51+
},
52+
{
53+
name: "relative path unchanged",
54+
input: "relative/path/memory.db",
55+
expected: "relative/path/memory.db",
56+
},
57+
{
58+
name: "tilde and env var combined",
59+
input: "~/${MY_TEST_SUBDIR}/memory.db",
60+
envSetup: map[string]string{"MY_TEST_SUBDIR": "data"},
61+
expected: filepath.Join(home, "data", "memory.db"),
62+
},
63+
}
64+
65+
for _, tt := range tests {
66+
t.Run(tt.name, func(t *testing.T) {
67+
for k, v := range tt.envSetup {
68+
t.Setenv(k, v)
69+
}
70+
result := ExpandPath(tt.input)
71+
if result != tt.expected {
72+
t.Errorf("ExpandPath(%q) = %q, want %q", tt.input, result, tt.expected)
73+
}
74+
})
75+
}
76+
}

pkg/teamloader/registry.go

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,23 @@ func NewDefaultToolsetRegistry() *ToolsetRegistry {
8585
return r
8686
}
8787

88+
// resolveToolsetPath expands shell patterns (~, env vars) in the given path,
89+
// then validates it relative to the working directory or parent directory.
90+
func resolveToolsetPath(toolsetPath, parentDir string, runConfig *config.RuntimeConfig) (string, error) {
91+
toolsetPath = path.ExpandPath(toolsetPath)
92+
93+
var basePath string
94+
if filepath.IsAbs(toolsetPath) {
95+
basePath = ""
96+
} else if wd := runConfig.WorkingDir; wd != "" {
97+
basePath = wd
98+
} else {
99+
basePath = parentDir
100+
}
101+
102+
return path.ValidatePathInDirectory(toolsetPath, basePath)
103+
}
104+
88105
func createTodoTool(_ context.Context, toolset latest.Toolset, _ string, _ *config.RuntimeConfig, _ string) (tools.ToolSet, error) {
89106
if toolset.Shared {
90107
return builtin.NewSharedTodoTool(), nil
@@ -98,16 +115,7 @@ func createTasksTool(_ context.Context, toolset latest.Toolset, parentDir string
98115
toolsetPath = "tasks.json"
99116
}
100117

101-
var basePath string
102-
if filepath.IsAbs(toolsetPath) {
103-
basePath = ""
104-
} else if wd := runConfig.WorkingDir; wd != "" {
105-
basePath = wd
106-
} else {
107-
basePath = parentDir
108-
}
109-
110-
validatedPath, err := path.ValidatePathInDirectory(toolsetPath, basePath)
118+
validatedPath, err := resolveToolsetPath(toolsetPath, parentDir, runConfig)
111119
if err != nil {
112120
return nil, fmt.Errorf("invalid tasks storage path: %w", err)
113121
}
@@ -122,18 +130,8 @@ func createMemoryTool(_ context.Context, toolset latest.Toolset, parentDir strin
122130
var validatedMemoryPath string
123131

124132
if toolset.Path != "" {
125-
// Explicit path provided - resolve relative to working dir or parent dir
126-
var basePath string
127-
if filepath.IsAbs(toolset.Path) {
128-
basePath = ""
129-
} else if wd := runConfig.WorkingDir; wd != "" {
130-
basePath = wd
131-
} else {
132-
basePath = parentDir
133-
}
134-
135133
var err error
136-
validatedMemoryPath, err = path.ValidatePathInDirectory(toolset.Path, basePath)
134+
validatedMemoryPath, err = resolveToolsetPath(toolset.Path, parentDir, runConfig)
137135
if err != nil {
138136
return nil, fmt.Errorf("invalid memory database path: %w", err)
139137
}

0 commit comments

Comments
 (0)