Skip to content

Commit 174ceda

Browse files
committed
Handle finding devcontainer in parent folders
1 parent c122a22 commit 174ceda

File tree

3 files changed

+85
-23
lines changed

3 files changed

+85
-23
lines changed

cmd/devcontainer/devcontainer.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,10 +142,11 @@ func createExecCommand() *cobra.Command {
142142
// TODO - update to check for devcontainers in the path ancestry
143143
// Can't just check up the path for a .devcontainer folder as the container might
144144
// have been created via repository containers (https://github.com/microsoft/vscode-dev-containers/tree/main/repository-containers)
145-
containerID, err = devcontainers.GetContainerIDForPath(devcontainerPath)
145+
devcontainer, err := devcontainers.GetClosestPathMatchForPath(devcontainerList, devcontainerPath)
146146
if err != nil {
147147
return err
148148
}
149+
containerID = devcontainer.ContainerID
149150
if workDir == "" {
150151
if devcontainerPath == "" {
151152
workDir = "."

internal/pkg/devcontainers/dockerutils.go

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"os/exec"
1010
"path"
1111
"path/filepath"
12+
"sort"
1213
"strings"
1314

1415
"github.com/stuartleeks/devcontainer-cli/internal/pkg/terminal"
@@ -68,10 +69,17 @@ func ListDevcontainers() ([]DevcontainerInfo, error) {
6869
name = name[index+1:]
6970
}
7071
}
72+
localPath := parts[listPartLocalFolder]
73+
if strings.HasPrefix(localPath, "\\\\wsl$") && wsl.IsWsl() {
74+
localPath, err = wsl.ConvertWindowsPathToWslPath(localPath)
75+
if err != nil {
76+
return []DevcontainerInfo{}, fmt.Errorf("error converting path: %s", err)
77+
}
78+
}
7179
devcontainer := DevcontainerInfo{
7280
ContainerID: parts[listPartID],
7381
ContainerName: parts[listPartContainerName],
74-
LocalFolderPath: parts[listPartLocalFolder],
82+
LocalFolderPath: localPath,
7583
DevcontainerName: name,
7684
}
7785
devcontainers = append(devcontainers, devcontainer)
@@ -129,37 +137,41 @@ func GetSourceMountFolderFromDevContainer(containerIDOrName string) (DockerMount
129137
return mount, nil
130138
}
131139

132-
// GetContainerIDForPath returns the ID of the running container that matches the path
133-
func GetContainerIDForPath(devcontainerPath string) (string, error) {
140+
type byLocalPathLength []DevcontainerInfo
141+
142+
func (s byLocalPathLength) Len() int {
143+
return len(s)
144+
}
145+
func (s byLocalPathLength) Swap(i, j int) {
146+
s[i], s[j] = s[j], s[i]
147+
}
148+
func (s byLocalPathLength) Less(i, j int) bool {
149+
return len(s[i].LocalFolderPath) < len(s[j].LocalFolderPath)
150+
}
151+
152+
// GetClosestPathMatchForPath returns the dev container with the closes match to the specified path
153+
func GetClosestPathMatchForPath(devContainers []DevcontainerInfo, devcontainerPath string) (DevcontainerInfo, error) {
134154
if devcontainerPath == "" {
135155
devcontainerPath = "."
136156
}
137157
absPath, err := filepath.Abs(devcontainerPath)
138158
if err != nil {
139-
return "", fmt.Errorf("Error handling path %q: %s", devcontainerPath, err)
159+
return DevcontainerInfo{}, fmt.Errorf("Error handling path %q: %s", devcontainerPath, err)
140160
}
141161

142-
windowsPath := absPath
143-
if wsl.IsWsl() {
144-
var err error
145-
windowsPath, err = wsl.ConvertWslPathToWindowsPath(windowsPath)
146-
if err != nil {
147-
return "", err
162+
matchingPaths := byLocalPathLength{}
163+
for _, devcontainer := range devContainers {
164+
// Treat as match if the specified path is within the devcontainer path
165+
if strings.HasPrefix(absPath, devcontainer.LocalFolderPath) {
166+
matchingPaths = append(matchingPaths, devcontainer)
148167
}
149168
}
150-
151-
devcontainerList, err := ListDevcontainers()
152-
if err != nil {
153-
return "", fmt.Errorf("Error getting container list: %s", err)
154-
}
155-
156-
for _, devcontainer := range devcontainerList {
157-
if devcontainer.LocalFolderPath == windowsPath {
158-
containerID := devcontainer.ContainerID
159-
return containerID, nil
160-
}
169+
if len(matchingPaths) == 0 {
170+
return DevcontainerInfo{}, fmt.Errorf("Could not find running container for path %q", devcontainerPath)
161171
}
162-
return "", fmt.Errorf("Could not find running container for path %q", devcontainerPath)
172+
// return longest prefix match
173+
sort.Sort(matchingPaths)
174+
return matchingPaths[len(matchingPaths)-1], nil
163175
}
164176

165177
func ExecInDevContainer(containerID string, workDir string, args []string) error {
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package devcontainers
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
)
8+
9+
func TestGetClosestPathMatchForPath_ReturnsLongestMatch(t *testing.T) {
10+
11+
inputs := []DevcontainerInfo{
12+
{LocalFolderPath: "/path/to/project"},
13+
{LocalFolderPath: "/path/to/somewhere/else"},
14+
{LocalFolderPath: "/path"},
15+
}
16+
17+
actual, err := GetClosestPathMatchForPath(inputs, "/path/to/project")
18+
if assert.NoError(t, err) {
19+
assert.Equal(t, "/path/to/project", actual.LocalFolderPath)
20+
}
21+
}
22+
23+
func TestGetClosestPathMatchForPath_ReturnsLongestMatchWithTrailingSlash(t *testing.T) {
24+
25+
inputs := []DevcontainerInfo{
26+
{LocalFolderPath: "/path/to/project"},
27+
{LocalFolderPath: "/path/to/somewhere/else"},
28+
{LocalFolderPath: "/path"},
29+
}
30+
31+
actual, err := GetClosestPathMatchForPath(inputs, "/path/to/project/")
32+
if assert.NoError(t, err) {
33+
assert.Equal(t, "/path/to/project", actual.LocalFolderPath)
34+
}
35+
}
36+
37+
func TestGetClosestPathMatchForPath_ReturnsLongestMatchForChildFolder(t *testing.T) {
38+
39+
inputs := []DevcontainerInfo{
40+
{LocalFolderPath: "/path/to/project"},
41+
{LocalFolderPath: "/path/to/somewhere/else"},
42+
{LocalFolderPath: "/path"},
43+
}
44+
45+
actual, err := GetClosestPathMatchForPath(inputs, "/path/to/project/with/child")
46+
if assert.NoError(t, err) {
47+
assert.Equal(t, "/path/to/project", actual.LocalFolderPath)
48+
}
49+
}

0 commit comments

Comments
 (0)