Skip to content

Commit 3fb615b

Browse files
committed
Merge pull request #20 from docker-exec/hotfix/sanitise-windows-paths
Not able to make it run on Windows #10
2 parents f20d64f + abac01c commit 3fb615b

File tree

3 files changed

+93
-20
lines changed

3 files changed

+93
-20
lines changed

dexec.go

Lines changed: 55 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import (
66
"os"
77
"path/filepath"
88
"regexp"
9+
"runtime"
10+
"strings"
911
)
1012

1113
// ExtractFileExtension extracts the extension from a filename. This is defined
@@ -90,12 +92,13 @@ var LookupImageByExtension = func() func(string) DexecImage {
9092
const dexecPath = "/tmp/dexec/build"
9193
const dexecImageTemplate = "%s:%s"
9294
const dexecVolumeTemplate = "%s/%s:%s/%s"
95+
const dexecSanitisedWindowsPathPattern = "/%s%s"
9396

9497
// ExtractBasenameAndPermission takes an include string and splits it into
9598
// its file or folder name and the permission string if present or the empty
9699
// string if not.
97100
func ExtractBasenameAndPermission(path string) (string, string) {
98-
pathPattern := regexp.MustCompile("([\\w.-]+)(:(rw|ro))")
101+
pathPattern := regexp.MustCompile("([\\w.:-]+)(:(rw|ro))")
99102
match := pathPattern.FindStringSubmatch(path)
100103

101104
basename := path
@@ -108,30 +111,64 @@ func ExtractBasenameAndPermission(path string) (string, string) {
108111
return basename, permission
109112
}
110113

111-
// RunDexecContainer runs an anonymous Docker container with a Docker Exec
112-
// image, mounting the specified sources and includes and passing the
113-
// list of sources and arguments to the entrypoint.
114-
func RunDexecContainer(dexecImage DexecImage, options map[OptionType][]string) {
115-
dockerImage := fmt.Sprintf(dexecImageTemplate, dexecImage.image, dexecImage.version)
116-
117-
path := "."
118-
if len(options[TargetDir]) > 0 {
119-
path = options[TargetDir][0]
120-
}
121-
absPath, _ := filepath.Abs(path)
114+
// BuildVolumeArgs takes a base path and returns an array of Docker volume
115+
// arguments. The array takes the form {"-v", "/foo:/bar:[rw|ro]", ...} for
116+
// each source or include.
117+
func BuildVolumeArgs(path string, targets []string) []string {
118+
var volumeArgs []string
122119

123-
var dockerArgs []string
124-
for _, source := range append(options[Source], options[Include]...) {
120+
for _, source := range targets {
125121
basename, _ := ExtractBasenameAndPermission(source)
126122

127-
dockerArgs = append(
128-
dockerArgs,
123+
volumeArgs = append(
124+
volumeArgs,
129125
[]string{
130126
"-v",
131-
fmt.Sprintf(dexecVolumeTemplate, absPath, basename, dexecPath, source),
127+
fmt.Sprintf(dexecVolumeTemplate, path, basename, dexecPath, source),
132128
}...,
133129
)
134130
}
131+
return volumeArgs
132+
}
133+
134+
// SanitisePath takes an absolute path as provided by filepath.Abs() and
135+
// makes it ready to be passed to Docker based on the current OS. So far
136+
// the only OS format that requires transforming is Windows which is provided
137+
// in the form 'C:\some\path' but Docker requires '/c/some/path'.
138+
func SanitisePath(path string, platform string) string {
139+
sanitised := path
140+
if platform == "windows" {
141+
windowsPathPattern := regexp.MustCompile("^([A-Za-z]):(.*)")
142+
match := windowsPathPattern.FindStringSubmatch(path)
143+
144+
driveLetter := strings.ToLower(match[1])
145+
pathRemainder := strings.Replace(match[2], "\\", "/", -1)
146+
147+
sanitised = fmt.Sprintf(dexecSanitisedWindowsPathPattern, driveLetter, pathRemainder)
148+
}
149+
return sanitised
150+
}
151+
152+
// RetrievePath takes an array whose first element may contain an overridden
153+
// path and converts either this, or the default of "." to an absolute path
154+
// using Go's file utilities. This is then passed to SanitisedPath with the
155+
// current OS to get it into a Docker ready format.
156+
func RetrievePath(targetDirs []string) string {
157+
path := "."
158+
if len(targetDirs) > 0 {
159+
path = targetDirs[0]
160+
}
161+
absPath, _ := filepath.Abs(path)
162+
return SanitisePath(absPath, runtime.GOOS)
163+
}
164+
165+
// RunDexecContainer runs an anonymous Docker container with a Docker Exec
166+
// image, mounting the specified sources and includes and passing the
167+
// list of sources and arguments to the entrypoint.
168+
func RunDexecContainer(dexecImage DexecImage, options map[OptionType][]string) {
169+
dockerImage := fmt.Sprintf(dexecImageTemplate, dexecImage.image, dexecImage.version)
170+
171+
volumeArgs := BuildVolumeArgs(RetrievePath(options[TargetDir]), append(options[Source], options[Include]...))
135172

136173
var sourceBasenames []string
137174
for _, source := range options[Source] {
@@ -151,7 +188,7 @@ func RunDexecContainer(dexecImage DexecImage, options map[OptionType][]string) {
151188

152189
RunAnonymousContainer(
153190
dockerImage,
154-
dockerArgs,
191+
volumeArgs,
155192
entrypointArgs,
156193
)
157194
}

dexec_test.go

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,43 @@
11
package main
22

3-
import "testing"
3+
import (
4+
"reflect"
5+
"testing"
6+
)
7+
8+
func TestBuildVolumeArgs(t *testing.T) {
9+
cases := []struct {
10+
path string
11+
targets []string
12+
wantVolumes []string
13+
}{
14+
{"/foo", []string{"bar"}, []string{"-v", "/foo/bar:/tmp/dexec/build/bar"}},
15+
}
16+
for _, c := range cases {
17+
gotVolumes := BuildVolumeArgs(c.path, c.targets)
18+
if !reflect.DeepEqual(gotVolumes, c.wantVolumes) {
19+
t.Errorf("BuildVolumeArgs(%q, %q) %q != %q", c.path, c.targets, gotVolumes, c.wantVolumes)
20+
}
21+
}
22+
}
23+
24+
func TestSanitisePath(t *testing.T) {
25+
cases := []struct {
26+
path string
27+
platform string
28+
want string
29+
}{
30+
{"/Users/foo/bar", "darin", "/Users/foo/bar"},
31+
{"/home/foo/bar", "linux", "/home/foo/bar"},
32+
{"C:\\Users\\foo\\bar", "windows", "/c/Users/foo/bar"},
33+
}
34+
for _, c := range cases {
35+
gotSanitisedPath := SanitisePath(c.path, c.platform)
36+
if gotSanitisedPath != c.want {
37+
t.Errorf("SanitisedPath %q != %q", gotSanitisedPath, c.want)
38+
}
39+
}
40+
}
441

542
func TestExtractFileExtension(t *testing.T) {
643
cases := []struct {

docker.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,6 @@ func IsDockerRunning() (running bool) {
113113
func RunAnonymousContainer(image string, extraDockerArgs []string, entrypointArgs []string) {
114114
baseDockerArgs := []string{"run", "--rm"}
115115
imageDockerArgs := []string{"-t", image}
116-
117116
out := exec.Command(
118117
"docker",
119118
JoinStringSlices(

0 commit comments

Comments
 (0)