github.com/remind101/go-getter@v0.0.0-20180809191950-4bda8fa99001/get_hg.go (about) 1 package getter 2 3 import ( 4 "fmt" 5 "net/url" 6 "os" 7 "os/exec" 8 "path/filepath" 9 "runtime" 10 11 urlhelper "github.com/hashicorp/go-getter/helper/url" 12 "github.com/hashicorp/go-safetemp" 13 ) 14 15 // HgGetter is a Getter implementation that will download a module from 16 // a Mercurial repository. 17 type HgGetter struct{} 18 19 func (g *HgGetter) ClientMode(_ *url.URL) (ClientMode, error) { 20 return ClientModeDir, nil 21 } 22 23 func (g *HgGetter) Get(dst string, u *url.URL) error { 24 if _, err := exec.LookPath("hg"); err != nil { 25 return fmt.Errorf("hg must be available and on the PATH") 26 } 27 28 newURL, err := urlhelper.Parse(u.String()) 29 if err != nil { 30 return err 31 } 32 if fixWindowsDrivePath(newURL) { 33 // See valid file path form on http://www.selenic.com/hg/help/urls 34 newURL.Path = fmt.Sprintf("/%s", newURL.Path) 35 } 36 37 // Extract some query parameters we use 38 var rev string 39 q := newURL.Query() 40 if len(q) > 0 { 41 rev = q.Get("rev") 42 q.Del("rev") 43 44 newURL.RawQuery = q.Encode() 45 } 46 47 _, err = os.Stat(dst) 48 if err != nil && !os.IsNotExist(err) { 49 return err 50 } 51 if err != nil { 52 if err := g.clone(dst, newURL); err != nil { 53 return err 54 } 55 } 56 57 if err := g.pull(dst, newURL); err != nil { 58 return err 59 } 60 61 return g.update(dst, newURL, rev) 62 } 63 64 // GetFile for Hg doesn't support updating at this time. It will download 65 // the file every time. 66 func (g *HgGetter) GetFile(dst string, u *url.URL) error { 67 // Create a temporary directory to store the full source. This has to be 68 // a non-existent directory. 69 td, tdcloser, err := safetemp.Dir("", "getter") 70 if err != nil { 71 return err 72 } 73 defer tdcloser.Close() 74 75 // Get the filename, and strip the filename from the URL so we can 76 // just get the repository directly. 77 filename := filepath.Base(u.Path) 78 u.Path = filepath.ToSlash(filepath.Dir(u.Path)) 79 80 // If we're on Windows, we need to set the host to "localhost" for hg 81 if runtime.GOOS == "windows" { 82 u.Host = "localhost" 83 } 84 85 // Get the full repository 86 if err := g.Get(td, u); err != nil { 87 return err 88 } 89 90 // Copy the single file 91 u, err = urlhelper.Parse(fmtFileURL(filepath.Join(td, filename))) 92 if err != nil { 93 return err 94 } 95 96 fg := &FileGetter{Copy: true} 97 return fg.GetFile(dst, u) 98 } 99 100 func (g *HgGetter) clone(dst string, u *url.URL) error { 101 cmd := exec.Command("hg", "clone", "-U", u.String(), dst) 102 return getRunCommand(cmd) 103 } 104 105 func (g *HgGetter) pull(dst string, u *url.URL) error { 106 cmd := exec.Command("hg", "pull") 107 cmd.Dir = dst 108 return getRunCommand(cmd) 109 } 110 111 func (g *HgGetter) update(dst string, u *url.URL, rev string) error { 112 args := []string{"update"} 113 if rev != "" { 114 args = append(args, rev) 115 } 116 117 cmd := exec.Command("hg", args...) 118 cmd.Dir = dst 119 return getRunCommand(cmd) 120 } 121 122 func fixWindowsDrivePath(u *url.URL) bool { 123 // hg assumes a file:/// prefix for Windows drive letter file paths. 124 // (e.g. file:///c:/foo/bar) 125 // If the URL Path does not begin with a '/' character, the resulting URL 126 // path will have a file:// prefix. (e.g. file://c:/foo/bar) 127 // See http://www.selenic.com/hg/help/urls and the examples listed in 128 // http://selenic.com/repo/hg-stable/file/1265a3a71d75/mercurial/util.py#l1936 129 return runtime.GOOS == "windows" && u.Scheme == "file" && 130 len(u.Path) > 1 && u.Path[0] != '/' && u.Path[1] == ':' 131 }