@@ -107,34 +107,71 @@ type DockerMount struct {
107107 Destination string `json:"Destination"`
108108}
109109
110- // GetSourceMountFolderFromDevContainer inspects the specified container and returns the DockerMount for the source mount
111- func GetSourceMountFolderFromDevContainer (containerIDOrName string ) (DockerMount , error ) {
110+ // SourceInfo holds properties about the source mounted in a dev container
111+ type SourceInfo struct {
112+ DevcontainerFolder string
113+ DockerMount DockerMount
114+ }
115+
116+ // getMountFolderFromFolder walks up the path hierarchy checking for a git repo. If found that is returned, if not the original folder is used
117+ func getMountFolderFromFolder (folder string ) (string , error ) {
118+
119+ for currentFolder := folder ; currentFolder != "" ; currentFolder = path .Dir (currentFolder ) {
120+ info , err := os .Stat (path .Join (currentFolder , ".git" ))
121+ if os .IsNotExist (err ) {
122+ continue // walk up the hierarchy
123+ }
124+ if err != nil {
125+ // other error - e.g. permissions issue
126+ // give up the search
127+ break
128+ }
129+ if info .IsDir () {
130+ // We've found a .git folder - use it
131+ return currentFolder , nil
132+ }
133+
134+ }
135+ // didn't find anything - use original folder
136+ return folder , nil
137+ }
138+
139+ // GetSourceInfoFromDevContainer inspects the specified container and returns the SourceInfo
140+ func GetSourceInfoFromDevContainer (containerIDOrName string ) (SourceInfo , error ) {
112141 localPath , err := GetLocalFolderFromDevContainer (containerIDOrName )
113142 if err != nil {
114- return DockerMount {}, err
143+ return SourceInfo {}, err
115144 }
116145
117146 if strings .HasPrefix (localPath , "\\ \\ wsl$" ) && wsl .IsWsl () {
118147 localPath , err = wsl .ConvertWindowsPathToWslPath (localPath )
119148 if err != nil {
120- return DockerMount {}, fmt .Errorf ("error converting path: %s" , err )
149+ return SourceInfo {}, fmt .Errorf ("error converting path: %s" , err )
121150 }
122151 }
123152
124- cmd := exec .Command ("docker" , "inspect" , containerIDOrName , "--format" , fmt .Sprintf ("{{ range .Mounts }}{{if eq .Source \" %s\" }}{{json .}}{{end}}{{end}}" , localPath ))
153+ mountFolder , err := getMountFolderFromFolder (localPath )
154+ if err != nil {
155+ return SourceInfo {}, fmt .Errorf ("search for mount folder failed: %s" , err )
156+ }
157+
158+ cmd := exec .Command ("docker" , "inspect" , containerIDOrName , "--format" , fmt .Sprintf ("{{ range .Mounts }}{{if eq .Source \" %s\" }}{{json .}}{{end}}{{end}}" , mountFolder ))
125159
126160 output , err := cmd .Output ()
127161 if err != nil {
128- return DockerMount {}, fmt .Errorf ("Failed to read docker stdout: %v" , err )
162+ return SourceInfo {}, fmt .Errorf ("Failed to read docker stdout: %v" , err )
129163 }
130164
131165 var mount DockerMount
132166 err = json .Unmarshal (output , & mount )
133167 if err != nil {
134- return DockerMount {}, err
168+ return SourceInfo {}, fmt . Errorf ( "failed to get parse JSON getting mount folder for container %q (path=%q): %s" , containerIDOrName , mountFolder , err )
135169 }
136170
137- return mount , nil
171+ return SourceInfo {
172+ DevcontainerFolder : localPath ,
173+ DockerMount : mount ,
174+ }, nil
138175}
139176
140177type byLocalPathLength []DevcontainerInfo
@@ -178,11 +215,11 @@ func ExecInDevContainer(containerID string, workDir string, args []string) error
178215
179216 statusWriter := & terminal.UpdatingStatusWriter {}
180217
181- sourceMount , err := GetSourceMountFolderFromDevContainer (containerID )
218+ sourceInfo , err := GetSourceInfoFromDevContainer (containerID )
182219 if err != nil {
183- return err
220+ return fmt . Errorf ( "failed to get source mount: %s" , err )
184221 }
185- localPath := sourceMount . Source
222+ localPath := sourceInfo . DevcontainerFolder
186223
187224 statusWriter .Printf ("Getting user name" )
188225 devcontainerJSONPath := path .Join (localPath , ".devcontainer/devcontainer.json" )
@@ -230,9 +267,9 @@ func ExecInDevContainer(containerID string, workDir string, args []string) error
230267 fmt .Println ("Continuing without setting VSCODE_IPC_HOOK_CLI..." )
231268 }
232269
233- mountPath := sourceMount .Destination
270+ mountPath := sourceInfo . DockerMount .Destination
234271 if workDir == "" {
235- workDir = mountPath
272+ workDir = sourceInfo . DevcontainerFolder
236273 } else if ! filepath .IsAbs (workDir ) {
237274
238275 // Convert to absolute (local) path
@@ -251,8 +288,8 @@ func ExecInDevContainer(containerID string, workDir string, args []string) error
251288 }
252289 if ! containerPathExists {
253290 // path not found - try converting from local path
254- // ? Should we check here that the workDir has localPath as a prefix?
255- devContainerRelativePath , err := filepath .Rel (localPath , workDir )
291+ // ? Should we check here that the workDir has mountPath as a prefix?
292+ devContainerRelativePath , err := filepath .Rel (sourceInfo . DockerMount . Source , workDir )
256293 if err != nil {
257294 return fmt .Errorf ("error getting path relative to mount dir: %s" , err )
258295 }
0 commit comments