| OLD | NEW |
| (Empty) |
| 1 // Copyright 2016 The LUCI Authors. All rights reserved. | |
| 2 // Use of this source code is governed under the Apache License, Version 2.0 | |
| 3 // that can be found in the LICENSE file. | |
| 4 | |
| 5 package main | |
| 6 | |
| 7 import ( | |
| 8 "os" | |
| 9 "os/exec" | |
| 10 "path/filepath" | |
| 11 "strconv" | |
| 12 "strings" | |
| 13 "sync" | |
| 14 | |
| 15 "github.com/luci/luci-go/common/errors" | |
| 16 | |
| 17 "golang.org/x/net/context" | |
| 18 ) | |
| 19 | |
| 20 // tools keeps track of command-line tools that are available. | |
| 21 type tools struct { | |
| 22 sync.Mutex | |
| 23 | |
| 24 pathMap map[string]string | |
| 25 } | |
| 26 | |
| 27 func (t *tools) getLookup(command string) (string, error) { | |
| 28 t.Lock() | |
| 29 defer t.Unlock() | |
| 30 | |
| 31 path, ok := t.pathMap[command] | |
| 32 if !ok { | |
| 33 // This is the first lookup for the tool. | |
| 34 var err error | |
| 35 path, err = exec.LookPath(command) | |
| 36 if err == nil { | |
| 37 path, err = filepath.Abs(path) | |
| 38 } | |
| 39 if err != nil { | |
| 40 path = "" | |
| 41 } | |
| 42 | |
| 43 if t.pathMap == nil { | |
| 44 t.pathMap = make(map[string]string) | |
| 45 } | |
| 46 t.pathMap[command] = path | |
| 47 } | |
| 48 | |
| 49 if path == "" { | |
| 50 // Lookup was attempted, but tool could not be found. | |
| 51 return "", errors.Reason("tool %(toolName)q is not available").D
("toolName", command).Err() | |
| 52 } | |
| 53 return path, nil | |
| 54 } | |
| 55 | |
| 56 func (t *tools) genericTool(name string) (*genericTool, error) { | |
| 57 exe, err := t.getLookup(name) | |
| 58 if err != nil { | |
| 59 return nil, err | |
| 60 } | |
| 61 return &genericTool{ | |
| 62 exe: exe, | |
| 63 }, nil | |
| 64 } | |
| 65 | |
| 66 func (t *tools) python() (*genericTool, error) { return t.genericTool("python")
} | |
| 67 func (t *tools) docker() (*genericTool, error) { return t.genericTool("docker")
} | |
| 68 func (t *tools) appcfg() (*genericTool, error) { return t.genericTool("appcfg.py
") } | |
| 69 | |
| 70 func (t *tools) git() (*gitTool, error) { | |
| 71 exe, err := t.getLookup("git") | |
| 72 if err != nil { | |
| 73 return nil, err | |
| 74 } | |
| 75 return &gitTool{ | |
| 76 exe: exe, | |
| 77 }, nil | |
| 78 } | |
| 79 | |
| 80 func (t *tools) goTool(goPath []string) (*goTool, error) { | |
| 81 exe, err := t.getLookup("go") | |
| 82 if err != nil { | |
| 83 return nil, err | |
| 84 } | |
| 85 return &goTool{ | |
| 86 exe: exe, | |
| 87 goPath: goPath, | |
| 88 }, nil | |
| 89 } | |
| 90 | |
| 91 func (t *tools) kubectl(context string) (*kubeTool, error) { | |
| 92 exe, err := t.getLookup("kubectl") | |
| 93 if err != nil { | |
| 94 return nil, err | |
| 95 } | |
| 96 return &kubeTool{ | |
| 97 exe: exe, | |
| 98 ctx: context, | |
| 99 }, nil | |
| 100 } | |
| 101 | |
| 102 func (t *tools) gcloud(project string) (*gcloudTool, error) { | |
| 103 exe, err := t.getLookup("gcloud") | |
| 104 if err != nil { | |
| 105 return nil, err | |
| 106 } | |
| 107 return &gcloudTool{ | |
| 108 exe: exe, | |
| 109 project: project, | |
| 110 }, nil | |
| 111 } | |
| 112 | |
| 113 func (t *tools) aedeploy(goPath []string) (*aedeployTool, error) { | |
| 114 exe, err := t.getLookup("aedeploy") | |
| 115 if err != nil { | |
| 116 return nil, err | |
| 117 } | |
| 118 return &aedeployTool{ | |
| 119 exe: exe, | |
| 120 goPath: goPath, | |
| 121 }, nil | |
| 122 } | |
| 123 | |
| 124 type genericTool struct { | |
| 125 exe string | |
| 126 } | |
| 127 | |
| 128 func (t *genericTool) exec(command string, args ...string) *workExecutor { | |
| 129 return execute(t.exe, append([]string{command}, args...)...) | |
| 130 } | |
| 131 | |
| 132 type gitTool struct { | |
| 133 exe string | |
| 134 } | |
| 135 | |
| 136 func (t *gitTool) exec(gitDir string, command string, args ...string) *workExecu
tor { | |
| 137 return execute(t.exe, append([]string{"-C", gitDir, command}, args...)..
.) | |
| 138 } | |
| 139 | |
| 140 func (t *gitTool) clone(c context.Context, src, dst string) error { | |
| 141 return t.exec(".", "clone", src, dst).check(c) | |
| 142 } | |
| 143 | |
| 144 func (t *gitTool) getHEAD(c context.Context, gitDir string) (string, error) { | |
| 145 x := t.exec(gitDir, "rev-parse", "HEAD") | |
| 146 if err := x.check(c); err != nil { | |
| 147 return "", err | |
| 148 } | |
| 149 rev := strings.TrimSpace(x.stdout.String()) | |
| 150 if len(rev) == 0 { | |
| 151 return "", errors.New("invalid empty revision") | |
| 152 } | |
| 153 return rev, nil | |
| 154 } | |
| 155 | |
| 156 func (t *gitTool) getMergeBase(c context.Context, gitDir, remote string) (string
, error) { | |
| 157 x := t.exec(gitDir, "merge-base", "HEAD", remote) | |
| 158 if err := x.check(c); err != nil { | |
| 159 return "", err | |
| 160 } | |
| 161 rev := strings.TrimSpace(x.stdout.String()) | |
| 162 if len(rev) == 0 { | |
| 163 return "", errors.New("invalid empty revision") | |
| 164 } | |
| 165 return rev, nil | |
| 166 } | |
| 167 | |
| 168 func (t *gitTool) getRevListCount(c context.Context, gitDir string) (int, error)
{ | |
| 169 x := t.exec(gitDir, "rev-list", "--count", "HEAD") | |
| 170 if err := x.check(c); err != nil { | |
| 171 return 0, err | |
| 172 } | |
| 173 | |
| 174 output := strings.TrimSpace(x.stdout.String()) | |
| 175 v, err := strconv.Atoi(output) | |
| 176 if err != nil { | |
| 177 return 0, errors.Annotate(err).Reason("failed to parse rev-list
count").D("output", output).Err() | |
| 178 } | |
| 179 return v, nil | |
| 180 } | |
| 181 | |
| 182 type goTool struct { | |
| 183 exe string | |
| 184 goPath []string | |
| 185 } | |
| 186 | |
| 187 func (t *goTool) exec(subCommand string, args ...string) *workExecutor { | |
| 188 return execute(t.exe, append([]string{subCommand}, args...)...).loadEnv(
os.Environ()).envPath("GOPATH", t.goPath...) | |
| 189 } | |
| 190 | |
| 191 func (t *goTool) build(c context.Context, out string, pkg ...string) error { | |
| 192 wtd := withTempDir | |
| 193 if out != "" { | |
| 194 wtd = func(f func(string) error) error { | |
| 195 return f(out) | |
| 196 } | |
| 197 } | |
| 198 | |
| 199 return wtd(func(tdir string) error { | |
| 200 return t.exec("build", pkg...).cwd(tdir).check(c) | |
| 201 }) | |
| 202 } | |
| 203 | |
| 204 type aedeployTool struct { | |
| 205 exe string | |
| 206 goPath []string | |
| 207 } | |
| 208 | |
| 209 func (t *aedeployTool) bootstrap(x *workExecutor) *workExecutor { | |
| 210 return x.bootstrap(t.exe).loadEnv(os.Environ()).envPath("GOPATH", t.goPa
th...) | |
| 211 } | |
| 212 | |
| 213 type gcloudTool struct { | |
| 214 exe string | |
| 215 project string | |
| 216 } | |
| 217 | |
| 218 func (t *gcloudTool) exec(command string, args ...string) *workExecutor { | |
| 219 return execute(t.exe, append([]string{"--project", t.project, command},
args...)...) | |
| 220 } | |
| OLD | NEW |