GoPLS Viewer

Home|gopls/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.
9package main
10
11import (
12    "bytes"
13    "flag"
14    "fmt"
15    "go/build"
16    exec "golang.org/x/sys/execabs"
17    "io/ioutil"
18    "log"
19    "os"
20    "path/filepath"
21    "regexp"
22    "runtime"
23    "strings"
24)
25
26var (
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
31func 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
45func detectrepo() string {
46    wderr := 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(wdrightdir) {
54            tail := wd[len(rightdir):]
55            end := strings.Index(tailstring(os.PathSeparator))
56            if end > 0 {
57                repo := tail[:end]
58                return repo
59            }
60        }
61    }
62
63    return "go"
64}
65
66var googleSourceRx = regexp.MustCompile(`(?m)^(go|go-review)?\.googlesource.com\b`)
67
68func checkCLA() {
69    slurperr := 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
85func expandUser(s stringstring {
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(homes[2:]))
100        } else {
101            s = filepath.Join(homes[2:])
102        }
103    }
104    return os.Expand(s, func(env stringstring {
105        if env == "HOME" {
106            return home
107        }
108        return os.Getenv(env)
109    })
110}
111
112func 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
126func 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        slurperr := 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                vslurp)
145        }
146    }
147}
148
149func checkWorkingDir() {
150    wderr := 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
158Current directory: %s
159GOPATH: %s
160`wdos.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(wdrightdir) {
172        dirExistserr := 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/"+*reporightdir)
181        }
182        log.Fatalf("Your current directory is:%q\n"+
183            "Working on golang/x/%v requires you be in %q\n",
184            wd, *reporightdir)
185    }
186}
187
188func firstGoPath() string {
189    list := filepath.SplitList(build.Default.GOPATH)
190    if len(list) < 1 {
191        return ""
192    }
193    return list[0]
194}
195
196func exists(path string) (boolerror) {
197    _err := os.Stat(path)
198    if os.IsNotExist(err) {
199        return falsenil
200    }
201    return trueerr
202}
203
204func inGoPath(wd stringbool {
205    if os.Getenv("GOPATH") == "" {
206        return false
207    }
208
209    for _path := range filepath.SplitList(os.Getenv("GOPATH")) {
210        if strings.HasPrefix(wdfilepath.Join(path"src")) {
211            return true
212        }
213    }
214
215    return false
216}
217
218// mostly check that they didn't clone from github
219func 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    remoteserr := 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(linewantRemote) {
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"curRemotewantRemote)
242        }
243        matches++
244    }
245    if matches == 0 {
246        log.Fatalf("git remote -v output didn't contain expected %q. Got:\n%s"wantRemoteremotes)
247    }
248}
249
250func cmdErr(err errorstring {
251    if eeok := err.(*exec.ExitError); ok && len(ee.Stderr) > 0 {
252        return fmt.Sprintf("%s: %s"erree.Stderr)
253    }
254    return fmt.Sprint(err)
255}
256
257func 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\""cmdcmd)
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"cmdcmdErr(err))
283            }
284        }
285    }
286    if missing {
287        log.Fatalf("Missing aliases. (While optional, this tool assumes you use them.)")
288    }
289}
290
MembersX
checkGitOrigin.remotes
checkGitOrigin.matches
cmdErr.err
detectrepo.RangeStmt_1230.BlockStmt.BlockStmt.end
checkWorkingDir.gopath
checkGoroot.BlockStmt.slurp
checkGitCodeReview.RangeStmt_7276.BlockStmt.v
cookiesFile._
checkGoroot.v
cookiesFile.s
checkWorkingDir
checkGitOrigin.BlockStmt.msg
checkGitOrigin.RangeStmt_5830.line
exec
cookiesFile.out
expandUser
exists.err
checkCLA.err
cookiesFile
checkGitOrigin._
checkGitCodeReview.RangeStmt_7276.cmd
bytes
checkCLA
firstGoPath.list
ioutil
firstGoPath
cmdErr
checkGitCodeReview
checkCLA.slurp
checkGitOrigin.err
checkWorkingDir.BlockStmt.err
checkGitCodeReview._
fmt
detectrepo.wd
inGoPath.wd
checkGitCodeReview.BlockStmt.err
runtime
checkWorkingDir.wd
detectrepo.RangeStmt_1230.path
checkGoroot
checkWorkingDir.rightdir
checkWorkingDir.BlockStmt.dirExists
inGoPath
inGoPath.RangeStmt_5113.path
os
filepath
checkGitCodeReview.RangeStmt_7276.BlockStmt.BlockStmt.err
expandUser.home
exists
checkGitCodeReview.err
flag
regexp
expandUser.s
expandUser.env
build
strings
checkGitOrigin
main
checkGoroot.BlockStmt.err
exists._
log
checkWorkingDir.err
checkGitCodeReview.missing
exists.path
checkGitCodeReview.RangeStmt_7276.BlockStmt._
detectrepo
detectrepo.err
Members
X