Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201105173854-bc9fc8d8c4bc / cmd / go-contrib-init / contrib.go
1 // Copyright 2017 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 // The go-contrib-init command helps new Go contributors get their development
6 // environment set up for the Go contribution process.
7 //
8 // It aims to be a complement or alternative to https://golang.org/doc/contribute.html.
9 package main
10
11 import (
12         "bytes"
13         "flag"
14         "fmt"
15         "go/build"
16         "io/ioutil"
17         "log"
18         "os"
19         "os/exec"
20         "path/filepath"
21         "regexp"
22         "runtime"
23         "strings"
24 )
25
26 var (
27         repo = flag.String("repo", detectrepo(), "Which go repo you want to contribute to. Use \"go\" for the core, or e.g. \"net\" for golang.org/x/net/*")
28         dry  = flag.Bool("dry-run", false, "Fail with problems instead of trying to fix things.")
29 )
30
31 func main() {
32         log.SetFlags(0)
33         flag.Parse()
34
35         checkCLA()
36         checkGoroot()
37         checkWorkingDir()
38         checkGitOrigin()
39         checkGitCodeReview()
40         fmt.Print("All good. Happy hacking!\n" +
41                 "Remember to squash your revised commits and preserve the magic Change-Id lines.\n" +
42                 "Next steps: https://golang.org/doc/contribute.html#commit_changes\n")
43 }
44
45 func detectrepo() string {
46         wd, err := os.Getwd()
47         if err != nil {
48                 return "go"
49         }
50
51         for _, path := range filepath.SplitList(build.Default.GOPATH) {
52                 rightdir := filepath.Join(path, "src", "golang.org", "x") + string(os.PathSeparator)
53                 if strings.HasPrefix(wd, rightdir) {
54                         tail := wd[len(rightdir):]
55                         end := strings.Index(tail, string(os.PathSeparator))
56                         if end > 0 {
57                                 repo := tail[:end]
58                                 return repo
59                         }
60                 }
61         }
62
63         return "go"
64 }
65
66 var googleSourceRx = regexp.MustCompile(`(?m)^(go|go-review)?\.googlesource.com\b`)
67
68 func checkCLA() {
69         slurp, err := ioutil.ReadFile(cookiesFile())
70         if err != nil && !os.IsNotExist(err) {
71                 log.Fatal(err)
72         }
73         if googleSourceRx.Match(slurp) {
74                 // Probably good.
75                 return
76         }
77         log.Fatal("Your .gitcookies file isn't configured.\n" +
78                 "Next steps:\n" +
79                 "  * Submit a CLA (https://golang.org/doc/contribute.html#cla) if not done\n" +
80                 "  * Go to https://go.googlesource.com/ and click \"Generate Password\" at the top,\n" +
81                 "    then follow instructions.\n" +
82                 "  * Run go-contrib-init again.\n")
83 }
84
85 func expandUser(s string) string {
86         env := "HOME"
87         if runtime.GOOS == "windows" {
88                 env = "USERPROFILE"
89         } else if runtime.GOOS == "plan9" {
90                 env = "home"
91         }
92         home := os.Getenv(env)
93         if home == "" {
94                 return s
95         }
96
97         if len(s) >= 2 && s[0] == '~' && os.IsPathSeparator(s[1]) {
98                 if runtime.GOOS == "windows" {
99                         s = filepath.ToSlash(filepath.Join(home, s[2:]))
100                 } else {
101                         s = filepath.Join(home, s[2:])
102                 }
103         }
104         return os.Expand(s, func(env string) string {
105                 if env == "HOME" {
106                         return home
107                 }
108                 return os.Getenv(env)
109         })
110 }
111
112 func cookiesFile() string {
113         out, _ := exec.Command("git", "config", "http.cookiefile").Output()
114         if s := strings.TrimSpace(string(out)); s != "" {
115                 if strings.HasPrefix(s, "~") {
116                         s = expandUser(s)
117                 }
118                 return s
119         }
120         if runtime.GOOS == "windows" {
121                 return filepath.Join(os.Getenv("USERPROFILE"), ".gitcookies")
122         }
123         return filepath.Join(os.Getenv("HOME"), ".gitcookies")
124 }
125
126 func checkGoroot() {
127         v := os.Getenv("GOROOT")
128         if v == "" {
129                 return
130         }
131         if *repo == "go" {
132                 if strings.HasPrefix(v, "/usr/") {
133                         log.Fatalf("Your GOROOT environment variable is set to %q\n"+
134                                 "This is almost certainly not what you want. Either unset\n"+
135                                 "your GOROOT or set it to the path of your development version\n"+
136                                 "of Go.", v)
137                 }
138                 slurp, err := ioutil.ReadFile(filepath.Join(v, "VERSION"))
139                 if err == nil {
140                         slurp = bytes.TrimSpace(slurp)
141                         log.Fatalf("Your GOROOT environment variable is set to %q\n"+
142                                 "But that path is to a binary release of Go, with VERSION file %q.\n"+
143                                 "You should hack on Go in a fresh checkout of Go. Fix or unset your GOROOT.\n",
144                                 v, slurp)
145                 }
146         }
147 }
148
149 func checkWorkingDir() {
150         wd, err := os.Getwd()
151         if err != nil {
152                 log.Fatal(err)
153         }
154         if *repo == "go" {
155                 if inGoPath(wd) {
156                         log.Fatalf(`You can't work on Go from within your GOPATH. Please checkout Go outside of your GOPATH
157
158 Current directory: %s
159 GOPATH: %s
160 `, wd, os.Getenv("GOPATH"))
161                 }
162                 return
163         }
164
165         gopath := firstGoPath()
166         if gopath == "" {
167                 log.Fatal("Your GOPATH is not set, please set it")
168         }
169
170         rightdir := filepath.Join(gopath, "src", "golang.org", "x", *repo)
171         if !strings.HasPrefix(wd, rightdir) {
172                 dirExists, err := exists(rightdir)
173                 if err != nil {
174                         log.Fatal(err)
175                 }
176                 if !dirExists {
177                         log.Fatalf("The repo you want to work on is currently not on your system.\n"+
178                                 "Run %q to obtain this repo\n"+
179                                 "then go to the directory %q\n",
180                                 "go get -d golang.org/x/"+*repo, rightdir)
181                 }
182                 log.Fatalf("Your current directory is:%q\n"+
183                         "Working on golang/x/%v requires you be in %q\n",
184                         wd, *repo, rightdir)
185         }
186 }
187
188 func firstGoPath() string {
189         list := filepath.SplitList(build.Default.GOPATH)
190         if len(list) < 1 {
191                 return ""
192         }
193         return list[0]
194 }
195
196 func exists(path string) (bool, error) {
197         _, err := os.Stat(path)
198         if os.IsNotExist(err) {
199                 return false, nil
200         }
201         return true, err
202 }
203
204 func inGoPath(wd string) bool {
205         if os.Getenv("GOPATH") == "" {
206                 return false
207         }
208
209         for _, path := range filepath.SplitList(os.Getenv("GOPATH")) {
210                 if strings.HasPrefix(wd, filepath.Join(path, "src")) {
211                         return true
212                 }
213         }
214
215         return false
216 }
217
218 // mostly check that they didn't clone from github
219 func checkGitOrigin() {
220         if _, err := exec.LookPath("git"); err != nil {
221                 log.Fatalf("You don't appear to have git installed. Do that.")
222         }
223         wantRemote := "https://go.googlesource.com/" + *repo
224         remotes, err := exec.Command("git", "remote", "-v").Output()
225         if err != nil {
226                 msg := cmdErr(err)
227                 if strings.Contains(msg, "Not a git repository") {
228                         log.Fatalf("Your current directory is not in a git checkout of %s", wantRemote)
229                 }
230                 log.Fatalf("Error running git remote -v: %v", msg)
231         }
232         matches := 0
233         for _, line := range strings.Split(string(remotes), "\n") {
234                 line = strings.TrimSpace(line)
235                 if !strings.HasPrefix(line, "origin") {
236                         continue
237                 }
238                 if !strings.Contains(line, wantRemote) {
239                         curRemote := strings.Fields(strings.TrimPrefix(line, "origin"))[0]
240                         // TODO: if not in dryRun mode, just fix it?
241                         log.Fatalf("Current directory's git was cloned from %q; origin should be %q", curRemote, wantRemote)
242                 }
243                 matches++
244         }
245         if matches == 0 {
246                 log.Fatalf("git remote -v output didn't contain expected %q. Got:\n%s", wantRemote, remotes)
247         }
248 }
249
250 func cmdErr(err error) string {
251         if ee, ok := err.(*exec.ExitError); ok && len(ee.Stderr) > 0 {
252                 return fmt.Sprintf("%s: %s", err, ee.Stderr)
253         }
254         return fmt.Sprint(err)
255 }
256
257 func checkGitCodeReview() {
258         if _, err := exec.LookPath("git-codereview"); err != nil {
259                 if *dry {
260                         log.Fatalf("You don't appear to have git-codereview tool. While this is technically optional,\n" +
261                                 "almost all Go contributors use it. Our documentation and this tool assume it is used.\n" +
262                                 "To install it, run:\n\n\t$ go get golang.org/x/review/git-codereview\n\n(Then run go-contrib-init again)")
263                 }
264                 err := exec.Command("go", "get", "golang.org/x/review/git-codereview").Run()
265                 if err != nil {
266                         log.Fatalf("Error running go get golang.org/x/review/git-codereview: %v", cmdErr(err))
267                 }
268                 log.Printf("Installed git-codereview (ran `go get golang.org/x/review/git-codereview`)")
269         }
270         missing := false
271         for _, cmd := range []string{"change", "gofmt", "mail", "pending", "submit", "sync"} {
272                 v, _ := exec.Command("git", "config", "alias."+cmd).Output()
273                 if strings.Contains(string(v), "codereview") {
274                         continue
275                 }
276                 if *dry {
277                         log.Printf("Missing alias. Run:\n\t$ git config alias.%s \"codereview %s\"", cmd, cmd)
278                         missing = true
279                 } else {
280                         err := exec.Command("git", "config", "alias."+cmd, "codereview "+cmd).Run()
281                         if err != nil {
282                                 log.Fatalf("Error setting alias.%s: %v", cmd, cmdErr(err))
283                         }
284                 }
285         }
286         if missing {
287                 log.Fatalf("Missing aliases. (While optional, this tool assumes you use them.)")
288         }
289 }