diff --git a/config/module/get_hg.go b/config/module/get_hg.go index a979eacfd..666762080 100644 --- a/config/module/get_hg.go +++ b/config/module/get_hg.go @@ -5,6 +5,7 @@ import ( "net/url" "os" "os/exec" + "runtime" ) // HgGetter is a Getter implementation that will download a module from @@ -16,34 +17,40 @@ func (g *HgGetter) Get(dst string, u *url.URL) error { return fmt.Errorf("hg must be available and on the PATH") } + newURL, err := urlParse(u.String()) + if err != nil { + return err + } + if fixWindowsDrivePath(newURL) { + // See valid file path form on http://www.selenic.com/hg/help/urls + newURL.Path = fmt.Sprintf("/%s", newURL.Path) + } + // Extract some query parameters we use var rev string - q := u.Query() + q := newURL.Query() if len(q) > 0 { rev = q.Get("rev") q.Del("rev") - // Copy the URL - var newU url.URL = *u - u = &newU - u.RawQuery = q.Encode() + newURL.RawQuery = q.Encode() } - _, err := os.Stat(dst) + _, err = os.Stat(dst) if err != nil && !os.IsNotExist(err) { return err } if err != nil { - if err := g.clone(dst, u); err != nil { + if err := g.clone(dst, newURL); err != nil { return err } } - if err := g.pull(dst, u); err != nil { + if err := g.pull(dst, newURL); err != nil { return err } - return g.update(dst, u, rev) + return g.update(dst, newURL, rev) } func (g *HgGetter) clone(dst string, u *url.URL) error { @@ -67,3 +74,14 @@ func (g *HgGetter) update(dst string, u *url.URL, rev string) error { cmd.Dir = dst return getRunCommand(cmd) } + +func fixWindowsDrivePath(u *url.URL) bool { + // hg assumes a file:/// prefix for Windows drive letter file paths. + // (e.g. file:///c:/foo/bar) + // If the URL Path does not begin with a '/' character, the resulting URL + // path will have a file:// prefix. (e.g. file://c:/foo/bar) + // See http://www.selenic.com/hg/help/urls and the examples listed in + // http://selenic.com/repo/hg-stable/file/1265a3a71d75/mercurial/util.py#l1936 + return runtime.GOOS == "windows" && u.Scheme == "file" && + len(u.Path) > 1 && u.Path[0] != '/' && u.Path[1] == ':' +} diff --git a/config/module/url_helper.go b/config/module/url_helper.go index b1a8756a2..792761927 100644 --- a/config/module/url_helper.go +++ b/config/module/url_helper.go @@ -5,6 +5,7 @@ import ( "net/url" "path/filepath" "runtime" + "strings" ) func urlParse(rawURL string) (*url.URL, error) { @@ -30,6 +31,13 @@ func urlParse(rawURL string) (*url.URL, error) { u.Scheme = "" } + if len(u.Host) > 1 && u.Host[1] == ':' && strings.HasPrefix(rawURL, "file://") { + // Assume we're dealing with a drive letter file path on Windows + // where the drive letter has been parsed into the URL Host. + u.Path = fmt.Sprintf("%s%s", u.Host, u.Path) + u.Host = "" + } + // Remove leading slash for absolute file paths on Windows. // For example, url.Parse yields u.Path = "/C:/Users/user" for // rawURL = "file:///C:/Users/user", which is an incorrect syntax.