├── .gitignore ├── vendor ├── github.com │ ├── kr │ │ ├── pretty │ │ │ ├── .gitignore │ │ │ ├── Readme │ │ │ ├── License │ │ │ ├── zero.go │ │ │ ├── pretty.go │ │ │ ├── diff.go │ │ │ └── formatter.go │ │ ├── fs │ │ │ ├── Readme │ │ │ ├── filesystem.go │ │ │ ├── LICENSE │ │ │ └── walk.go │ │ └── text │ │ │ ├── doc.go │ │ │ ├── Readme │ │ │ ├── License │ │ │ ├── indent.go │ │ │ └── wrap.go │ └── pmezard │ │ └── go-difflib │ │ └── LICENSE └── golang.org │ └── x │ └── tools │ ├── go │ └── vcs │ │ ├── env.go │ │ ├── http.go │ │ └── discovery.go │ ├── PATENTS │ └── LICENSE ├── .vscode ├── settings.json └── tasks.json ├── Godeps ├── Readme └── Godeps.json ├── .github └── ISSUE_TEMPLATE ├── doc.go ├── errors.go ├── msg.go ├── path.go ├── util.go ├── FAQ.md ├── version.go ├── license.go ├── License ├── .travis.yml ├── dep_test.go ├── diff.go ├── pkg.go ├── get.go ├── license_test.go ├── diff_test.go ├── vcs_test.go ├── dep.go ├── go.go ├── match_test.go ├── rewrite.go ├── godepfile.go ├── restore.go ├── main.go ├── update.go ├── rewrite_test.go ├── Readme.md ├── vcs.go ├── Changelog.md ├── save.go ├── list.go └── update_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | /godep 2 | -------------------------------------------------------------------------------- /vendor/github.com/kr/pretty/.gitignore: -------------------------------------------------------------------------------- 1 | [568].out 2 | _go* 3 | _test* 4 | _obj 5 | -------------------------------------------------------------------------------- /vendor/github.com/kr/fs/Readme: -------------------------------------------------------------------------------- 1 | Filesystem Package 2 | 3 | http://godoc.org/github.com/kr/fs 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | } -------------------------------------------------------------------------------- /vendor/github.com/kr/text/doc.go: -------------------------------------------------------------------------------- 1 | // Package text provides rudimentary functions for manipulating text in 2 | // paragraphs. 3 | package text 4 | -------------------------------------------------------------------------------- /Godeps/Readme: -------------------------------------------------------------------------------- 1 | This directory tree is generated automatically by godep. 2 | 3 | Please do not edit. 4 | 5 | See https://github.com/tools/godep for more information. 6 | -------------------------------------------------------------------------------- /vendor/github.com/kr/text/Readme: -------------------------------------------------------------------------------- 1 | This is a Go package for manipulating paragraphs of text. 2 | 3 | See http://go.pkgdoc.org/github.com/kr/text for full documentation. 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE: -------------------------------------------------------------------------------- 1 | ### Expected behavior 2 | 3 | ### Actual behavior 4 | 5 | ### Steps to reproduce behavior 6 | 7 | ### `godep version` output 8 | 9 | ### `go version` output 10 | 11 | ### Contents of Godeps.json file -------------------------------------------------------------------------------- /vendor/github.com/kr/pretty/Readme: -------------------------------------------------------------------------------- 1 | package pretty 2 | 3 | import "github.com/kr/pretty" 4 | 5 | Package pretty provides pretty-printing for Go values. 6 | 7 | Documentation 8 | 9 | http://godoc.org/github.com/kr/pretty 10 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Command godep helps build packages reproducibly by fixing 4 | their dependencies. 5 | 6 | Example Usage 7 | 8 | Save currently-used dependencies to file Godeps: 9 | 10 | $ godep save 11 | 12 | Build project using saved dependencies: 13 | 14 | $ godep go install 15 | 16 | or 17 | 18 | $ GOPATH=`godep path`:$GOPATH 19 | $ go install 20 | 21 | */ 22 | package main 23 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "0.1.0", 5 | "command": "go", 6 | "isShellCommand": true, 7 | "args": ["test"], 8 | "showOutput": "always", 9 | "tasks": [ 10 | { 11 | "taskName":"Local Test", 12 | "args":[ "-v", "-run=Update"], 13 | "suppressTaskName": true 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /errors.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "errors" 4 | 5 | var ( 6 | errorLoadingDeps = errors.New("error loading dependencies") 7 | errorLoadingPackages = errors.New("error loading packages") 8 | errorCopyingSourceCode = errors.New("error copying source code") 9 | errorNoPackagesUpdatable = errors.New("no packages can be updated") 10 | ) 11 | 12 | type errPackageNotFound struct { 13 | path string 14 | } 15 | 16 | func (e errPackageNotFound) Error() string { 17 | return "Package (" + e.path + ") not found" 18 | } 19 | -------------------------------------------------------------------------------- /msg.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | 7 | "github.com/kr/pretty" 8 | ) 9 | 10 | func debugln(a ...interface{}) (int, error) { 11 | if debug { 12 | return fmt.Println(a...) 13 | } 14 | return 0, nil 15 | } 16 | 17 | func verboseln(a ...interface{}) { 18 | if verbose { 19 | log.Println(a...) 20 | } 21 | } 22 | 23 | func debugf(format string, a ...interface{}) (int, error) { 24 | if debug { 25 | return fmt.Printf(format, a...) 26 | } 27 | return 0, nil 28 | } 29 | 30 | func ppln(a ...interface{}) (int, error) { 31 | if debug { 32 | return pretty.Println(a...) 33 | } 34 | return 0, nil 35 | } 36 | -------------------------------------------------------------------------------- /Godeps/Godeps.json: -------------------------------------------------------------------------------- 1 | { 2 | "ImportPath": "github.com/tools/godep", 3 | "GoVersion": "go1.7", 4 | "GodepVersion": "v74", 5 | "Deps": [ 6 | { 7 | "ImportPath": "github.com/kr/fs", 8 | "Rev": "2788f0dbd16903de03cb8186e5c7d97b69ad387b" 9 | }, 10 | { 11 | "ImportPath": "github.com/kr/pretty", 12 | "Comment": "go.weekly.2011-12-22-24-gf31442d", 13 | "Rev": "f31442d60e51465c69811e2107ae978868dbea5c" 14 | }, 15 | { 16 | "ImportPath": "github.com/kr/text", 17 | "Rev": "6807e777504f54ad073ecef66747de158294b639" 18 | }, 19 | { 20 | "ImportPath": "github.com/pmezard/go-difflib/difflib", 21 | "Rev": "f78a839676152fd9f4863704f5d516195c18fc14" 22 | }, 23 | { 24 | "ImportPath": "golang.org/x/tools/go/vcs", 25 | "Rev": "1f1b3322f67af76803c942fd237291538ec68262" 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /path.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | ) 7 | 8 | var cmdPath = &Command{ 9 | Name: "path", 10 | Short: "print GOPATH for dependency code", 11 | Long: ` 12 | Command path prints a path for use in env var GOPATH 13 | that makes available the specified version of each dependency. 14 | 15 | The printed path does not include any GOPATH value from 16 | the environment. 17 | 18 | For more about how GOPATH works, see 'go help gopath'. 19 | `, 20 | Run: runPath, 21 | OnlyInGOPATH: true, 22 | } 23 | 24 | // Print the gopath that points to 25 | // the included dependency code. 26 | func runPath(cmd *Command, args []string) { 27 | if len(args) != 0 { 28 | cmd.UsageExit() 29 | } 30 | if VendorExperiment { 31 | fmt.Fprintln(os.Stderr, "Error: GO15VENDOREXPERIMENT is enabled and the vendor/ directory is not a valid Go workspace.") 32 | os.Exit(1) 33 | } 34 | gopath := prepareGopath() 35 | fmt.Println(gopath) 36 | } 37 | -------------------------------------------------------------------------------- /util.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "path/filepath" 5 | "runtime" 6 | "strings" 7 | ) 8 | 9 | // driveLetterToUpper converts Windows path's drive letters to uppercase. This 10 | // is needed when comparing 2 paths with different drive letter case. 11 | func driveLetterToUpper(path string) string { 12 | if runtime.GOOS != "windows" || path == "" { 13 | return path 14 | } 15 | 16 | p := path 17 | 18 | // If path's drive letter is lowercase, change it to uppercase. 19 | if len(p) >= 2 && p[1] == ':' && 'a' <= p[0] && p[0] <= 'z' { 20 | p = string(p[0]+'A'-'a') + p[1:] 21 | } 22 | 23 | return p 24 | } 25 | 26 | // clean the path and ensure that a drive letter is upper case (if it exists). 27 | func cleanPath(path string) string { 28 | return driveLetterToUpper(filepath.Clean(path)) 29 | } 30 | 31 | // deal with case insensitive filesystems and other weirdness 32 | func pathEqual(a, b string) bool { 33 | a = cleanPath(a) 34 | b = cleanPath(b) 35 | return strings.EqualFold(a, b) 36 | } 37 | -------------------------------------------------------------------------------- /vendor/github.com/kr/text/License: -------------------------------------------------------------------------------- 1 | Copyright 2012 Keith Rarick 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /vendor/github.com/kr/pretty/License: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright 2012 Keith Rarick 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /vendor/github.com/kr/pretty/zero.go: -------------------------------------------------------------------------------- 1 | package pretty 2 | 3 | import ( 4 | "reflect" 5 | ) 6 | 7 | func nonzero(v reflect.Value) bool { 8 | switch v.Kind() { 9 | case reflect.Bool: 10 | return v.Bool() 11 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 12 | return v.Int() != 0 13 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 14 | return v.Uint() != 0 15 | case reflect.Float32, reflect.Float64: 16 | return v.Float() != 0 17 | case reflect.Complex64, reflect.Complex128: 18 | return v.Complex() != complex(0, 0) 19 | case reflect.String: 20 | return v.String() != "" 21 | case reflect.Struct: 22 | for i := 0; i < v.NumField(); i++ { 23 | if nonzero(getField(v, i)) { 24 | return true 25 | } 26 | } 27 | return false 28 | case reflect.Array: 29 | for i := 0; i < v.Len(); i++ { 30 | if nonzero(v.Index(i)) { 31 | return true 32 | } 33 | } 34 | return false 35 | case reflect.Map, reflect.Interface, reflect.Slice, reflect.Ptr, reflect.Chan, reflect.Func: 36 | return !v.IsNil() 37 | case reflect.UnsafePointer: 38 | return v.Pointer() != 0 39 | } 40 | return true 41 | } 42 | -------------------------------------------------------------------------------- /vendor/golang.org/x/tools/go/vcs/env.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 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 | package vcs 6 | 7 | import ( 8 | "os" 9 | "strings" 10 | ) 11 | 12 | // envForDir returns a copy of the environment 13 | // suitable for running in the given directory. 14 | // The environment is the current process's environment 15 | // but with an updated $PWD, so that an os.Getwd in the 16 | // child will be faster. 17 | func envForDir(dir string) []string { 18 | env := os.Environ() 19 | // Internally we only use rooted paths, so dir is rooted. 20 | // Even if dir is not rooted, no harm done. 21 | return mergeEnvLists([]string{"PWD=" + dir}, env) 22 | } 23 | 24 | // mergeEnvLists merges the two environment lists such that 25 | // variables with the same name in "in" replace those in "out". 26 | func mergeEnvLists(in, out []string) []string { 27 | NextVar: 28 | for _, inkv := range in { 29 | k := strings.SplitAfterN(inkv, "=", 2)[0] 30 | for i, outkv := range out { 31 | if strings.HasPrefix(outkv, k) { 32 | out[i] = inkv 33 | continue NextVar 34 | } 35 | } 36 | out = append(out, inkv) 37 | } 38 | return out 39 | } 40 | -------------------------------------------------------------------------------- /FAQ.md: -------------------------------------------------------------------------------- 1 | ## Why do I need to check in `vendor/`? 2 | 3 | godep's primary concern is to allow you to repeatably build your project. Your 4 | dependencies are part of that project. Without them it won't build. Not 5 | committing `vendor/` adds additional external dependencies that are outside of 6 | your control. In Go, fetching packages is tied to multiple external systems 7 | (DNS, web servers, etc). Over time other developers or code hosting sites may 8 | discontinue service, delete code, force push, or take any number of other 9 | actions that may make a package unreachable. Therefore it's the opinion of the 10 | godep authors that `vendor/` should always be checked in. 11 | 12 | ## Should I use `godep restore`? 13 | 14 | Probably not, unless you **need** to. Situations where you would **need** to are: 15 | 16 | 1. Using older Godep Workspaces (`Godeps/_workspace`) and not using `godep go 17 | `. 18 | 1. Resetting the state of $GOPATH to what is in your `Godeps.json` file in order 19 | to cleanly re-vendor everything w/o upgrading/changing any deps. This is 20 | useful when [migrating](https://github.com/tools/godep#migrating-to-vendor) 21 | from workspaces to `vendor` or when a bug is fixed in `godep` that cleans up 22 | a previous vendoring error. 23 | -------------------------------------------------------------------------------- /vendor/github.com/kr/fs/filesystem.go: -------------------------------------------------------------------------------- 1 | package fs 2 | 3 | import ( 4 | "io/ioutil" 5 | "os" 6 | "path/filepath" 7 | ) 8 | 9 | // FileSystem defines the methods of an abstract filesystem. 10 | type FileSystem interface { 11 | 12 | // ReadDir reads the directory named by dirname and returns a 13 | // list of directory entries. 14 | ReadDir(dirname string) ([]os.FileInfo, error) 15 | 16 | // Lstat returns a FileInfo describing the named file. If the file is a 17 | // symbolic link, the returned FileInfo describes the symbolic link. Lstat 18 | // makes no attempt to follow the link. 19 | Lstat(name string) (os.FileInfo, error) 20 | 21 | // Join joins any number of path elements into a single path, adding a 22 | // separator if necessary. The result is Cleaned; in particular, all 23 | // empty strings are ignored. 24 | // 25 | // The separator is FileSystem specific. 26 | Join(elem ...string) string 27 | } 28 | 29 | // fs represents a FileSystem provided by the os package. 30 | type fs struct{} 31 | 32 | func (f *fs) ReadDir(dirname string) ([]os.FileInfo, error) { return ioutil.ReadDir(dirname) } 33 | 34 | func (f *fs) Lstat(name string) (os.FileInfo, error) { return os.Lstat(name) } 35 | 36 | func (f *fs) Join(elem ...string) string { return filepath.Join(elem...) } 37 | -------------------------------------------------------------------------------- /vendor/golang.org/x/tools/PATENTS: -------------------------------------------------------------------------------- 1 | Additional IP Rights Grant (Patents) 2 | 3 | "This implementation" means the copyrightable works distributed by 4 | Google as part of the Go project. 5 | 6 | Google hereby grants to You a perpetual, worldwide, non-exclusive, 7 | no-charge, royalty-free, irrevocable (except as stated in this section) 8 | patent license to make, have made, use, offer to sell, sell, import, 9 | transfer and otherwise run, modify and propagate the contents of this 10 | implementation of Go, where such license applies only to those patent 11 | claims, both currently owned or controlled by Google and acquired in 12 | the future, licensable by Google that are necessarily infringed by this 13 | implementation of Go. This grant does not include claims that would be 14 | infringed only as a consequence of further modification of this 15 | implementation. If you or your agent or exclusive licensee institute or 16 | order or agree to the institution of patent litigation against any 17 | entity (including a cross-claim or counterclaim in a lawsuit) alleging 18 | that this implementation of Go or any code incorporated within this 19 | implementation of Go constitutes direct or contributory patent 20 | infringement, or inducement of patent infringement, then any patent 21 | rights granted to you under this License for this implementation of Go 22 | shall terminate as of the date such litigation is filed. 23 | -------------------------------------------------------------------------------- /vendor/github.com/pmezard/go-difflib/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Patrick Mezard 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | The names of its contributors may not be used to endorse or promote 14 | products derived from this software without specific prior written 15 | permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 18 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 19 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 20 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 23 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 24 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 25 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 26 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendor/github.com/kr/fs/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendor/golang.org/x/tools/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /version.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "runtime" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | const version = 80 12 | 13 | var cmdVersion = &Command{ 14 | Name: "version", 15 | Short: "show version info", 16 | Long: ` 17 | 18 | Displays the version of godep as well as the target OS, architecture and go runtime version. 19 | `, 20 | Run: runVersion, 21 | } 22 | 23 | func versionString() string { 24 | return fmt.Sprintf("godep v%d (%s/%s/%s)", version, runtime.GOOS, runtime.GOARCH, runtime.Version()) 25 | } 26 | 27 | func runVersion(cmd *Command, args []string) { 28 | fmt.Printf("%s\n", versionString()) 29 | } 30 | 31 | func GoVersionFields(c rune) bool { 32 | return c == 'g' || c == 'o' || c == '.' 33 | } 34 | 35 | // isSameOrNewer go version (goA.B) 36 | // go1.6 >= go1.6 == true 37 | // go1.5 >= go1.6 == false 38 | func isSameOrNewer(base, check string) bool { 39 | if base == check { 40 | return true 41 | } 42 | if strings.HasPrefix(check, "devel-") { 43 | return true 44 | } 45 | bp := strings.FieldsFunc(base, GoVersionFields) 46 | cp := strings.FieldsFunc(check, GoVersionFields) 47 | if len(bp) < 2 || len(cp) < 2 { 48 | log.Fatalf("Error comparing %s to %s\n", base, check) 49 | } 50 | if bp[0] == cp[0] { // We only have go version 1 right now 51 | bm, err := strconv.Atoi(bp[1]) 52 | // These errors are unlikely and there is nothing nice to do here anyway 53 | if err != nil { 54 | panic(err) 55 | } 56 | cm, err := strconv.Atoi(cp[1]) 57 | if err != nil { 58 | panic(err) 59 | } 60 | return cm >= bm 61 | } 62 | return false 63 | } 64 | -------------------------------------------------------------------------------- /license.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | // LicenseFilePrefix is a list of filename prefixes that indicate it 8 | // might contain a software license 9 | var LicenseFilePrefix = []string{ 10 | "licence", // UK spelling 11 | "license", // US spelling 12 | "copying", 13 | "unlicense", 14 | "copyright", 15 | "copyleft", 16 | "authors", 17 | "contributors", 18 | } 19 | 20 | // LegalFileSubstring are substrings that indicate the file is likely 21 | // to contain some type of legal declaration. "legal" is often used 22 | // that it might moved to LicenseFilePrefix 23 | var LegalFileSubstring = []string{ 24 | "legal", 25 | "notice", 26 | "disclaimer", 27 | "patent", 28 | "third-party", 29 | "thirdparty", 30 | } 31 | 32 | // IsLicenseFile returns true if the filename might be contain a 33 | // software license 34 | func IsLicenseFile(filename string) bool { 35 | lowerfile := strings.ToLower(filename) 36 | for _, prefix := range LicenseFilePrefix { 37 | if strings.HasPrefix(lowerfile, prefix) { 38 | return true 39 | } 40 | } 41 | return false 42 | } 43 | 44 | // IsLegalFile returns true if the file is likely to contain some type 45 | // of of legal declaration or licensing information 46 | func IsLegalFile(filename string) bool { 47 | lowerfile := strings.ToLower(filename) 48 | for _, prefix := range LicenseFilePrefix { 49 | if strings.HasPrefix(lowerfile, prefix) { 50 | return true 51 | } 52 | } 53 | for _, substring := range LegalFileSubstring { 54 | if strings.Contains(lowerfile, substring) { 55 | return true 56 | } 57 | } 58 | return false 59 | } 60 | -------------------------------------------------------------------------------- /License: -------------------------------------------------------------------------------- 1 | Copyright © 2013 Keith Rarick. 2 | Portions Copyright (c) 2012 The Go Authors. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above 11 | copyright notice, this list of conditions and the following disclaimer 12 | in the documentation and/or other materials provided with the 13 | distribution. 14 | * Neither the name of Google Inc. nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | sudo: false 3 | go: 1.6 4 | script: 5 | # Godep's unit tests run git, and git complains 6 | # if we don't set these config parameters. 7 | # We put dummy values here because they don't matter. 8 | - git config --global user.email "you@example.com" 9 | - git config --global user.name "Your Name" 10 | - test -z "$(go fmt)" 11 | - go vet 12 | - go test -v 13 | - go test -v -race 14 | - test -z "$(goimports -l .)" 15 | before_install: 16 | - go get golang.org/x/tools/cmd/goimports 17 | before_deploy: 18 | - export OS_TARGETS="linux darwin windows" 19 | - export ARCH_TARGETS="386 amd64" 20 | - go get github.com/mitchellh/gox 21 | - gox -os "$OS_TARGETS" -arch="$ARCH_TARGETS" 22 | deploy: 23 | skip_cleanup: true 24 | provider: releases 25 | api_key: 26 | secure: Q1JP8LziaXMTxFmNXiyC1YhS9e4M4WnI6UDjRTMf6mm1LZeJyUFOCCtXnifL7RyCIR1hpjp6s8M1aWE+NpuweF96IZI3Uk4ASx5C8FePC4qvhsCdtJ2sLD2GTIrp9b0MS9/+ao20AIbpVDSaLaF9IjqXpMxMyM0P8P5coRTkwItlGxmQbVJW3YuiYcPa8UojwM4EyafO2CIoUKapW8lwb9KcimBJV8PfF/XZjPVhMkn2ABhh5Hqbn2zBJtvPYMMzi0CnY50JQF5LwN3vGTMpTsRP+lOLCNbOWfkl+2hgG7VpKrtx+cX62knOodpF457sIJ31KUzmeLUVBejTGb1zuVeTojuyi8Huo8YBIBCcN+p3Dqd+n2ZK45mIrheGiEJIkf/vI4MI6A01Nu/o+xU0IPsVfAL/xU5j5nntEGfFWVoclPrl9qcfqf74xdRcARzcCJVmdc8iw49DBDHJfnPa3zxzVz//00+Rz6mZXmhk+Npk/HLLNW59vmJIjP+8XOtPor7dST9HrS1a9AcnmIjNuw9yfbwK5769SDVxCKgqNwXW/Dy5F39aIH5AL4I4y9hCEeeT8ctvSJHGOyiB9MWU5jnt5tluPtz5opG51tFXnIYP/XaWpTfO+eJ6x55pbwT+n3LfRS5l1POM+jGAFF1MFWwc14RY7qynEIEzm4Wb/UE= 27 | file: 28 | - godep_darwin_amd64 29 | - godep_linux_amd64 30 | - godep_windows_386.exe 31 | - godep_windows_amd64.exe 32 | on: 33 | tags: true 34 | repo: tools/godep 35 | -------------------------------------------------------------------------------- /dep_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "testing" 4 | 5 | func TestTrimGoVersion(t *testing.T) { 6 | var cases = []struct { 7 | in, out string 8 | err bool 9 | }{ 10 | {in: "go1.5", out: "go1.5", err: false}, 11 | {in: "go1.5beta1", out: "go1.5", err: false}, 12 | {in: "go1.6rc1", out: "go1.6", err: false}, 13 | {in: "go1.5.1", out: "go1.5", err: false}, 14 | {in: "devel", out: "devel", err: false}, 15 | {in: "devel+15f7a66", out: "devel-15f7a66", err: false}, 16 | {in: "devel-15f7a66", out: "devel-15f7a66", err: false}, 17 | {in: "boom", out: "", err: true}, 18 | } 19 | 20 | for _, c := range cases { 21 | mv, err := trimGoVersion(c.in) 22 | if err != nil && !c.err { 23 | t.Errorf("Unexpected error: %s", err) 24 | } 25 | if mv != c.out { 26 | t.Errorf("Expected trimGoVersion(%s) == '%s', but got '%s'", c.in, c.out, mv) 27 | } 28 | } 29 | } 30 | 31 | func TestGoVersion(t *testing.T) { 32 | var cases = []struct { 33 | o, r string 34 | err bool 35 | }{ 36 | {o: "go version go1.6.2 darwin/amd64", r: "go1.6", err: false}, 37 | {o: "go version go1.6 darwin/amd64", r: "go1.6", err: false}, 38 | {o: "go version go1.6.2 linux/amd64", r: "go1.6", err: false}, 39 | {o: "go version devel +da6205b Wed Apr 13 17:22:38 2016 +0000 darwin/amd64", r: "devel-da6205b", err: false}, 40 | } 41 | 42 | for _, c := range cases { 43 | goVersionTestOutput = c.o 44 | v, err := goVersion() 45 | if err != nil && !c.err { 46 | t.Errorf("Unexpected error: %s", err) 47 | } 48 | if v != c.r { 49 | t.Errorf("Expected goVersion() == '%s', but got '%s'", c.r, v) 50 | } 51 | goVersionTestOutput = "" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /diff.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "log" 7 | 8 | "github.com/pmezard/go-difflib/difflib" 9 | ) 10 | 11 | var cmdDiff = &Command{ 12 | Name: "diff", 13 | Short: "shows the diff between current and previously saved set of dependencies", 14 | Long: ` 15 | Shows the difference, in a unified diff format, between the 16 | current set of dependencies and those generated on a 17 | previous 'go save' execution. 18 | `, 19 | Run: runDiff, 20 | OnlyInGOPATH: true, 21 | } 22 | 23 | func runDiff(cmd *Command, args []string) { 24 | gold, err := loadDefaultGodepsFile() 25 | if err != nil { 26 | log.Fatalln(err) 27 | } 28 | 29 | pkgs := []string{"."} 30 | dot, err := LoadPackages(pkgs...) 31 | if err != nil { 32 | log.Fatalln(err) 33 | } 34 | 35 | gnew := &Godeps{ 36 | ImportPath: dot[0].ImportPath, 37 | GoVersion: gold.GoVersion, 38 | } 39 | 40 | err = gnew.fill(dot, dot[0].ImportPath) 41 | if err != nil { 42 | log.Fatalln(err) 43 | } 44 | 45 | diff, err := diffStr(&gold, gnew) 46 | if err != nil { 47 | log.Fatalln(err) 48 | } 49 | fmt.Println(diff) 50 | } 51 | 52 | // diffStr returns a unified diff string of two Godeps. 53 | func diffStr(a, b *Godeps) (string, error) { 54 | var ab, bb bytes.Buffer 55 | 56 | _, err := a.writeTo(&ab) 57 | if err != nil { 58 | log.Fatalln(err) 59 | } 60 | 61 | _, err = b.writeTo(&bb) 62 | if err != nil { 63 | log.Fatalln(err) 64 | } 65 | 66 | diff := difflib.UnifiedDiff{ 67 | A: difflib.SplitLines(ab.String()), 68 | B: difflib.SplitLines(bb.String()), 69 | FromFile: b.file(), 70 | ToFile: "$GOPATH", 71 | Context: 10, 72 | } 73 | return difflib.GetUnifiedDiffString(diff) 74 | } 75 | -------------------------------------------------------------------------------- /vendor/github.com/kr/text/indent.go: -------------------------------------------------------------------------------- 1 | package text 2 | 3 | import ( 4 | "io" 5 | ) 6 | 7 | // Indent inserts prefix at the beginning of each non-empty line of s. The 8 | // end-of-line marker is NL. 9 | func Indent(s, prefix string) string { 10 | return string(IndentBytes([]byte(s), []byte(prefix))) 11 | } 12 | 13 | // IndentBytes inserts prefix at the beginning of each non-empty line of b. 14 | // The end-of-line marker is NL. 15 | func IndentBytes(b, prefix []byte) []byte { 16 | var res []byte 17 | bol := true 18 | for _, c := range b { 19 | if bol && c != '\n' { 20 | res = append(res, prefix...) 21 | } 22 | res = append(res, c) 23 | bol = c == '\n' 24 | } 25 | return res 26 | } 27 | 28 | // Writer indents each line of its input. 29 | type indentWriter struct { 30 | w io.Writer 31 | bol bool 32 | pre [][]byte 33 | sel int 34 | off int 35 | } 36 | 37 | // NewIndentWriter makes a new write filter that indents the input 38 | // lines. Each line is prefixed in order with the corresponding 39 | // element of pre. If there are more lines than elements, the last 40 | // element of pre is repeated for each subsequent line. 41 | func NewIndentWriter(w io.Writer, pre ...[]byte) io.Writer { 42 | return &indentWriter{ 43 | w: w, 44 | pre: pre, 45 | bol: true, 46 | } 47 | } 48 | 49 | // The only errors returned are from the underlying indentWriter. 50 | func (w *indentWriter) Write(p []byte) (n int, err error) { 51 | for _, c := range p { 52 | if w.bol { 53 | var i int 54 | i, err = w.w.Write(w.pre[w.sel][w.off:]) 55 | w.off += i 56 | if err != nil { 57 | return n, err 58 | } 59 | } 60 | _, err = w.w.Write([]byte{c}) 61 | if err != nil { 62 | return n, err 63 | } 64 | n++ 65 | w.bol = c == '\n' 66 | if w.bol { 67 | w.off = 0 68 | if w.sel < len(w.pre)-1 { 69 | w.sel++ 70 | } 71 | } 72 | } 73 | return n, nil 74 | } 75 | -------------------------------------------------------------------------------- /pkg.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "go/build" 5 | "regexp" 6 | "strings" 7 | ) 8 | 9 | // Package represents a Go package. 10 | type Package struct { 11 | Dir string 12 | Root string 13 | ImportPath string 14 | Deps []string 15 | Standard bool 16 | Processed bool 17 | 18 | GoFiles []string 19 | CgoFiles []string 20 | IgnoredGoFiles []string 21 | 22 | TestGoFiles []string 23 | TestImports []string 24 | XTestGoFiles []string 25 | XTestImports []string 26 | 27 | Error struct { 28 | Err string 29 | } 30 | 31 | // --- New stuff for now 32 | Imports []string 33 | Dependencies []build.Package 34 | } 35 | 36 | // LoadPackages loads the named packages 37 | // Unlike the go tool, an empty argument list is treated as an empty list; "." 38 | // must be given explicitly if desired. 39 | // IgnoredGoFiles will be processed and their dependencies resolved recursively 40 | func LoadPackages(names ...string) (a []*Package, err error) { 41 | debugln("LoadPackages", names) 42 | if len(names) == 0 { 43 | return nil, nil 44 | } 45 | for _, i := range importPaths(names) { 46 | p, err := listPackage(i) 47 | if err != nil { 48 | return nil, err 49 | } 50 | a = append(a, p) 51 | } 52 | return a, nil 53 | } 54 | 55 | func (p *Package) allGoFiles() []string { 56 | var a []string 57 | a = append(a, p.GoFiles...) 58 | a = append(a, p.CgoFiles...) 59 | a = append(a, p.TestGoFiles...) 60 | a = append(a, p.XTestGoFiles...) 61 | a = append(a, p.IgnoredGoFiles...) 62 | return a 63 | } 64 | 65 | // matchPattern(pattern)(name) reports whether 66 | // name matches pattern. Pattern is a limited glob 67 | // pattern in which '...' means 'any string' and there 68 | // is no other special syntax. 69 | // Taken from $GOROOT/src/cmd/go/main.go. 70 | func matchPattern(pattern string) func(name string) bool { 71 | re := regexp.QuoteMeta(pattern) 72 | re = strings.Replace(re, `\.\.\.`, `.*`, -1) 73 | // Special case: foo/... matches foo too. 74 | if strings.HasSuffix(re, `/.*`) { 75 | re = re[:len(re)-len(`/.*`)] + `(/.*)?` 76 | } 77 | reg := regexp.MustCompile(`^` + re + `$`) 78 | return func(name string) bool { 79 | return reg.MatchString(name) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /vendor/golang.org/x/tools/go/vcs/http.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 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 | package vcs 6 | 7 | import ( 8 | "fmt" 9 | "io" 10 | "io/ioutil" 11 | "log" 12 | "net/http" 13 | "net/url" 14 | ) 15 | 16 | // httpClient is the default HTTP client, but a variable so it can be 17 | // changed by tests, without modifying http.DefaultClient. 18 | var httpClient = http.DefaultClient 19 | 20 | // httpGET returns the data from an HTTP GET request for the given URL. 21 | func httpGET(url string) ([]byte, error) { 22 | resp, err := httpClient.Get(url) 23 | if err != nil { 24 | return nil, err 25 | } 26 | defer resp.Body.Close() 27 | if resp.StatusCode != 200 { 28 | return nil, fmt.Errorf("%s: %s", url, resp.Status) 29 | } 30 | b, err := ioutil.ReadAll(resp.Body) 31 | if err != nil { 32 | return nil, fmt.Errorf("%s: %v", url, err) 33 | } 34 | return b, nil 35 | } 36 | 37 | // httpsOrHTTP returns the body of either the importPath's 38 | // https resource or, if unavailable, the http resource. 39 | func httpsOrHTTP(importPath string) (urlStr string, body io.ReadCloser, err error) { 40 | fetch := func(scheme string) (urlStr string, res *http.Response, err error) { 41 | u, err := url.Parse(scheme + "://" + importPath) 42 | if err != nil { 43 | return "", nil, err 44 | } 45 | u.RawQuery = "go-get=1" 46 | urlStr = u.String() 47 | if Verbose { 48 | log.Printf("Fetching %s", urlStr) 49 | } 50 | res, err = httpClient.Get(urlStr) 51 | return 52 | } 53 | closeBody := func(res *http.Response) { 54 | if res != nil { 55 | res.Body.Close() 56 | } 57 | } 58 | urlStr, res, err := fetch("https") 59 | if err != nil || res.StatusCode != 200 { 60 | if Verbose { 61 | if err != nil { 62 | log.Printf("https fetch failed.") 63 | } else { 64 | log.Printf("ignoring https fetch with status code %d", res.StatusCode) 65 | } 66 | } 67 | closeBody(res) 68 | urlStr, res, err = fetch("http") 69 | } 70 | if err != nil { 71 | closeBody(res) 72 | return "", nil, err 73 | } 74 | // Note: accepting a non-200 OK here, so people can serve a 75 | // meta import in their http 404 page. 76 | if Verbose { 77 | log.Printf("Parsing meta tags from %s (status code %d)", urlStr, res.StatusCode) 78 | } 79 | return urlStr, res.Body, nil 80 | } 81 | -------------------------------------------------------------------------------- /get.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "os" 6 | "os/exec" 7 | ) 8 | 9 | var cmdGet = &Command{ 10 | Name: "get", 11 | Args: "[-t] [packages]", 12 | Short: "download and install packages with specified dependencies", 13 | Long: ` 14 | Get downloads to GOPATH the packages named by the import paths, and installs 15 | them with the dependencies specified in their Godeps files. 16 | 17 | If any of the packages do not have Godeps files, those are installed 18 | as if by go get. 19 | 20 | If -t is given, dependencies of test files are also downloaded and installed. 21 | 22 | For more about specifying packages, see 'go help packages'. 23 | `, 24 | Run: runGet, 25 | OnlyInGOPATH: true, 26 | } 27 | 28 | var getT bool 29 | 30 | func init() { 31 | cmdGet.Flag.BoolVar(&getT, "t", false, "get test dependencies") 32 | } 33 | 34 | func runGet(cmd *Command, args []string) { 35 | if len(args) == 0 { 36 | args = []string{"."} 37 | } 38 | 39 | cmdArgs := []interface{}{"get", "-d"} 40 | if verbose { 41 | cmdArgs = append(cmdArgs, "-v") 42 | } 43 | 44 | if getT { 45 | cmdArgs = append(cmdArgs, "-t") 46 | } 47 | 48 | err := command("go", append(cmdArgs, args)...).Run() 49 | if err != nil { 50 | log.Fatalln(err) 51 | } 52 | 53 | // group import paths by Godeps location 54 | groups := make(map[string][]string) 55 | ps, err := LoadPackages(args...) 56 | if err != nil { 57 | log.Fatalln(err) 58 | } 59 | for _, pkg := range ps { 60 | if pkg.Error.Err != "" { 61 | log.Fatalln(pkg.Error.Err) 62 | } 63 | dir, _ := findInParents(pkg.Dir, "Godeps") 64 | groups[dir] = append(groups[dir], pkg.ImportPath) 65 | } 66 | for dir, packages := range groups { 67 | var c *exec.Cmd 68 | if dir == "" { 69 | c = command("go", "install", packages) 70 | } else { 71 | c = command("godep", "go", "install", packages) 72 | c.Dir = dir 73 | } 74 | if err := c.Run(); err != nil { 75 | log.Fatalln(err) 76 | } 77 | } 78 | } 79 | 80 | // command is like exec.Command, but the returned 81 | // Cmd inherits stderr from the current process, and 82 | // elements of args may be either string or []string. 83 | func command(name string, args ...interface{}) *exec.Cmd { 84 | var a []string 85 | for _, arg := range args { 86 | switch v := arg.(type) { 87 | case string: 88 | a = append(a, v) 89 | case []string: 90 | a = append(a, v...) 91 | } 92 | } 93 | c := exec.Command(name, a...) 94 | c.Stderr = os.Stderr 95 | return c 96 | } 97 | -------------------------------------------------------------------------------- /vendor/golang.org/x/tools/go/vcs/discovery.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 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 | package vcs 6 | 7 | import ( 8 | "encoding/xml" 9 | "fmt" 10 | "io" 11 | "strings" 12 | ) 13 | 14 | // charsetReader returns a reader for the given charset. Currently 15 | // it only supports UTF-8 and ASCII. Otherwise, it returns a meaningful 16 | // error which is printed by go get, so the user can find why the package 17 | // wasn't downloaded if the encoding is not supported. Note that, in 18 | // order to reduce potential errors, ASCII is treated as UTF-8 (i.e. characters 19 | // greater than 0x7f are not rejected). 20 | func charsetReader(charset string, input io.Reader) (io.Reader, error) { 21 | switch strings.ToLower(charset) { 22 | case "ascii": 23 | return input, nil 24 | default: 25 | return nil, fmt.Errorf("can't decode XML document using charset %q", charset) 26 | } 27 | } 28 | 29 | // parseMetaGoImports returns meta imports from the HTML in r. 30 | // Parsing ends at the end of the section or the beginning of the . 31 | func parseMetaGoImports(r io.Reader) (imports []metaImport, err error) { 32 | d := xml.NewDecoder(r) 33 | d.CharsetReader = charsetReader 34 | d.Strict = false 35 | var t xml.Token 36 | for { 37 | t, err = d.Token() 38 | if err != nil { 39 | if err == io.EOF || len(imports) > 0 { 40 | err = nil 41 | } 42 | return 43 | } 44 | if e, ok := t.(xml.StartElement); ok && strings.EqualFold(e.Name.Local, "body") { 45 | return 46 | } 47 | if e, ok := t.(xml.EndElement); ok && strings.EqualFold(e.Name.Local, "head") { 48 | return 49 | } 50 | e, ok := t.(xml.StartElement) 51 | if !ok || !strings.EqualFold(e.Name.Local, "meta") { 52 | continue 53 | } 54 | if attrValue(e.Attr, "name") != "go-import" { 55 | continue 56 | } 57 | if f := strings.Fields(attrValue(e.Attr, "content")); len(f) == 3 { 58 | imports = append(imports, metaImport{ 59 | Prefix: f[0], 60 | VCS: f[1], 61 | RepoRoot: f[2], 62 | }) 63 | } 64 | } 65 | } 66 | 67 | // attrValue returns the attribute value for the case-insensitive key 68 | // `name', or the empty string if nothing is found. 69 | func attrValue(attrs []xml.Attr, name string) string { 70 | for _, a := range attrs { 71 | if strings.EqualFold(a.Name.Local, name) { 72 | return a.Value 73 | } 74 | } 75 | return "" 76 | } 77 | -------------------------------------------------------------------------------- /vendor/github.com/kr/text/wrap.go: -------------------------------------------------------------------------------- 1 | package text 2 | 3 | import ( 4 | "bytes" 5 | "math" 6 | ) 7 | 8 | var ( 9 | nl = []byte{'\n'} 10 | sp = []byte{' '} 11 | ) 12 | 13 | const defaultPenalty = 1e5 14 | 15 | // Wrap wraps s into a paragraph of lines of length lim, with minimal 16 | // raggedness. 17 | func Wrap(s string, lim int) string { 18 | return string(WrapBytes([]byte(s), lim)) 19 | } 20 | 21 | // WrapBytes wraps b into a paragraph of lines of length lim, with minimal 22 | // raggedness. 23 | func WrapBytes(b []byte, lim int) []byte { 24 | words := bytes.Split(bytes.Replace(bytes.TrimSpace(b), nl, sp, -1), sp) 25 | var lines [][]byte 26 | for _, line := range WrapWords(words, 1, lim, defaultPenalty) { 27 | lines = append(lines, bytes.Join(line, sp)) 28 | } 29 | return bytes.Join(lines, nl) 30 | } 31 | 32 | // WrapWords is the low-level line-breaking algorithm, useful if you need more 33 | // control over the details of the text wrapping process. For most uses, either 34 | // Wrap or WrapBytes will be sufficient and more convenient. 35 | // 36 | // WrapWords splits a list of words into lines with minimal "raggedness", 37 | // treating each byte as one unit, accounting for spc units between adjacent 38 | // words on each line, and attempting to limit lines to lim units. Raggedness 39 | // is the total error over all lines, where error is the square of the 40 | // difference of the length of the line and lim. Too-long lines (which only 41 | // happen when a single word is longer than lim units) have pen penalty units 42 | // added to the error. 43 | func WrapWords(words [][]byte, spc, lim, pen int) [][][]byte { 44 | n := len(words) 45 | 46 | length := make([][]int, n) 47 | for i := 0; i < n; i++ { 48 | length[i] = make([]int, n) 49 | length[i][i] = len(words[i]) 50 | for j := i + 1; j < n; j++ { 51 | length[i][j] = length[i][j-1] + spc + len(words[j]) 52 | } 53 | } 54 | 55 | nbrk := make([]int, n) 56 | cost := make([]int, n) 57 | for i := range cost { 58 | cost[i] = math.MaxInt32 59 | } 60 | for i := n - 1; i >= 0; i-- { 61 | if length[i][n-1] <= lim { 62 | cost[i] = 0 63 | nbrk[i] = n 64 | } else { 65 | for j := i + 1; j < n; j++ { 66 | d := lim - length[i][j-1] 67 | c := d*d + cost[j] 68 | if length[i][j-1] > lim { 69 | c += pen // too-long lines get a worse penalty 70 | } 71 | if c < cost[i] { 72 | cost[i] = c 73 | nbrk[i] = j 74 | } 75 | } 76 | } 77 | } 78 | 79 | var lines [][][]byte 80 | i := 0 81 | for i < n { 82 | lines = append(lines, words[i:nbrk[i]]) 83 | i = nbrk[i] 84 | } 85 | return lines 86 | } 87 | -------------------------------------------------------------------------------- /license_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | // most of these were found via google searches 8 | // site:github.com FILENAME 9 | // 10 | func TestLicenseFiles(t *testing.T) { 11 | var testcases = []struct { 12 | filename string 13 | license bool 14 | legal bool 15 | }{ 16 | {"license", true, true}, 17 | {"License", true, true}, 18 | {"LICENSE.md", true, true}, 19 | {"LICENSE.rst", true, true}, 20 | {"LICENSE.txt", true, true}, 21 | {"licence", true, true}, 22 | {"LICENCE.broadcom", true, true}, 23 | {"LICENCE.md", true, true}, 24 | {"copying", true, true}, 25 | {"COPYING.txt", true, true}, 26 | {"unlicense", true, true}, 27 | {"copyright", true, true}, 28 | {"COPYRIGHT.txt", true, true}, 29 | {"copyleft", true, true}, 30 | {"COPYLEFT.txt", true, true}, 31 | {"copyleft.txt", true, true}, 32 | {"Copyleft.txt", true, true}, 33 | {"copyleft-next-0.2.1.txt", true, true}, 34 | {"legal", false, true}, 35 | {"notice", false, true}, 36 | {"NOTICE", false, true}, 37 | {"disclaimer", false, true}, 38 | {"patent", false, true}, 39 | {"patents", false, true}, 40 | {"third-party", false, true}, 41 | {"thirdparty", false, true}, 42 | {"thirdparty.txt", false, true}, 43 | {"license-ThirdParty.txt", true, true}, 44 | {"LICENSE-ThirdParty.txt", true, true}, 45 | {"THIRDPARTY.md", false, true}, 46 | {"third-party.md", false, true}, 47 | {"THIRD-PARTY.txt", false, true}, 48 | {"extensions-third-party.md", false, true}, 49 | {"ThirdParty.md", false, true}, 50 | {"third-party-licenses.md", false, true}, 51 | {"0070-01-01-third-party.md", false, true}, 52 | {"LEGAL.txt", false, true}, 53 | {"legal.txt", false, true}, 54 | {"Legal.md", false, true}, 55 | {"LEGAL.md", false, true}, 56 | {"legal.rst", false, true}, 57 | {"Legal.rtf", false, true}, 58 | {"legal.rtf", false, true}, 59 | {"PATENTS.TXT", false, true}, 60 | {"ttf-PATENTS.txt", false, true}, 61 | {"patents.txt", false, true}, 62 | {"INRIA-DISCLAIMER.txt", false, true}, 63 | 64 | {"MPL-2.0-no-copyleft-exception.txt", false, false}, 65 | } 66 | 67 | for pos, tt := range testcases { 68 | license := IsLicenseFile(tt.filename) 69 | if tt.license != license { 70 | if license { 71 | t.Errorf("%d/file %q is not marked as license", pos, tt.filename) 72 | } else { 73 | t.Errorf("%d/file %q was marked incorrectly as a license", pos, tt.filename) 74 | } 75 | } 76 | 77 | legal := IsLegalFile(tt.filename) 78 | if tt.legal != legal { 79 | if legal { 80 | t.Errorf("%d/File %q is not marked as legal file", pos, tt.filename) 81 | } else { 82 | t.Errorf("%d/File %q was marked incorrectly as a legal file", pos, tt.filename) 83 | } 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /diff_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "testing" 7 | ) 8 | 9 | var ( 10 | d1 = fmt.Sprintf(`--- Godeps/Godeps.json 11 | +++ $GOPATH 12 | @@ -1,13 +1,13 @@ 13 | { 14 | "ImportPath": "C", 15 | "GoVersion": "go1.2", 16 | "GodepVersion": "v%d", 17 | "Deps": [ 18 | { 19 | "ImportPath": "D101", 20 | - "Comment": "D202", 21 | + "Comment": "D303", 22 | "Rev": "" 23 | } 24 | ] 25 | } 26 | `, version) 27 | 28 | d2 = fmt.Sprintf(`--- Godeps/Godeps.json 29 | +++ $GOPATH 30 | @@ -1,13 +1,18 @@ 31 | { 32 | "ImportPath": "C", 33 | "GoVersion": "go1.2", 34 | "GodepVersion": "v%d", 35 | "Deps": [ 36 | { 37 | "ImportPath": "D101", 38 | "Comment": "D202", 39 | "Rev": "" 40 | + }, 41 | + { 42 | + "ImportPath": "D102", 43 | + "Comment": "D203", 44 | + "Rev": "" 45 | } 46 | ] 47 | } 48 | `, version) 49 | ) 50 | 51 | var ( 52 | dep1 = Godeps{ 53 | ImportPath: "C", 54 | GoVersion: "go1.2", 55 | Deps: []Dependency{ 56 | {ImportPath: "D101", Comment: "D202"}, 57 | }, 58 | } 59 | 60 | dep2 = Godeps{ 61 | ImportPath: "C", 62 | GoVersion: "go1.2", 63 | Deps: []Dependency{ 64 | {ImportPath: "D101", Comment: "D202"}, 65 | }, 66 | } 67 | ) 68 | 69 | func TestDiff(t *testing.T) { 70 | // Equiv Godeps, should yield an empty diff. 71 | diff, _ := diffStr(&dep1, &dep2) 72 | if diff != "" { 73 | t.Errorf("Diff is %v want ''", diff) 74 | } 75 | 76 | // Test modifications in packages make it to the diff. 77 | dep2.Deps[0].Comment = "D303" 78 | diff, _ = diffStr(&dep1, &dep2) 79 | if !diffsEqual(strings.Fields(diff), strings.Fields(d1)) { 80 | t.Errorf("Expecting diffs to be equal. Obs <%s>. Exp <%s>", diff, d1) 81 | } 82 | 83 | // Test additional packages in new Godeps 84 | dep2.Deps[0].Comment = "D202" 85 | dep2.Deps = append(dep2.Deps, Dependency{ImportPath: "D102", Comment: "D203"}) 86 | diff, _ = diffStr(&dep1, &dep2) 87 | 88 | if !diffsEqual(strings.Fields(diff), strings.Fields(d2)) { 89 | t.Errorf("Expecting diffs to be equal. Obs <%v>. Exp <%v>", diff, d2) 90 | } 91 | } 92 | 93 | // diffsEqual asserts that two slices are equivalent. 94 | func diffsEqual(a, b []string) bool { 95 | if len(a) != len(b) { 96 | return false 97 | } 98 | 99 | for i := range a { 100 | if a[i] != b[i] { 101 | return false 102 | } 103 | } 104 | return true 105 | } 106 | -------------------------------------------------------------------------------- /vendor/github.com/kr/fs/walk.go: -------------------------------------------------------------------------------- 1 | // Package fs provides filesystem-related functions. 2 | package fs 3 | 4 | import ( 5 | "os" 6 | ) 7 | 8 | // Walker provides a convenient interface for iterating over the 9 | // descendants of a filesystem path. 10 | // Successive calls to the Step method will step through each 11 | // file or directory in the tree, including the root. The files 12 | // are walked in lexical order, which makes the output deterministic 13 | // but means that for very large directories Walker can be inefficient. 14 | // Walker does not follow symbolic links. 15 | type Walker struct { 16 | fs FileSystem 17 | cur item 18 | stack []item 19 | descend bool 20 | } 21 | 22 | type item struct { 23 | path string 24 | info os.FileInfo 25 | err error 26 | } 27 | 28 | // Walk returns a new Walker rooted at root. 29 | func Walk(root string) *Walker { 30 | return WalkFS(root, new(fs)) 31 | } 32 | 33 | // WalkFS returns a new Walker rooted at root on the FileSystem fs. 34 | func WalkFS(root string, fs FileSystem) *Walker { 35 | info, err := fs.Lstat(root) 36 | return &Walker{ 37 | fs: fs, 38 | stack: []item{{root, info, err}}, 39 | } 40 | } 41 | 42 | // Step advances the Walker to the next file or directory, 43 | // which will then be available through the Path, Stat, 44 | // and Err methods. 45 | // It returns false when the walk stops at the end of the tree. 46 | func (w *Walker) Step() bool { 47 | if w.descend && w.cur.err == nil && w.cur.info.IsDir() { 48 | list, err := w.fs.ReadDir(w.cur.path) 49 | if err != nil { 50 | w.cur.err = err 51 | w.stack = append(w.stack, w.cur) 52 | } else { 53 | for i := len(list) - 1; i >= 0; i-- { 54 | path := w.fs.Join(w.cur.path, list[i].Name()) 55 | w.stack = append(w.stack, item{path, list[i], nil}) 56 | } 57 | } 58 | } 59 | 60 | if len(w.stack) == 0 { 61 | return false 62 | } 63 | i := len(w.stack) - 1 64 | w.cur = w.stack[i] 65 | w.stack = w.stack[:i] 66 | w.descend = true 67 | return true 68 | } 69 | 70 | // Path returns the path to the most recent file or directory 71 | // visited by a call to Step. It contains the argument to Walk 72 | // as a prefix; that is, if Walk is called with "dir", which is 73 | // a directory containing the file "a", Path will return "dir/a". 74 | func (w *Walker) Path() string { 75 | return w.cur.path 76 | } 77 | 78 | // Stat returns info for the most recent file or directory 79 | // visited by a call to Step. 80 | func (w *Walker) Stat() os.FileInfo { 81 | return w.cur.info 82 | } 83 | 84 | // Err returns the error, if any, for the most recent attempt 85 | // by Step to visit a file or directory. If a directory has 86 | // an error, w will not descend into that directory. 87 | func (w *Walker) Err() error { 88 | return w.cur.err 89 | } 90 | 91 | // SkipDir causes the currently visited directory to be skipped. 92 | // If w is not on a directory, SkipDir has no effect. 93 | func (w *Walker) SkipDir() { 94 | w.descend = false 95 | } 96 | -------------------------------------------------------------------------------- /vcs_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "testing" 4 | 5 | func TestGitDetermineDefaultBranch(t *testing.T) { 6 | cases := []struct { 7 | r, o string 8 | v string 9 | err bool 10 | }{ 11 | {"test", 12 | `* remote origin 13 | Fetch URL: https://gopkg.in/mgo.v2 14 | Push URL: https://gopkg.in/mgo.v2 15 | HEAD branch: v2 16 | Remote branches: 17 | master tracked 18 | v2 tracked 19 | v2-unstable tracked 20 | Local branches configured for 'git pull': 21 | master merges with remote master 22 | v2 merges with remote v2 23 | Local refs configured for 'git push': 24 | master pushes to master (up to date) 25 | v2 pushes to v2 (local out of date) 26 | `, "v2", false}, 27 | {"test", 28 | `* remote origin 29 | Fetch URL: https://gopkg.in/bluesuncorp/validator.v5 30 | Push URL: https://gopkg.in/bluesuncorp/validator.v5 31 | HEAD branch (remote HEAD is ambiguous, may be one of the following): 32 | master 33 | v5 34 | Remote branches: 35 | krhubert tracked 36 | master tracked 37 | v4 tracked 38 | v5 tracked 39 | v5-development tracked 40 | v6 tracked 41 | v6-development tracked 42 | v7 tracked 43 | v7-development tracked 44 | v8 tracked 45 | v8-development tracked 46 | Local branch configured for 'git pull': 47 | master merges with remote master 48 | Local ref configured for 'git push': 49 | master pushes to master (up to date) 50 | `, "master", false}, 51 | {"test", 52 | `* remote origin 53 | Fetch URL: https://github.com/gin-gonic/gin 54 | Push URL: https://github.com/gin-gonic/gin 55 | HEAD branch: develop 56 | Remote branches: 57 | benchmarks tracked 58 | better-bind-errors tracked 59 | develop tracked 60 | fasthttp tracked 61 | fix-binding tracked 62 | fix-tests tracked 63 | gh-pages tracked 64 | honteng-bind_test tracked 65 | master tracked 66 | new-binding-validator tracked 67 | new-catch-all tracked 68 | performance tracked 69 | routes-list tracked 70 | Local branch configured for 'git pull': 71 | develop merges with remote develop 72 | Local ref configured for 'git push': 73 | develop pushes to develop (local out of date) 74 | `, "develop", false}, 75 | {"test", "", "", true}, 76 | } 77 | 78 | for i, test := range cases { 79 | v, e := gitDetermineDefaultBranch(test.r, test.o) 80 | if v != test.v { 81 | t.Errorf("%d Unexpected value returned: %s, wanted %s", i, v, test.v) 82 | } 83 | if e != nil { 84 | t.Log("Err", e.Error()) 85 | } 86 | if test.err && e == nil { 87 | t.Errorf("%d Test should err, but didn't", i) 88 | } 89 | 90 | if !test.err && e != nil { 91 | t.Errorf("%d Test shouldn't err, but did with: %s", i, e) 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /vendor/github.com/kr/pretty/pretty.go: -------------------------------------------------------------------------------- 1 | // Package pretty provides pretty-printing for Go values. This is 2 | // useful during debugging, to avoid wrapping long output lines in 3 | // the terminal. 4 | // 5 | // It provides a function, Formatter, that can be used with any 6 | // function that accepts a format string. It also provides 7 | // convenience wrappers for functions in packages fmt and log. 8 | package pretty 9 | 10 | import ( 11 | "fmt" 12 | "io" 13 | "log" 14 | ) 15 | 16 | // Errorf is a convenience wrapper for fmt.Errorf. 17 | // 18 | // Calling Errorf(f, x, y) is equivalent to 19 | // fmt.Errorf(f, Formatter(x), Formatter(y)). 20 | func Errorf(format string, a ...interface{}) error { 21 | return fmt.Errorf(format, wrap(a, false)...) 22 | } 23 | 24 | // Fprintf is a convenience wrapper for fmt.Fprintf. 25 | // 26 | // Calling Fprintf(w, f, x, y) is equivalent to 27 | // fmt.Fprintf(w, f, Formatter(x), Formatter(y)). 28 | func Fprintf(w io.Writer, format string, a ...interface{}) (n int, error error) { 29 | return fmt.Fprintf(w, format, wrap(a, false)...) 30 | } 31 | 32 | // Log is a convenience wrapper for log.Printf. 33 | // 34 | // Calling Log(x, y) is equivalent to 35 | // log.Print(Formatter(x), Formatter(y)), but each operand is 36 | // formatted with "%# v". 37 | func Log(a ...interface{}) { 38 | log.Print(wrap(a, true)...) 39 | } 40 | 41 | // Logf is a convenience wrapper for log.Printf. 42 | // 43 | // Calling Logf(f, x, y) is equivalent to 44 | // log.Printf(f, Formatter(x), Formatter(y)). 45 | func Logf(format string, a ...interface{}) { 46 | log.Printf(format, wrap(a, false)...) 47 | } 48 | 49 | // Logln is a convenience wrapper for log.Printf. 50 | // 51 | // Calling Logln(x, y) is equivalent to 52 | // log.Println(Formatter(x), Formatter(y)), but each operand is 53 | // formatted with "%# v". 54 | func Logln(a ...interface{}) { 55 | log.Println(wrap(a, true)...) 56 | } 57 | 58 | // Print pretty-prints its operands and writes to standard output. 59 | // 60 | // Calling Print(x, y) is equivalent to 61 | // fmt.Print(Formatter(x), Formatter(y)), but each operand is 62 | // formatted with "%# v". 63 | func Print(a ...interface{}) (n int, errno error) { 64 | return fmt.Print(wrap(a, true)...) 65 | } 66 | 67 | // Printf is a convenience wrapper for fmt.Printf. 68 | // 69 | // Calling Printf(f, x, y) is equivalent to 70 | // fmt.Printf(f, Formatter(x), Formatter(y)). 71 | func Printf(format string, a ...interface{}) (n int, errno error) { 72 | return fmt.Printf(format, wrap(a, false)...) 73 | } 74 | 75 | // Println pretty-prints its operands and writes to standard output. 76 | // 77 | // Calling Print(x, y) is equivalent to 78 | // fmt.Println(Formatter(x), Formatter(y)), but each operand is 79 | // formatted with "%# v". 80 | func Println(a ...interface{}) (n int, errno error) { 81 | return fmt.Println(wrap(a, true)...) 82 | } 83 | 84 | // Sprintf is a convenience wrapper for fmt.Sprintf. 85 | // 86 | // Calling Sprintf(f, x, y) is equivalent to 87 | // fmt.Sprintf(f, Formatter(x), Formatter(y)). 88 | func Sprintf(format string, a ...interface{}) string { 89 | return fmt.Sprintf(format, wrap(a, false)...) 90 | } 91 | 92 | func wrap(a []interface{}, force bool) []interface{} { 93 | w := make([]interface{}, len(a)) 94 | for i, x := range a { 95 | w[i] = formatter{x: x, force: force} 96 | } 97 | return w 98 | } 99 | -------------------------------------------------------------------------------- /dep.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "os/exec" 7 | "sort" 8 | "strings" 9 | ) 10 | 11 | // A Dependency is a specific revision of a package. 12 | type Dependency struct { 13 | ImportPath string 14 | Comment string `json:",omitempty"` // Description of commit, if present. 15 | Rev string // VCS-specific commit ID. 16 | 17 | // used by command save & update 18 | ws string // workspace 19 | root string // import path to repo root 20 | dir string // full path to package 21 | 22 | // used by command update 23 | matched bool // selected for update by command line 24 | pkg *Package 25 | missing bool // packages is missing 26 | 27 | // used by command go 28 | vcs *VCS 29 | } 30 | 31 | func eqDeps(a, b []Dependency) bool { 32 | ok := true 33 | for _, da := range a { 34 | for _, db := range b { 35 | if da.ImportPath == db.ImportPath && da.Rev != db.Rev { 36 | ok = false 37 | } 38 | } 39 | } 40 | return ok 41 | } 42 | 43 | // containsPathPrefix returns whether any string in a 44 | // is s or a directory containing s. 45 | // For example, pattern ["a"] matches "a" and "a/b" 46 | // (but not "ab"). 47 | func containsPathPrefix(pats []string, s string) bool { 48 | for _, pat := range pats { 49 | if pat == s || strings.HasPrefix(s, pat+"/") { 50 | return true 51 | } 52 | } 53 | return false 54 | } 55 | 56 | func uniq(a []string) []string { 57 | var s string 58 | var i int 59 | if !sort.StringsAreSorted(a) { 60 | sort.Strings(a) 61 | } 62 | for _, t := range a { 63 | if t != s { 64 | a[i] = t 65 | i++ 66 | s = t 67 | } 68 | } 69 | return a[:i] 70 | } 71 | 72 | // trimGoVersion and return the major version 73 | func trimGoVersion(version string) (string, error) { 74 | if version == "devel" { 75 | return "devel", nil 76 | } 77 | if strings.HasPrefix(version, "devel+") || strings.HasPrefix(version, "devel-") { 78 | return strings.Replace(version, "devel+", "devel-", 1), nil 79 | } 80 | p := strings.Split(version, ".") 81 | if len(p) < 2 { 82 | return "", fmt.Errorf("Error determining major go version from: %q", version) 83 | } 84 | var split string 85 | switch { 86 | case strings.Contains(p[1], "beta"): 87 | split = "beta" 88 | case strings.Contains(p[1], "rc"): 89 | split = "rc" 90 | } 91 | if split != "" { 92 | p[1] = strings.Split(p[1], split)[0] 93 | } 94 | return p[0] + "." + p[1], nil 95 | } 96 | 97 | var goVersionTestOutput = "" 98 | 99 | func getGoVersion() (string, error) { 100 | // For testing purposes only 101 | if goVersionTestOutput != "" { 102 | return goVersionTestOutput, nil 103 | } 104 | 105 | // Godep might have been compiled with a different 106 | // version, so we can't just use runtime.Version here. 107 | cmd := exec.Command("go", "version") 108 | cmd.Stderr = os.Stderr 109 | out, err := cmd.Output() 110 | return string(out), err 111 | } 112 | 113 | // goVersion returns the major version string of the Go compiler 114 | // currently installed, e.g. "go1.5". 115 | func goVersion() (string, error) { 116 | out, err := getGoVersion() 117 | if err != nil { 118 | return "", err 119 | } 120 | gv := strings.Split(out, " ") 121 | if len(gv) < 4 { 122 | return "", fmt.Errorf("Error splitting output of `go version`: Expected 4 or more elements, but there are < 4: %q", out) 123 | } 124 | if gv[2] == "devel" { 125 | return trimGoVersion(gv[2] + gv[3]) 126 | } 127 | return trimGoVersion(gv[2]) 128 | } 129 | -------------------------------------------------------------------------------- /go.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | "os/exec" 8 | "path/filepath" 9 | "strings" 10 | ) 11 | 12 | var cmdGo = &Command{ 13 | Name: "go", 14 | Args: "command [arguments]", 15 | Short: "run the go tool with saved dependencies", 16 | Long: ` 17 | Go runs the go tool with a modified GOPATH giving access to 18 | dependencies saved in Godeps. 19 | 20 | Any go tool command can run this way, but "godep go get" 21 | is unnecessary and has been disabled. Instead, use 22 | "godep go install". 23 | `, 24 | Run: runGo, 25 | OnlyInGOPATH: true, 26 | } 27 | 28 | // Find the godep GOPATH for this file tree and run the go tool. 29 | func runGo(cmd *Command, args []string) { 30 | gopath := prepareGopath() 31 | if s := os.Getenv("GOPATH"); s != "" { 32 | gopath += string(os.PathListSeparator) + os.Getenv("GOPATH") 33 | } 34 | if len(args) > 0 && args[0] == "get" { 35 | log.Printf("invalid subcommand: %q", "go get") 36 | fmt.Fprintln(os.Stderr, "Use 'godep go install' instead.") 37 | fmt.Fprintln(os.Stderr, "Run 'godep help go' for usage.") 38 | os.Exit(2) 39 | } 40 | c := exec.Command("go", args...) 41 | c.Env = append(envNoGopath(), "GOPATH="+gopath) 42 | c.Stdin = os.Stdin 43 | c.Stdout = os.Stdout 44 | c.Stderr = os.Stderr 45 | err := c.Run() 46 | if err != nil { 47 | log.Fatalln("go", err) 48 | } 49 | } 50 | 51 | // prepareGopath reads dependency information from the filesystem 52 | // entry name, fetches any necessary code, and returns a gopath 53 | // causing the specified dependencies to be used. 54 | func prepareGopath() (gopath string) { 55 | dir, isDir := findGodeps() 56 | if dir == "" { 57 | log.Fatalln("No Godeps found (or in any parent directory)") 58 | } 59 | if !isDir { 60 | log.Fatalln(strings.TrimSpace(needSource)) 61 | } 62 | return filepath.Join(dir, "Godeps", "_workspace") 63 | } 64 | 65 | // findGodeps looks for a directory entry "Godeps" in the 66 | // current directory or any parent, and returns the containing 67 | // directory and whether the entry itself is a directory. 68 | // If Godeps can't be found, findGodeps returns "". 69 | // For any other error, it exits the program. 70 | func findGodeps() (dir string, isDir bool) { 71 | wd, err := os.Getwd() 72 | if err != nil { 73 | log.Fatalln(err) 74 | } 75 | return findInParents(wd, "Godeps") 76 | } 77 | 78 | // isRoot returns true iff a path is a root. 79 | // On Unix: "/". 80 | // On Windows: "C:\", "D:\", ... 81 | func isRoot(p string) bool { 82 | p = filepath.Clean(p) 83 | volume := filepath.VolumeName(p) 84 | 85 | p = strings.TrimPrefix(p, volume) 86 | p = filepath.ToSlash(p) 87 | 88 | return p == "/" 89 | } 90 | 91 | // findInParents returns the path to the directory containing name 92 | // in dir or any ancestor, and whether name itself is a directory. 93 | // If name cannot be found, findInParents returns the empty string. 94 | func findInParents(dir, name string) (container string, isDir bool) { 95 | for { 96 | fi, err := os.Stat(filepath.Join(dir, name)) 97 | if os.IsNotExist(err) && isRoot(dir) { 98 | return "", false 99 | } 100 | if os.IsNotExist(err) { 101 | dir = filepath.Dir(dir) 102 | continue 103 | } 104 | if err != nil { 105 | log.Fatalln(err) 106 | } 107 | return dir, fi.IsDir() 108 | } 109 | } 110 | 111 | func envNoGopath() (a []string) { 112 | for _, s := range os.Environ() { 113 | if !strings.HasPrefix(s, "GOPATH=") { 114 | a = append(a, s) 115 | } 116 | } 117 | return a 118 | } 119 | 120 | const needSource = ` 121 | outdated Godeps missing source code 122 | 123 | This dependency list was created with an old version of godep. 124 | 125 | To work around this, you have two options: 126 | 1. Run 'godep restore', and try again. 127 | 2. Ask the maintainer to switch to a newer version of godep, 128 | then try again with the updated package. 129 | ` 130 | -------------------------------------------------------------------------------- /vendor/github.com/kr/pretty/diff.go: -------------------------------------------------------------------------------- 1 | package pretty 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "reflect" 7 | ) 8 | 9 | type sbuf []string 10 | 11 | func (s *sbuf) Write(b []byte) (int, error) { 12 | *s = append(*s, string(b)) 13 | return len(b), nil 14 | } 15 | 16 | // Diff returns a slice where each element describes 17 | // a difference between a and b. 18 | func Diff(a, b interface{}) (desc []string) { 19 | Fdiff((*sbuf)(&desc), a, b) 20 | return desc 21 | } 22 | 23 | // Fdiff writes to w a description of the differences between a and b. 24 | func Fdiff(w io.Writer, a, b interface{}) { 25 | diffWriter{w: w}.diff(reflect.ValueOf(a), reflect.ValueOf(b)) 26 | } 27 | 28 | type diffWriter struct { 29 | w io.Writer 30 | l string // label 31 | } 32 | 33 | func (w diffWriter) printf(f string, a ...interface{}) { 34 | var l string 35 | if w.l != "" { 36 | l = w.l + ": " 37 | } 38 | fmt.Fprintf(w.w, l+f, a...) 39 | } 40 | 41 | func (w diffWriter) diff(av, bv reflect.Value) { 42 | if !av.IsValid() && bv.IsValid() { 43 | w.printf("nil != %#v", bv.Interface()) 44 | return 45 | } 46 | if av.IsValid() && !bv.IsValid() { 47 | w.printf("%#v != nil", av.Interface()) 48 | return 49 | } 50 | if !av.IsValid() && !bv.IsValid() { 51 | return 52 | } 53 | 54 | at := av.Type() 55 | bt := bv.Type() 56 | if at != bt { 57 | w.printf("%v != %v", at, bt) 58 | return 59 | } 60 | 61 | // numeric types, including bool 62 | if at.Kind() < reflect.Array { 63 | a, b := av.Interface(), bv.Interface() 64 | if a != b { 65 | w.printf("%#v != %#v", a, b) 66 | } 67 | return 68 | } 69 | 70 | switch at.Kind() { 71 | case reflect.String: 72 | a, b := av.Interface(), bv.Interface() 73 | if a != b { 74 | w.printf("%q != %q", a, b) 75 | } 76 | case reflect.Ptr: 77 | switch { 78 | case av.IsNil() && !bv.IsNil(): 79 | w.printf("nil != %v", bv.Interface()) 80 | case !av.IsNil() && bv.IsNil(): 81 | w.printf("%v != nil", av.Interface()) 82 | case !av.IsNil() && !bv.IsNil(): 83 | w.diff(av.Elem(), bv.Elem()) 84 | } 85 | case reflect.Struct: 86 | for i := 0; i < av.NumField(); i++ { 87 | w.relabel(at.Field(i).Name).diff(av.Field(i), bv.Field(i)) 88 | } 89 | case reflect.Slice: 90 | lenA := av.Len() 91 | lenB := bv.Len() 92 | if lenA != lenB { 93 | w.printf("%s[%d] != %s[%d]", av.Type(), lenA, bv.Type(), lenB) 94 | break 95 | } 96 | for i := 0; i < lenA; i++ { 97 | w.relabel(fmt.Sprintf("[%d]", i)).diff(av.Index(i), bv.Index(i)) 98 | } 99 | case reflect.Map: 100 | ak, both, bk := keyDiff(av.MapKeys(), bv.MapKeys()) 101 | for _, k := range ak { 102 | w := w.relabel(fmt.Sprintf("[%#v]", k.Interface())) 103 | w.printf("%q != (missing)", av.MapIndex(k)) 104 | } 105 | for _, k := range both { 106 | w := w.relabel(fmt.Sprintf("[%#v]", k.Interface())) 107 | w.diff(av.MapIndex(k), bv.MapIndex(k)) 108 | } 109 | for _, k := range bk { 110 | w := w.relabel(fmt.Sprintf("[%#v]", k.Interface())) 111 | w.printf("(missing) != %q", bv.MapIndex(k)) 112 | } 113 | case reflect.Interface: 114 | w.diff(reflect.ValueOf(av.Interface()), reflect.ValueOf(bv.Interface())) 115 | default: 116 | if !reflect.DeepEqual(av.Interface(), bv.Interface()) { 117 | w.printf("%# v != %# v", Formatter(av.Interface()), Formatter(bv.Interface())) 118 | } 119 | } 120 | } 121 | 122 | func (d diffWriter) relabel(name string) (d1 diffWriter) { 123 | d1 = d 124 | if d.l != "" && name[0] != '[' { 125 | d1.l += "." 126 | } 127 | d1.l += name 128 | return d1 129 | } 130 | 131 | func keyDiff(a, b []reflect.Value) (ak, both, bk []reflect.Value) { 132 | for _, av := range a { 133 | inBoth := false 134 | for _, bv := range b { 135 | if reflect.DeepEqual(av.Interface(), bv.Interface()) { 136 | inBoth = true 137 | both = append(both, av) 138 | break 139 | } 140 | } 141 | if !inBoth { 142 | ak = append(ak, av) 143 | } 144 | } 145 | for _, bv := range b { 146 | inBoth := false 147 | for _, av := range a { 148 | if reflect.DeepEqual(av.Interface(), bv.Interface()) { 149 | inBoth = true 150 | break 151 | } 152 | } 153 | if !inBoth { 154 | bk = append(bk, bv) 155 | } 156 | } 157 | return 158 | } 159 | -------------------------------------------------------------------------------- /match_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io/ioutil" 5 | "os" 6 | "path/filepath" 7 | "testing" 8 | ) 9 | 10 | func TestMatchPattern(t *testing.T) { 11 | // Test cases from $GOROOT/src/cmd/go/match_test.go. 12 | cases := []struct { 13 | pat string 14 | path string 15 | want bool 16 | }{ 17 | {"...", "foo", true}, 18 | {"net", "net", true}, 19 | {"net", "net/http", false}, 20 | {"net/http", "net", false}, 21 | {"net/http", "net/http", true}, 22 | {"net...", "netchan", true}, 23 | {"net...", "net", true}, 24 | {"net...", "net/http", true}, 25 | {"net...", "not/http", false}, 26 | {"net/...", "netchan", false}, 27 | {"net/...", "net", true}, 28 | {"net/...", "net/http", true}, 29 | {"net/...", "not/http", false}, 30 | } 31 | for _, test := range cases { 32 | ok := matchPattern(test.pat)(test.path) 33 | if ok != test.want { 34 | t.Errorf("matchPackages(%q)(%q) = %v want %v", test.pat, test.path, ok, test.want) 35 | } 36 | } 37 | } 38 | 39 | func TestSubPath(t *testing.T) { 40 | cases := []struct { 41 | sub string 42 | dir string 43 | want bool 44 | }{ 45 | //Basic 46 | {`/Users/emuller/go/src/github.com/tools/godep`, `/Users/emuller/go`, true}, 47 | //Case insensitive filesystem used in dir 48 | {`/Users/emuller/go/src/github.com/tools/godep`, `/Users/Emuller/go`, true}, 49 | {`/Users/emuller/go/Src/github.com/tools/godep`, `/Users/Emuller/go`, true}, 50 | //spaces 51 | {`/Users/e muller/go/Src/github.com/tools/godep`, `/Users/E muller/go`, true}, 52 | // () 53 | {`/Users/e muller/(Personal)/go/Src/github.com/tools/godep`, `/Users/E muller/(Personal)/go`, true}, 54 | //Not even close, but same length 55 | {`/foo`, `/bar`, false}, 56 | // Same, so not sub path (same path) 57 | {`/foo`, `/foo`, false}, 58 | // Windows with different cases 59 | {`c:\foo\bar`, `C:\foo`, true}, 60 | } 61 | 62 | for _, test := range cases { 63 | ok := subPath(test.sub, test.dir) 64 | if ok != test.want { 65 | t.Errorf("subdir(%s,%s) = %v want %v", test.sub, test.dir, ok, test.want) 66 | } 67 | } 68 | } 69 | 70 | func TestIsSameOrNewer(t *testing.T) { 71 | cases := []struct { 72 | base string 73 | check string 74 | want bool 75 | }{ 76 | {`go1.6`, `go1.6`, true}, 77 | {`go1.5`, `go1.6`, true}, 78 | {`go1.7`, `go1.6`, false}, 79 | {`go1.6`, `devel-8f48efb`, true}, // devel versions are always never 80 | } 81 | 82 | for _, test := range cases { 83 | ok := isSameOrNewer(test.base, test.check) 84 | if ok != test.want { 85 | t.Errorf("isSameOrNewer(%s,%s) = %v want %v", test.base, test.check, ok, test.want) 86 | } 87 | } 88 | } 89 | 90 | func TestDetermineVersion(t *testing.T) { 91 | cases := []struct { 92 | v string 93 | go15ve string 94 | vendorDir []string 95 | want bool 96 | }{ 97 | {"go1.5", "", nil, false}, 98 | {"go1.5", "1", nil, true}, 99 | {"go1.5", "1", []string{"Godeps", "_workspace"}, false}, 100 | {"go1.5", "0", nil, false}, 101 | {"go1.6", "", nil, true}, 102 | {"go1.6", "1", nil, true}, 103 | {"go1.6", "1", []string{"Godeps", "_workspace"}, false}, 104 | {"go1.6", "0", nil, false}, 105 | {"devel", "", nil, true}, 106 | {"devel-12345", "", nil, true}, 107 | {"devel", "1", nil, true}, 108 | {"devel-12345", "1", nil, true}, 109 | {"devel", "1", []string{"Godeps", "_workspace"}, false}, 110 | {"devel-12345", "1", []string{"Godeps", "_workspace"}, false}, 111 | {"devel", "0", nil, true}, 112 | {"devel-12345", "0", nil, true}, 113 | } 114 | 115 | wd, err := os.Getwd() 116 | if err != nil { 117 | t.Fatal(err) 118 | } 119 | defer func() { 120 | os.Chdir(wd) 121 | }() 122 | 123 | ove := os.Getenv("GO15VENDOREXPERIMENT") 124 | defer func() { 125 | os.Setenv("GO15VENDOREXPERIMENT", ove) 126 | }() 127 | 128 | for i, test := range cases { 129 | os.Setenv("GO15VENDOREXPERIMENT", test.go15ve) 130 | tdir, err := ioutil.TempDir("", "godeptest") 131 | if err != nil { 132 | t.Fatal(err) 133 | } 134 | defer os.RemoveAll(tdir) 135 | os.Chdir(tdir) 136 | 137 | if len(test.vendorDir) > 0 { 138 | md := tdir 139 | for _, vd := range test.vendorDir { 140 | md = filepath.Join(md, vd) 141 | if err := os.Mkdir(md, os.ModePerm); err != nil { 142 | t.Fatal(err) 143 | } 144 | } 145 | } 146 | 147 | if e := determineVendor(test.v); e != test.want { 148 | t.Errorf("%d GO15VENDOREXPERIMENT=%s determineVendor(%s) == %t, but wanted %t\n", i, test.go15ve, test.v, e, test.want) 149 | } 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /rewrite.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "log" 6 | "os" 7 | "path/filepath" 8 | "strconv" 9 | "strings" 10 | 11 | "go/ast" 12 | "go/parser" 13 | "go/printer" 14 | "go/token" 15 | 16 | "github.com/kr/fs" 17 | ) 18 | 19 | // rewrite visits the go files in pkgs, plus all go files 20 | // in the directory tree Godeps, rewriting import statements 21 | // according to the rules for func qualify. 22 | func rewrite(pkgs []*Package, qual string, paths []string) error { 23 | for _, path := range pkgFiles(pkgs) { 24 | debugln("rewrite", path) 25 | err := rewriteTree(path, qual, paths) 26 | if err != nil { 27 | return err 28 | } 29 | } 30 | return rewriteTree("Godeps", qual, paths) 31 | } 32 | 33 | // pkgFiles returns the full filesystem path to all go files in pkgs. 34 | func pkgFiles(pkgs []*Package) []string { 35 | var a []string 36 | for _, pkg := range pkgs { 37 | for _, s := range pkg.allGoFiles() { 38 | a = append(a, filepath.Join(pkg.Dir, s)) 39 | } 40 | } 41 | return a 42 | } 43 | 44 | // rewriteTree recursively visits the go files in path, rewriting 45 | // import statements according to the rules for func qualify. 46 | // This function ignores the 'testdata' directory. 47 | func rewriteTree(path, qual string, paths []string) error { 48 | w := fs.Walk(path) 49 | for w.Step() { 50 | if w.Err() != nil { 51 | log.Println("rewrite:", w.Err()) 52 | continue 53 | } 54 | s := w.Stat() 55 | if s.IsDir() && s.Name() == "testdata" { 56 | w.SkipDir() 57 | continue 58 | } 59 | if strings.HasSuffix(w.Path(), ".go") { 60 | err := rewriteGoFile(w.Path(), qual, paths) 61 | if err != nil { 62 | return err 63 | } 64 | } 65 | } 66 | return nil 67 | } 68 | 69 | // rewriteGoFile rewrites import statements in the named file 70 | // according to the rules for func qualify. 71 | func rewriteGoFile(name, qual string, paths []string) error { 72 | debugln("rewriteGoFile", name, ",", qual, ",", paths) 73 | printerConfig := &printer.Config{Mode: printer.TabIndent | printer.UseSpaces, Tabwidth: 8} 74 | fset := token.NewFileSet() 75 | f, err := parser.ParseFile(fset, name, nil, parser.ParseComments) 76 | if err != nil { 77 | return err 78 | } 79 | 80 | var changed bool 81 | for _, s := range f.Imports { 82 | name, err := strconv.Unquote(s.Path.Value) 83 | if err != nil { 84 | return err // can't happen 85 | } 86 | q := qualify(unqualify(name), qual, paths) 87 | if q != name { 88 | s.Path.Value = strconv.Quote(q) 89 | changed = true 90 | } 91 | } 92 | if !changed { 93 | return nil 94 | } 95 | var buffer bytes.Buffer 96 | if err = printerConfig.Fprint(&buffer, fset, f); err != nil { 97 | return err 98 | } 99 | fset = token.NewFileSet() 100 | f, err = parser.ParseFile(fset, name, &buffer, parser.ParseComments) 101 | if err != nil { 102 | return err 103 | } 104 | ast.SortImports(fset, f) 105 | tpath := name + ".temp" 106 | t, err := os.Create(tpath) 107 | if err != nil { 108 | return err 109 | } 110 | if err = printerConfig.Fprint(t, fset, f); err != nil { 111 | return err 112 | } 113 | if err = t.Close(); err != nil { 114 | return err 115 | } 116 | // This is required before the rename on windows. 117 | if err = os.Remove(name); err != nil { 118 | return err 119 | } 120 | return os.Rename(tpath, name) 121 | } 122 | 123 | func defaultSep(experiment bool) string { 124 | if experiment { 125 | return "/vendor/" 126 | } 127 | return "/Godeps/_workspace/src/" 128 | } 129 | 130 | func relativeVendorTarget(experiment bool) string { 131 | full := defaultSep(experiment) 132 | if full[0] == '/' { 133 | full = full[1:] 134 | } 135 | return filepath.FromSlash(full) 136 | } 137 | 138 | // unqualify returns the part of importPath after the last 139 | // occurrence of the signature path elements 140 | // (Godeps/_workspace/src) that always precede imported 141 | // packages in rewritten import paths. 142 | // 143 | // For example, 144 | // unqualify(C) = C 145 | // unqualify(D/Godeps/_workspace/src/C) = C 146 | func unqualify(importPath string) string { 147 | if i := strings.LastIndex(importPath, sep); i != -1 { 148 | importPath = importPath[i+len(sep):] 149 | } 150 | return importPath 151 | } 152 | 153 | // qualify qualifies importPath with its corresponding import 154 | // path in the Godeps src copy of package pkg. If importPath 155 | // is a directory lexically contained in a path in paths, 156 | // it will be qualified with package pkg; otherwise, it will 157 | // be returned unchanged. 158 | // 159 | // For example, given paths {D, T} and pkg C, 160 | // importPath returns 161 | // C C 162 | // fmt fmt 163 | // D C/Godeps/_workspace/src/D 164 | // D/P C/Godeps/_workspace/src/D/P 165 | // T C/Godeps/_workspace/src/T 166 | func qualify(importPath, pkg string, paths []string) string { 167 | if containsPathPrefix(paths, importPath) { 168 | return pkg + sep + importPath 169 | } 170 | return importPath 171 | } 172 | -------------------------------------------------------------------------------- /godepfile.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io" 7 | "log" 8 | "os" 9 | "path/filepath" 10 | ) 11 | 12 | var ( 13 | godepsFile = filepath.Join("Godeps", "Godeps.json") 14 | oldGodepsFile = filepath.Join("Godeps") 15 | ) 16 | 17 | // Godeps describes what a package needs to be rebuilt reproducibly. 18 | // It's the same information stored in file Godeps. 19 | type Godeps struct { 20 | ImportPath string 21 | GoVersion string 22 | GodepVersion string 23 | Packages []string `json:",omitempty"` // Arguments to save, if any. 24 | Deps []Dependency 25 | isOldFile bool 26 | } 27 | 28 | func loadGodepsFile(path string) (Godeps, error) { 29 | var g Godeps 30 | f, err := os.Open(path) 31 | if err != nil { 32 | return g, err 33 | } 34 | defer f.Close() 35 | err = json.NewDecoder(f).Decode(&g) 36 | if err != nil { 37 | err = fmt.Errorf("Unable to parse %s: %s", path, err.Error()) 38 | } 39 | return g, err 40 | } 41 | 42 | func loadDefaultGodepsFile() (Godeps, error) { 43 | var g Godeps 44 | var err error 45 | g, err = loadGodepsFile(godepsFile) 46 | if err != nil { 47 | if os.IsNotExist(err) { 48 | var err1 error 49 | g, err1 = loadGodepsFile(oldGodepsFile) 50 | if err1 != nil { 51 | if os.IsNotExist(err1) { 52 | return g, err 53 | } 54 | return g, err1 55 | } 56 | g.isOldFile = true 57 | return g, nil 58 | } 59 | } 60 | return g, err 61 | } 62 | 63 | // pkgs is the list of packages to read dependencies for 64 | func (g *Godeps) fill(pkgs []*Package, destImportPath string) error { 65 | debugln("fill", destImportPath) 66 | ppln(pkgs) 67 | var err1 error 68 | var path, testImports []string 69 | dipp := []string{destImportPath} 70 | for _, p := range pkgs { 71 | if p.Standard { 72 | log.Println("ignoring stdlib package:", p.ImportPath) 73 | continue 74 | } 75 | if p.Error.Err != "" { 76 | log.Println(p.Error.Err) 77 | err1 = errorLoadingPackages 78 | continue 79 | } 80 | path = append(path, p.ImportPath) 81 | path = append(path, p.Deps...) 82 | testImports = append(testImports, p.TestImports...) 83 | testImports = append(testImports, p.XTestImports...) 84 | } 85 | ps, err := LoadPackages(testImports...) 86 | if err != nil { 87 | return err 88 | } 89 | for _, p := range ps { 90 | if p.Standard { 91 | continue 92 | } 93 | if p.Error.Err != "" { 94 | log.Println(p.Error.Err) 95 | err1 = errorLoadingPackages 96 | continue 97 | } 98 | path = append(path, p.ImportPath) 99 | path = append(path, p.Deps...) 100 | } 101 | debugln("path", path) 102 | for i, p := range path { 103 | path[i] = unqualify(p) 104 | } 105 | path = uniq(path) 106 | debugln("uniq, unqualify'd path", path) 107 | ps, err = LoadPackages(path...) 108 | if err != nil { 109 | return err 110 | } 111 | for _, pkg := range ps { 112 | if pkg.Error.Err != "" { 113 | log.Println(pkg.Error.Err) 114 | err1 = errorLoadingDeps 115 | continue 116 | } 117 | if pkg.Standard || containsPathPrefix(dipp, pkg.ImportPath) { 118 | debugln("standard or dest skipping", pkg.ImportPath) 119 | continue 120 | } 121 | vcs, reporoot, err := VCSFromDir(pkg.Dir, filepath.Join(pkg.Root, "src")) 122 | if err != nil { 123 | log.Println(err) 124 | err1 = errorLoadingDeps 125 | continue 126 | } 127 | id, err := vcs.identify(pkg.Dir) 128 | if err != nil { 129 | log.Println(err) 130 | err1 = errorLoadingDeps 131 | continue 132 | } 133 | if vcs.isDirty(pkg.Dir, id) { 134 | log.Println("dirty working tree (please commit changes):", pkg.Dir) 135 | err1 = errorLoadingDeps 136 | continue 137 | } 138 | comment := vcs.describe(pkg.Dir, id) 139 | g.Deps = append(g.Deps, Dependency{ 140 | ImportPath: pkg.ImportPath, 141 | Rev: id, 142 | Comment: comment, 143 | dir: pkg.Dir, 144 | ws: pkg.Root, 145 | root: filepath.ToSlash(reporoot), 146 | vcs: vcs, 147 | }) 148 | } 149 | return err1 150 | } 151 | 152 | func (g *Godeps) copy() *Godeps { 153 | h := *g 154 | h.Deps = make([]Dependency, len(g.Deps)) 155 | copy(h.Deps, g.Deps) 156 | return &h 157 | } 158 | 159 | func (g *Godeps) file() string { 160 | if g.isOldFile { 161 | return oldGodepsFile 162 | } 163 | return godepsFile 164 | } 165 | 166 | func (g *Godeps) save() (int64, error) { 167 | f, err := os.Create(g.file()) 168 | if err != nil { 169 | return 0, err 170 | } 171 | defer f.Close() 172 | return g.writeTo(f) 173 | } 174 | 175 | func (g *Godeps) writeTo(w io.Writer) (int64, error) { 176 | g.GodepVersion = fmt.Sprintf("v%d", version) // godep always writes its current version. 177 | b, err := json.MarshalIndent(g, "", "\t") 178 | if err != nil { 179 | return 0, err 180 | } 181 | n, err := w.Write(append(b, '\n')) 182 | return int64(n), err 183 | } 184 | 185 | func (g *Godeps) addOrUpdateDeps(deps []Dependency) { 186 | var missing []Dependency 187 | for _, d := range deps { 188 | var found bool 189 | for i := range g.Deps { 190 | if g.Deps[i].ImportPath == d.ImportPath { 191 | g.Deps[i] = d 192 | found = true 193 | break 194 | } 195 | } 196 | if !found { 197 | missing = append(missing, d) 198 | } 199 | } 200 | g.Deps = append(g.Deps, missing...) 201 | } 202 | 203 | func (g *Godeps) removeDeps(deps []Dependency) { 204 | var f []Dependency 205 | for i := range g.Deps { 206 | var found bool 207 | for _, d := range deps { 208 | if g.Deps[i].ImportPath == d.ImportPath { 209 | found = true 210 | break 211 | } 212 | } 213 | if !found { 214 | f = append(f, g.Deps[i]) 215 | } 216 | } 217 | g.Deps = f 218 | } 219 | -------------------------------------------------------------------------------- /restore.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "go/build" 6 | "log" 7 | "os" 8 | "path/filepath" 9 | 10 | "golang.org/x/tools/go/vcs" 11 | ) 12 | 13 | var cmdRestore = &Command{ 14 | Name: "restore", 15 | Short: "check out listed dependency versions in GOPATH", 16 | Long: ` 17 | Restore checks out the Godeps-specified version of each package in GOPATH. 18 | 19 | NOTE: restore leaves git repositories in a detached state. go1.6+ no longer 20 | checks out the master branch when doing a "go get", see: 21 | https://github.com/golang/go/commit/42206598671a44111c8f726ad33dc7b265bdf669. 22 | 23 | `, 24 | Run: runRestore, 25 | OnlyInGOPATH: true, 26 | } 27 | 28 | // Three phases: 29 | // 1. Download all deps 30 | // 2. Restore all deps (checkout the recorded rev) 31 | // 3. Attempt to load all deps as a simple consistency check 32 | func runRestore(cmd *Command, args []string) { 33 | if len(build.Default.GOPATH) == 0 { 34 | log.Println("Error restore requires GOPATH but it is empty.") 35 | os.Exit(1) 36 | } 37 | 38 | var hadError bool 39 | checkErr := func(s string) { 40 | if hadError { 41 | log.Println(s) 42 | os.Exit(1) 43 | } 44 | } 45 | 46 | g, err := loadDefaultGodepsFile() 47 | if err != nil { 48 | log.Fatalln(err) 49 | } 50 | for i, dep := range g.Deps { 51 | verboseln("Downloading dependency (if needed):", dep.ImportPath) 52 | err := download(&dep) 53 | if err != nil { 54 | log.Printf("error downloading dep (%s): %s\n", dep.ImportPath, err) 55 | hadError = true 56 | } 57 | g.Deps[i] = dep 58 | } 59 | checkErr("Error downloading some deps. Aborting restore and check.") 60 | for _, dep := range g.Deps { 61 | verboseln("Restoring dependency (if needed):", dep.ImportPath) 62 | err := restore(dep) 63 | if err != nil { 64 | log.Printf("error restoring dep (%s): %s\n", dep.ImportPath, err) 65 | hadError = true 66 | } 67 | } 68 | checkErr("Error restoring some deps. Aborting check.") 69 | for _, dep := range g.Deps { 70 | verboseln("Checking dependency:", dep.ImportPath) 71 | _, err := LoadPackages(dep.ImportPath) 72 | if err != nil { 73 | log.Printf("Dep (%s) restored, but was unable to load it with error:\n\t%s\n", dep.ImportPath, err) 74 | if me, ok := err.(errorMissingDep); ok { 75 | log.Println("\tThis may be because the dependencies were saved with an older version of godep (< v33).") 76 | log.Printf("\tTry `go get %s`. Then `godep save` to update deps.\n", me.i) 77 | } 78 | hadError = true 79 | } 80 | } 81 | checkErr("Error checking some deps.") 82 | } 83 | 84 | var downloaded = make(map[string]bool) 85 | 86 | // download the given dependency. 87 | // 2 Passes: 1) go get -d , 2) git pull (if necessary) 88 | func download(dep *Dependency) error { 89 | 90 | rr, err := vcs.RepoRootForImportPath(dep.ImportPath, debug) 91 | if err != nil { 92 | debugln("Error determining repo root for", dep.ImportPath) 93 | return err 94 | } 95 | ppln("rr", rr) 96 | 97 | dep.vcs = cmd[rr.VCS] 98 | 99 | // try to find an existing directory in the GOPATHs 100 | for _, gp := range filepath.SplitList(build.Default.GOPATH) { 101 | t := filepath.Join(gp, "src", rr.Root) 102 | fi, err := os.Stat(t) 103 | if err != nil { 104 | continue 105 | } 106 | if fi.IsDir() { 107 | dep.root = t 108 | break 109 | } 110 | } 111 | 112 | // If none found, just pick the first GOPATH entry (AFAICT that's what go get does) 113 | if dep.root == "" { 114 | dep.root = filepath.Join(filepath.SplitList(build.Default.GOPATH)[0], "src", rr.Root) 115 | } 116 | ppln("dep", dep) 117 | 118 | if downloaded[rr.Repo] { 119 | verboseln("Skipping already downloaded repo", rr.Repo) 120 | return nil 121 | } 122 | 123 | fi, err := os.Stat(dep.root) 124 | if err != nil { 125 | if os.IsNotExist(err) { 126 | if err := os.MkdirAll(filepath.Dir(dep.root), os.ModePerm); err != nil { 127 | debugln("Error creating base dir of", dep.root) 128 | return err 129 | } 130 | err := rr.VCS.CreateAtRev(dep.root, rr.Repo, dep.Rev) 131 | debugln("CreatedAtRev", dep.root, rr.Repo, dep.Rev) 132 | if err != nil { 133 | debugln("CreateAtRev error", err) 134 | return err 135 | } 136 | downloaded[rr.Repo] = true 137 | return nil 138 | } 139 | debugln("Error checking repo root for", dep.ImportPath, "at", dep.root, ":", err) 140 | return err 141 | } 142 | 143 | if !fi.IsDir() { 144 | return errors.New("repo root src dir exists, but isn't a directory for " + dep.ImportPath + " at " + dep.root) 145 | } 146 | 147 | if !dep.vcs.exists(dep.root, dep.Rev) { 148 | debugln("Updating existing", dep.root) 149 | if dep.vcs == vcsGit { 150 | detached, err := gitDetached(dep.root) 151 | if err != nil { 152 | return err 153 | } 154 | if detached { 155 | db, err := gitDefaultBranch(dep.root) 156 | if err != nil { 157 | return err 158 | } 159 | if err := gitCheckout(dep.root, db); err != nil { 160 | return err 161 | } 162 | } 163 | } 164 | 165 | dep.vcs.vcs.Download(dep.root) 166 | downloaded[rr.Repo] = true 167 | } 168 | 169 | debugln("Nothing to download") 170 | return nil 171 | } 172 | 173 | var restored = make(map[string]string) // dep.root -> dep.Rev 174 | 175 | // restore checks out the given revision. 176 | func restore(dep Dependency) error { 177 | rev, ok := restored[dep.root] 178 | debugln(rev) 179 | debugln(ok) 180 | debugln(dep.root) 181 | if ok { 182 | if rev != dep.Rev { 183 | return errors.New("Wanted to restore rev " + dep.Rev + ", already restored rev " + rev + " for another package in the repo") 184 | } 185 | verboseln("Skipping already restored repo") 186 | return nil 187 | } 188 | 189 | debugln("Restoring:", dep.ImportPath, dep.Rev) 190 | err := dep.vcs.RevSync(dep.root, dep.Rev) 191 | if err == nil { 192 | restored[dep.root] = dep.Rev 193 | } 194 | return err 195 | } 196 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "go/build" 7 | "io" 8 | "log" 9 | "os" 10 | "path/filepath" 11 | "runtime/pprof" 12 | "strings" 13 | "text/template" 14 | ) 15 | 16 | var ( 17 | cpuprofile string 18 | verbose bool // Verbose flag for commands that support it 19 | debug bool // Debug flag for commands that support it 20 | majorGoVersion string 21 | VendorExperiment bool 22 | sep string 23 | ) 24 | 25 | // Command is an implementation of a godep command 26 | // like godep save or godep go. 27 | type Command struct { 28 | // Run runs the command. 29 | // The args are the arguments after the command name. 30 | Run func(cmd *Command, args []string) 31 | 32 | // Name of the command 33 | Name string 34 | 35 | // Args the command would expect 36 | Args string 37 | 38 | // Short is the short description shown in the 'godep help' output. 39 | Short string 40 | 41 | // Long is the long message shown in the 42 | // 'godep help ' output. 43 | Long string 44 | 45 | // Flag is a set of flags specific to this command. 46 | Flag flag.FlagSet 47 | 48 | // OnlyInGOPATH limits this command to being run only while inside of a GOPATH 49 | OnlyInGOPATH bool 50 | } 51 | 52 | // UsageExit prints usage information and exits. 53 | func (c *Command) UsageExit() { 54 | fmt.Fprintf(os.Stderr, "Args: godep %s [-v] [-d] %s\n\n", c.Name, c.Args) 55 | fmt.Fprintf(os.Stderr, "Run 'godep help %s' for help.\n", c.Name) 56 | os.Exit(2) 57 | } 58 | 59 | // Commands lists the available commands and help topics. 60 | // The order here is the order in which they are printed 61 | // by 'godep help'. 62 | var commands = []*Command{ 63 | cmdSave, 64 | cmdGo, 65 | cmdGet, 66 | cmdPath, 67 | cmdRestore, 68 | cmdUpdate, 69 | cmdDiff, 70 | cmdVersion, 71 | } 72 | 73 | // VendorExperiment is the Go 1.5 vendor directory experiment flag, see 74 | // https://github.com/golang/go/commit/183cc0cd41f06f83cb7a2490a499e3f9101befff 75 | // Honor the env var unless the project already has an old school godep workspace 76 | func determineVendor(v string) bool { 77 | go15ve := os.Getenv("GO15VENDOREXPERIMENT") 78 | var ev bool 79 | switch v { 80 | case "go1", "go1.1", "go1.2", "go1.3", "go1.4": 81 | ev = false 82 | case "go1.5": 83 | ev = go15ve == "1" 84 | case "go1.6": 85 | ev = go15ve != "0" 86 | default: //go1.7+, devel* 87 | ev = true 88 | } 89 | 90 | ws := filepath.Join("Godeps", "_workspace") 91 | s, err := os.Stat(ws) 92 | if err == nil && s.IsDir() { 93 | log.Printf("WARNING: Godep workspaces (./Godeps/_workspace) are deprecated and support for them will be removed when go1.8 is released.") 94 | if ev { 95 | log.Printf("WARNING: Go version (%s) & $GO15VENDOREXPERIMENT=%s wants to enable the vendor experiment, but disabling because a Godep workspace (%s) exists\n", v, go15ve, ws) 96 | } 97 | return false 98 | } 99 | 100 | return ev 101 | } 102 | 103 | func main() { 104 | log.SetFlags(0) 105 | log.SetPrefix("godep: ") 106 | log.SetOutput(os.Stderr) 107 | 108 | flag.Usage = usageExit 109 | flag.Parse() 110 | args := flag.Args() 111 | if len(args) < 1 { 112 | usageExit() 113 | } 114 | 115 | if args[0] == "help" { 116 | help(args[1:]) 117 | return 118 | } 119 | 120 | var err error 121 | majorGoVersion, err = goVersion() 122 | if err != nil { 123 | log.Fatal(err) 124 | } 125 | 126 | for _, cmd := range commands { 127 | if cmd.Name == args[0] { 128 | if cmd.OnlyInGOPATH { 129 | checkInGOPATH() 130 | } 131 | 132 | VendorExperiment = determineVendor(majorGoVersion) 133 | // sep is the signature set of path elements that 134 | // precede the original path of an imported package. 135 | sep = defaultSep(VendorExperiment) 136 | 137 | cmd.Flag.BoolVar(&verbose, "v", false, "enable verbose output") 138 | cmd.Flag.BoolVar(&debug, "d", false, "enable debug output") 139 | cmd.Flag.StringVar(&cpuprofile, "cpuprofile", "", "Write cpu profile to this file") 140 | cmd.Flag.Usage = func() { cmd.UsageExit() } 141 | cmd.Flag.Parse(args[1:]) 142 | 143 | debugln("versionString()", versionString()) 144 | debugln("majorGoVersion", majorGoVersion) 145 | debugln("VendorExperiment", VendorExperiment) 146 | debugln("sep", sep) 147 | 148 | if cpuprofile != "" { 149 | f, err := os.Create(cpuprofile) 150 | if err != nil { 151 | log.Fatal(err) 152 | } 153 | pprof.StartCPUProfile(f) 154 | defer pprof.StopCPUProfile() 155 | } 156 | cmd.Run(cmd, cmd.Flag.Args()) 157 | return 158 | } 159 | } 160 | 161 | fmt.Fprintf(os.Stderr, "godep: unknown command %q\n", args[0]) 162 | fmt.Fprintf(os.Stderr, "Run 'godep help' for usage.\n") 163 | os.Exit(2) 164 | } 165 | 166 | func subPath(sub, path string) bool { 167 | ls := strings.ToLower(sub) 168 | lp := strings.ToLower(path) 169 | if ls == lp { 170 | return false 171 | } 172 | return strings.HasPrefix(ls, lp) 173 | } 174 | 175 | func checkInGOPATH() { 176 | pwd, err := os.Getwd() 177 | if err != nil { 178 | log.Fatal("Unable to determine current working directory", err) 179 | } 180 | dirs := build.Default.SrcDirs() 181 | for _, p := range dirs { 182 | if ok := subPath(pwd, p); ok { 183 | return 184 | } 185 | } 186 | 187 | log.Println("[WARNING]: godep should only be used inside a valid go package directory and") 188 | log.Println("[WARNING]: may not function correctly. You are probably outside of your $GOPATH.") 189 | log.Printf("[WARNING]:\tCurrent Directory: %s\n", pwd) 190 | log.Printf("[WARNING]:\t$GOPATH: %s\n", os.Getenv("GOPATH")) 191 | } 192 | 193 | var usageTemplate = ` 194 | Godep is a tool for managing Go package dependencies. 195 | 196 | Usage: 197 | 198 | godep command [arguments] 199 | 200 | The commands are: 201 | {{range .}} 202 | {{.Name | printf "%-8s"}} {{.Short}}{{end}} 203 | 204 | Use "godep help [command]" for more information about a command. 205 | ` 206 | 207 | var helpTemplate = ` 208 | Args: godep {{.Name}} [-v] [-d] {{.Args}} 209 | 210 | {{.Long | trim}} 211 | 212 | If -v is given, verbose output is enabled. 213 | 214 | If -d is given, debug output is enabled (you probably don't want this, see -v). 215 | 216 | ` 217 | 218 | func help(args []string) { 219 | if len(args) == 0 { 220 | printUsage(os.Stdout) 221 | return 222 | } 223 | if len(args) != 1 { 224 | fmt.Fprintf(os.Stderr, "usage: godep help command\n\n") 225 | fmt.Fprintf(os.Stderr, "Too many arguments given.\n") 226 | os.Exit(2) 227 | } 228 | for _, cmd := range commands { 229 | if cmd.Name == args[0] { 230 | tmpl(os.Stdout, helpTemplate, cmd) 231 | return 232 | } 233 | } 234 | } 235 | 236 | func usageExit() { 237 | printUsage(os.Stderr) 238 | os.Exit(2) 239 | } 240 | 241 | func printUsage(w io.Writer) { 242 | tmpl(w, usageTemplate, commands) 243 | } 244 | 245 | // tmpl executes the given template text on data, writing the result to w. 246 | func tmpl(w io.Writer, text string, data interface{}) { 247 | t := template.New("top") 248 | t.Funcs(template.FuncMap{ 249 | "trim": strings.TrimSpace, 250 | }) 251 | template.Must(t.Parse(strings.TrimSpace(text) + "\n\n")) 252 | if err := t.Execute(w, data); err != nil { 253 | panic(err) 254 | } 255 | } 256 | -------------------------------------------------------------------------------- /update.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "go/parser" 5 | "go/token" 6 | "log" 7 | "os" 8 | "path" 9 | "path/filepath" 10 | "strconv" 11 | "strings" 12 | ) 13 | 14 | var cmdUpdate = &Command{ 15 | Name: "update", 16 | Args: "[-goversion] [packages]", 17 | Short: "update selected packages or the go version", 18 | Long: ` 19 | Update changes the named dependency packages to use the 20 | revision of each currently installed in GOPATH. New code will 21 | be copied into the Godeps workspace or vendor folder and the 22 | new revision will be written to the manifest. 23 | 24 | If -goversion is specified, update the recorded go version. 25 | 26 | For more about specifying packages, see 'go help packages'. 27 | `, 28 | Run: runUpdate, 29 | OnlyInGOPATH: true, 30 | } 31 | 32 | var ( 33 | updateGoVer bool 34 | ) 35 | 36 | func init() { 37 | cmdUpdate.Flag.BoolVar(&saveT, "t", false, "save test files during update") 38 | cmdUpdate.Flag.BoolVar(&updateGoVer, "goversion", false, "update the recorded go version") 39 | } 40 | 41 | func runUpdate(cmd *Command, args []string) { 42 | if updateGoVer { 43 | err := updateGoVersion() 44 | if err != nil { 45 | log.Fatalln(err) 46 | } 47 | } 48 | if len(args) > 0 { 49 | err := update(args) 50 | if err != nil { 51 | log.Fatalln(err) 52 | } 53 | } 54 | } 55 | 56 | func updateGoVersion() error { 57 | gold, err := loadDefaultGodepsFile() 58 | if err != nil { 59 | if !os.IsNotExist(err) { 60 | return err 61 | } 62 | } 63 | cv, err := goVersion() 64 | if err != nil { 65 | return err 66 | } 67 | 68 | gv := gold.GoVersion 69 | gold.GoVersion = cv 70 | _, err = gold.save() 71 | if err != nil { 72 | return err 73 | } 74 | 75 | if gv != cv { 76 | log.Println("Updated major go version to", cv) 77 | } 78 | return nil 79 | 80 | } 81 | 82 | func update(args []string) error { 83 | if len(args) == 0 { 84 | args = []string{"."} 85 | } 86 | g, err := loadDefaultGodepsFile() 87 | if err != nil { 88 | return err 89 | } 90 | for _, arg := range args { 91 | arg := path.Clean(arg) 92 | any := markMatches(arg, g.Deps) 93 | if !any { 94 | log.Println("not in manifest:", arg) 95 | } 96 | } 97 | deps, rdeps, err := LoadVCSAndUpdate(g.Deps) 98 | if err != nil { 99 | return err 100 | } 101 | if len(deps) == 0 { 102 | return errorNoPackagesUpdatable 103 | } 104 | g.addOrUpdateDeps(deps) 105 | g.removeDeps(rdeps) 106 | if _, err = g.save(); err != nil { 107 | return err 108 | } 109 | 110 | srcdir := relativeVendorTarget(VendorExperiment) 111 | if err := removeSrc(filepath.FromSlash(strings.Trim(sep, "/")), rdeps); err != nil { 112 | return err 113 | } 114 | copySrc(srcdir, deps) 115 | 116 | ok, err := needRewrite(g.Packages) 117 | if err != nil { 118 | return err 119 | } 120 | var rewritePaths []string 121 | if ok { 122 | for _, dep := range g.Deps { 123 | rewritePaths = append(rewritePaths, dep.ImportPath) 124 | } 125 | } 126 | return rewrite(nil, g.ImportPath, rewritePaths) 127 | } 128 | 129 | func needRewrite(importPaths []string) (bool, error) { 130 | if len(importPaths) == 0 { 131 | importPaths = []string{"."} 132 | } 133 | a, err := LoadPackages(importPaths...) 134 | if err != nil { 135 | return false, err 136 | } 137 | for _, p := range a { 138 | for _, name := range p.allGoFiles() { 139 | path := filepath.Join(p.Dir, name) 140 | hasSep, err := hasRewrittenImportStatement(path) 141 | if err != nil { 142 | return false, err 143 | } 144 | if hasSep { 145 | return true, nil 146 | } 147 | } 148 | } 149 | return false, nil 150 | } 151 | 152 | func hasRewrittenImportStatement(path string) (bool, error) { 153 | fset := token.NewFileSet() 154 | f, err := parser.ParseFile(fset, path, nil, 0) 155 | if err != nil { 156 | return false, err 157 | } 158 | for _, s := range f.Imports { 159 | name, _ := strconv.Unquote(s.Path.Value) 160 | if strings.Contains(name, sep) { 161 | return true, nil 162 | } 163 | } 164 | return false, nil 165 | } 166 | 167 | // markMatches marks each entry in deps with an import path that 168 | // matches pat. It returns whether any matches occurred. 169 | func markMatches(pat string, deps []Dependency) (matched bool) { 170 | f := matchPattern(pat) 171 | for i, dep := range deps { 172 | if f(dep.ImportPath) { 173 | deps[i].matched = true 174 | matched = true 175 | } 176 | } 177 | return matched 178 | } 179 | 180 | func fillDeps(deps []Dependency) ([]Dependency, error) { 181 | for i := range deps { 182 | if deps[i].pkg != nil { 183 | continue 184 | } 185 | ps, err := LoadPackages(deps[i].ImportPath) 186 | if err != nil { 187 | if _, ok := err.(errPackageNotFound); ok { 188 | deps[i].missing = true 189 | continue 190 | } 191 | return nil, err 192 | } 193 | if len(ps) > 1 { 194 | panic("More than one package found for " + deps[i].ImportPath) 195 | } 196 | p := ps[0] 197 | deps[i].pkg = p 198 | deps[i].dir = p.Dir 199 | deps[i].ws = p.Root 200 | 201 | vcs, reporoot, err := VCSFromDir(p.Dir, filepath.Join(p.Root, "src")) 202 | if err != nil { 203 | return nil, errorLoadingDeps 204 | } 205 | deps[i].root = filepath.ToSlash(reporoot) 206 | deps[i].vcs = vcs 207 | } 208 | 209 | return deps, nil 210 | } 211 | 212 | // LoadVCSAndUpdate loads and updates a set of dependencies. 213 | func LoadVCSAndUpdate(deps []Dependency) ([]Dependency, []Dependency, error) { 214 | var err1 error 215 | 216 | deps, err := fillDeps(deps) 217 | if err != nil { 218 | return nil, nil, err 219 | } 220 | 221 | repoMask := make(map[string]bool) 222 | for i := range deps { 223 | if !deps[i].matched { 224 | repoMask[deps[i].root] = true 225 | } 226 | } 227 | 228 | // Determine if we need any new packages because of new transitive imports 229 | for _, dep := range deps { 230 | if !dep.matched || dep.missing { 231 | continue 232 | } 233 | for _, dp := range dep.pkg.Dependencies { 234 | if dp.Goroot { 235 | continue 236 | } 237 | var have bool 238 | for _, d := range deps { 239 | if d.ImportPath == dp.ImportPath { 240 | have = true 241 | break 242 | } 243 | } 244 | if !have { 245 | deps = append(deps, Dependency{ImportPath: dp.ImportPath, matched: true}) 246 | } 247 | } 248 | } 249 | 250 | deps, err = fillDeps(deps) 251 | if err != nil { 252 | return nil, nil, err 253 | } 254 | 255 | var toUpdate, toRemove []Dependency 256 | for _, d := range deps { 257 | if !d.matched || repoMask[d.root] { 258 | continue 259 | } 260 | if d.missing { 261 | toRemove = append(toRemove, d) 262 | continue 263 | } 264 | toUpdate = append(toUpdate, d) 265 | } 266 | 267 | debugln("toUpdate") 268 | ppln(toUpdate) 269 | 270 | var toCopy []Dependency 271 | for _, d := range toUpdate { 272 | id, err := d.vcs.identify(d.dir) 273 | if err != nil { 274 | log.Println(err) 275 | err1 = errorLoadingDeps 276 | continue 277 | } 278 | if d.vcs.isDirty(d.dir, id) { 279 | log.Println("dirty working tree (please commit changes):", d.dir) 280 | } 281 | d.Rev = id 282 | d.Comment = d.vcs.describe(d.dir, id) 283 | toCopy = append(toCopy, d) 284 | } 285 | debugln("toCopy") 286 | ppln(toCopy) 287 | 288 | if err1 != nil { 289 | return nil, nil, err1 290 | } 291 | return toCopy, toRemove, nil 292 | } 293 | -------------------------------------------------------------------------------- /rewrite_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | "testing" 7 | ) 8 | 9 | func TestUnqualify(t *testing.T) { 10 | setGlobals(false) 11 | var cases = []struct { 12 | path string 13 | want string 14 | }{ 15 | {"C", "C"}, 16 | {"D/Godeps/_workspace/src/T", "T"}, 17 | {"C/Godeps/_workspace/src/D/Godeps/_workspace/src/T", "T"}, 18 | } 19 | for _, test := range cases { 20 | g := unqualify(test.path) 21 | if g != test.want { 22 | t.Errorf("qualify(%s) = %s want %s", test.path, g, test.want) 23 | } 24 | } 25 | } 26 | 27 | func TestQualify(t *testing.T) { 28 | var cases = []struct { 29 | path string 30 | want string 31 | }{ 32 | {"C", "C"}, 33 | {"C/P", "C/P"}, 34 | {"fmt", "fmt"}, 35 | {"DP", "DP"}, 36 | {"D", "C/Godeps/_workspace/src/D"}, 37 | {"D/P", "C/Godeps/_workspace/src/D/P"}, 38 | } 39 | for _, test := range cases { 40 | g := qualify(test.path, "C", []string{"D"}) 41 | if g != test.want { 42 | t.Errorf("qualify({C}, %s) = %s want %s", test.path, g, test.want) 43 | } 44 | } 45 | } 46 | 47 | const ( 48 | whitespace = `package main 49 | 50 | import "D" 51 | 52 | var ( 53 | x int 54 | abc int 55 | ) 56 | ` 57 | whitespaceRewritten = `package main 58 | 59 | import "C/Godeps/_workspace/src/D" 60 | 61 | var ( 62 | x int 63 | abc int 64 | ) 65 | ` 66 | sortOrder = `package main 67 | 68 | import ( 69 | "E" 70 | "C/Godeps/_workspace/src/D" 71 | ) 72 | ` 73 | sortOrderRewritten = `package main 74 | 75 | import ( 76 | "C/Godeps/_workspace/src/D" 77 | "C/Godeps/_workspace/src/E" 78 | ) 79 | ` 80 | sortOrderPreserveComment = `package main 81 | 82 | import ( 83 | "C/Godeps/_workspace/src/E" // src E 84 | "D" // src D 85 | ) 86 | ` 87 | sortOrderPreserveCommentRewritten = `package main 88 | 89 | import ( 90 | "C/Godeps/_workspace/src/D" // src D 91 | "C/Godeps/_workspace/src/E" // src E 92 | ) 93 | ` 94 | ) 95 | 96 | func TestRewrite(t *testing.T) { 97 | var cases = []struct { 98 | cwd string 99 | paths []string 100 | start []*node 101 | want []*node 102 | werr bool 103 | }{ 104 | { // simple case, one dependency 105 | cwd: "C", 106 | paths: []string{"D"}, 107 | start: []*node{ 108 | {"C/main.go", pkg("main", "D"), nil}, 109 | {"C/Godeps/_workspace/src/D/main.go", pkg("D"), nil}, 110 | }, 111 | want: []*node{ 112 | {"C/main.go", pkg("main", "C/Godeps/_workspace/src/D"), nil}, 113 | {"C/Godeps/_workspace/src/D/main.go", pkg("D"), nil}, 114 | }, 115 | }, 116 | { // transitive dep 117 | cwd: "C", 118 | paths: []string{"D", "T"}, 119 | start: []*node{ 120 | {"C/main.go", pkg("main", "D"), nil}, 121 | {"C/Godeps/_workspace/src/D/main.go", pkg("D", "T"), nil}, 122 | {"C/Godeps/_workspace/src/T/main.go", pkg("T"), nil}, 123 | }, 124 | want: []*node{ 125 | {"C/main.go", pkg("main", "C/Godeps/_workspace/src/D"), nil}, 126 | {"C/Godeps/_workspace/src/D/main.go", pkg("D", "C/Godeps/_workspace/src/T"), nil}, 127 | {"C/Godeps/_workspace/src/T/main.go", pkg("T"), nil}, 128 | }, 129 | }, 130 | { // intermediate dep that uses godep save -r 131 | cwd: "C", 132 | paths: []string{"D", "T"}, 133 | start: []*node{ 134 | {"C/main.go", pkg("main", "D"), nil}, 135 | {"C/Godeps/_workspace/src/D/main.go", pkg("D", "D/Godeps/_workspace/src/T"), nil}, 136 | {"C/Godeps/_workspace/src/T/main.go", pkg("T"), nil}, 137 | }, 138 | want: []*node{ 139 | {"C/main.go", pkg("main", "C/Godeps/_workspace/src/D"), nil}, 140 | {"C/Godeps/_workspace/src/D/main.go", pkg("D", "C/Godeps/_workspace/src/T"), nil}, 141 | {"C/Godeps/_workspace/src/T/main.go", pkg("T"), nil}, 142 | }, 143 | }, 144 | { // don't qualify standard library and local imports 145 | cwd: "C", 146 | start: []*node{ 147 | {"C/main.go", pkg("main", "fmt", "C/D"), nil}, 148 | {"C/D/main.go", pkg("D"), nil}, 149 | }, 150 | want: []*node{ 151 | {"C/main.go", pkg("main", "fmt", "C/D"), nil}, 152 | {"C/D/main.go", pkg("D"), nil}, 153 | }, 154 | }, 155 | { // simple case, one dependency, -r=false 156 | cwd: "C", 157 | start: []*node{ 158 | {"C/main.go", pkg("main", "D"), nil}, 159 | {"C/Godeps/_workspace/src/D/main.go", pkg("D"), nil}, 160 | }, 161 | want: []*node{ 162 | {"C/main.go", pkg("main", "D"), nil}, 163 | {"C/Godeps/_workspace/src/D/main.go", pkg("D"), nil}, 164 | }, 165 | }, 166 | { // transitive dep, -r=false 167 | cwd: "C", 168 | start: []*node{ 169 | {"C/main.go", pkg("main", "D"), nil}, 170 | {"C/Godeps/_workspace/src/D/main.go", pkg("D", "T"), nil}, 171 | {"C/Godeps/_workspace/src/T/main.go", pkg("T"), nil}, 172 | }, 173 | want: []*node{ 174 | {"C/main.go", pkg("main", "D"), nil}, 175 | {"C/Godeps/_workspace/src/D/main.go", pkg("D", "T"), nil}, 176 | {"C/Godeps/_workspace/src/T/main.go", pkg("T"), nil}, 177 | }, 178 | }, 179 | { // intermediate dep that uses godep save -r, -r=false 180 | cwd: "C", 181 | start: []*node{ 182 | {"C/main.go", pkg("main", "D"), nil}, 183 | {"C/Godeps/_workspace/src/D/main.go", pkg("D", "D/Godeps/_workspace/src/T"), nil}, 184 | {"C/Godeps/_workspace/src/T/main.go", pkg("T"), nil}, 185 | }, 186 | want: []*node{ 187 | {"C/main.go", pkg("main", "D"), nil}, 188 | {"C/Godeps/_workspace/src/D/main.go", pkg("D", "T"), nil}, 189 | {"C/Godeps/_workspace/src/T/main.go", pkg("T"), nil}, 190 | }, 191 | }, 192 | { // whitespace 193 | cwd: "C", 194 | paths: []string{"D"}, 195 | start: []*node{ 196 | {"C/main.go", whitespace, nil}, 197 | }, 198 | want: []*node{ 199 | {"C/main.go", whitespaceRewritten, nil}, 200 | }, 201 | }, 202 | { // sort after rewrite 203 | cwd: "C", 204 | paths: []string{"D", "E"}, 205 | start: []*node{ 206 | {"C/main.go", sortOrder, nil}, 207 | }, 208 | want: []*node{ 209 | {"C/main.go", sortOrderRewritten, nil}, 210 | }, 211 | }, 212 | { // sort after rewrite 213 | cwd: "C", 214 | paths: []string{"D", "E"}, 215 | start: []*node{ 216 | {"C/main.go", sortOrderPreserveComment, nil}, 217 | }, 218 | want: []*node{ 219 | {"C/main.go", sortOrderPreserveCommentRewritten, nil}, 220 | }, 221 | }, 222 | { // testdata directory is copied unmodified. 223 | cwd: "C", 224 | paths: []string{"D"}, 225 | start: []*node{ 226 | {"C/main.go", pkg("main", "D"), nil}, 227 | {"C/testdata", "", 228 | []*node{ 229 | {"badpkg.go", "//", nil}, 230 | }, 231 | }, 232 | }, 233 | want: []*node{ 234 | {"C/main.go", pkg("main", "C/Godeps/_workspace/src/D"), nil}, 235 | {"C/testdata", "", 236 | []*node{ 237 | {"badpkg.go", "//", nil}, 238 | }, 239 | }, 240 | }, 241 | }, 242 | } 243 | 244 | const gopath = "godeptest" 245 | defer os.RemoveAll(gopath) 246 | for pos, test := range cases { 247 | setGlobals(false) 248 | err := os.RemoveAll(gopath) 249 | if err != nil { 250 | t.Fatal(err) 251 | } 252 | src := filepath.Join(gopath, "src") 253 | makeTree(t, &node{src, "", test.start}, "") 254 | err = rewriteTree(filepath.Join(src, test.cwd), test.cwd, test.paths) 255 | if g := err != nil; g != test.werr { 256 | t.Errorf("save err = %v (%v) want %v", g, err, test.werr) 257 | } 258 | tempFiles, err := filepath.Glob(filepath.Join(src, test.cwd) + "/*.temp") 259 | if err != nil { 260 | t.Errorf("Error while running glob: %s", err.Error()) 261 | } 262 | if len(tempFiles) != 0 { 263 | t.Errorf("Unexpected tempfiles: %+v", tempFiles) 264 | } 265 | 266 | checkTree(t, pos, &node{src, "", test.want}) 267 | } 268 | } 269 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Godep - Archived 2 | 3 | Please use [dep](https://github.com/golang/dep) or another tool instead. 4 | 5 | The rest of this readme is preserved for those that may still need its contents. 6 | 7 | [![Build Status](https://travis-ci.org/tools/godep.svg)](https://travis-ci.org/tools/godep) 8 | 9 | [![GoDoc](https://godoc.org/github.com/tools/godep?status.svg)](https://godoc.org/github.com/tools/godep) 10 | 11 | godep helps build packages reproducibly by fixing their dependencies. 12 | 13 | This tool assumes you are working in a standard Go workspace, as described [here](http://golang.org/doc/code.html). We 14 | expect godep to build on Go 1.4* or newer, but you can use it on any project that works with Go 1 or newer. 15 | 16 | Please check the [FAQ](FAQ.md) if you have a question. 17 | 18 | ## Golang Dep 19 | 20 | The Go community now has the [dep](https://github.com/golang/dep) project to 21 | manage dependencies. Please consider trying to migrate from Godep to dep. If there 22 | is an issue preventing you from migrating please file an issue with dep so the 23 | problem can be corrected. Godep will continue to be supported for some time but 24 | is considered to be in a state of support rather than active feature development. 25 | 26 | ## Install 27 | 28 | ```console 29 | go get github.com/tools/godep 30 | ``` 31 | 32 | ## How to use godep with a new project 33 | 34 | Assuming you've got everything working already, so you can build your project 35 | with `go install` and test it with `go test`, it's one command to start using: 36 | 37 | ```console 38 | godep save 39 | ``` 40 | 41 | This will save a list of dependencies to the file `Godeps/Godeps.json` and copy 42 | their source code into `vendor/` (or `Godeps/_workspace/` when using older 43 | versions of Go). Godep does **not copy**: 44 | 45 | - files from source repositories that are not tracked in version control. 46 | - `*_test.go` files. 47 | - `testdata` directories. 48 | - files outside of the go packages. 49 | 50 | Godep does not process the imports of `.go` files with either the `ignore` 51 | or `appengine` build tags. 52 | 53 | Test files and testdata directories can be saved by adding `-t`. 54 | 55 | Read over the contents of `vendor/` and make sure it looks reasonable. Then 56 | commit the `Godeps/` and `vendor/` directories to version control. 57 | 58 | ## The deprecated `-r` flag 59 | 60 | For older versions of Go, the `-r` flag tells save to automatically rewrite 61 | package import paths. This allows your code to refer directly to the copied 62 | dependencies in `Godeps/_workspace`. So, a package C that depends on package 63 | D will actually import `C/Godeps/_workspace/src/D`. This makes C's repo 64 | self-contained and causes `go get` to build C with the right version of all 65 | dependencies. 66 | 67 | If you don't use `-r`, when using older version of Go, then in order to use the 68 | fixed dependencies and get reproducible builds, you must make sure that **every 69 | time** you run a Go-related command, you wrap it in one of these two ways: 70 | 71 | - If the command you are running is just `go`, run it as `godep go ...`, e.g. 72 | `godep go install -v ./...` 73 | - When using a different command, set your `$GOPATH` using `godep path` as 74 | described below. 75 | 76 | `-r` isn't necessary with go1.6+ and isn't allowed. 77 | 78 | ## Additional Operations 79 | 80 | ### Restore 81 | 82 | The `godep restore` installs the 83 | package versions specified in `Godeps/Godeps.json` to your `$GOPATH`. This 84 | modifies the state of packages in your `$GOPATH`. NOTE: `godep restore` leaves 85 | git repositories in a detached state. `go1.6`+ no longer checks out the master 86 | branch when doing a `go get`, see [here](https://github.com/golang/go/commit/42206598671a44111c8f726ad33dc7b265bdf669). 87 | 88 | > If you run `godep restore` in your main `$GOPATH` `go get -u` will fail on packages that are behind master. 89 | 90 | Please see the [FAQ](https://github.com/tools/godep/blob/master/FAQ.md#should-i-use-godep-restore) section about restore. 91 | 92 | ### Edit-test Cycle 93 | 94 | 1. Edit code 95 | 1. Run `godep go test` 96 | 1. (repeat) 97 | 98 | ### Add a Dependency 99 | 100 | To add a new package foo/bar, do this: 101 | 102 | 1. Run `go get foo/bar` 103 | 1. Edit your code to import foo/bar. 104 | 1. Run `godep save` (or `godep save ./...`). 105 | 106 | ### Update a Dependency 107 | 108 | To update a package from your `$GOPATH`, do this: 109 | 110 | 1. Run `go get -u foo/bar` 111 | 1. Run `godep update foo/bar`. 112 | 113 | You can use the `...` wildcard, for example `godep update foo/...`. Before comitting the change, you'll probably want to 114 | inspect the changes to Godeps, for example with `git diff`, and make sure it looks reasonable. 115 | 116 | ## Multiple Packages 117 | 118 | If your repository has more than one package, you're probably accustomed to 119 | running commands like `go test ./...`, `go install ./...`, and `go fmt ./...`. 120 | Similarly, you should run `godep save ./...` to capture the dependencies of all 121 | packages in your application. 122 | 123 | ## File Format 124 | 125 | Godeps is a json file with the following structure: 126 | 127 | ```go 128 | type Godeps struct { 129 | ImportPath string 130 | GoVersion string // Abridged output of 'go version'. 131 | GodepVersion string // Abridged output of 'godep version' 132 | Packages []string // Arguments to godep save, if any. 133 | Deps []struct { 134 | ImportPath string 135 | Comment string // Description of commit, if present. 136 | Rev string // VCS-specific commit ID. 137 | } 138 | } 139 | ``` 140 | 141 | Example Godeps: 142 | 143 | ```json 144 | { 145 | "ImportPath": "github.com/kr/hk", 146 | "GoVersion": "go1.6", 147 | "Deps": [ 148 | { 149 | "ImportPath": "code.google.com/p/go-netrc/netrc", 150 | "Rev": "28676070ab99" 151 | }, 152 | { 153 | "ImportPath": "github.com/kr/binarydist", 154 | "Rev": "3380ade90f8b0dfa3e363fd7d7e941fa857d0d13" 155 | } 156 | ] 157 | } 158 | ``` 159 | 160 | ## Migrating to vendor/ 161 | 162 | Godep supports the Go 1.5+ vendor/ 163 | [experiment](https://github.com/golang/go/commit/183cc0cd41f06f83cb7a2490a499e3f9101befff) 164 | utilizing the same environment variable that the go tooling itself supports 165 | (`GO15VENDOREXPERIMENT`). 166 | 167 | godep mostly works the same way as the `go` command line tool. If you have go 168 | 1.5.X and set `GO15VENDOREXPERIMENT=1` or have go1.6.X (or devel) `vendor/` 169 | is enabled. **Unless** you already have a `Godeps/_workspace`. This is a safety 170 | feature and godep warns you about this. 171 | 172 | When `vendor/` is enabled godep will write the vendored code into the top level 173 | `./vendor/` directory. A `./Godeps/Godeps.json` file is created to track 174 | the dependencies and revisions. `vendor/` is not compatible with rewrites. 175 | 176 | There is currently no automated migration between the old Godeps workspace and 177 | the vendor directory, but the following steps should work: 178 | 179 | ```term 180 | # just to be safe 181 | $ unset GO15VENDOREXPERIMENT 182 | 183 | # restore currently vendored deps to the $GOPATH 184 | $ godep restore 185 | 186 | # The next line is only needed to automatically undo rewritten imports that were 187 | # created with godep save -r. 188 | $ godep save -r=false 189 | 190 | # Remove the old Godeps folder 191 | $ rm -rf Godeps 192 | 193 | # If on go1.5.X to enable `vendor/` 194 | $ export GO15VENDOREXPERIMENT=1 195 | 196 | # re-analyze deps and save to `vendor/`. 197 | $ godep save 198 | 199 | # Add the changes to your VCS 200 | $ git add -A . ; git commit -am "Godep workspace -> vendor/" 201 | 202 | # You should see your Godeps/_workspace/src files "moved" to vendor/. 203 | ``` 204 | 205 | ## Releasing 206 | 207 | 1. Increment the version in `version.go`. 208 | 1. Tag the commit with the same version number. 209 | 1. Update `Changelog.md`. 210 | -------------------------------------------------------------------------------- /vcs.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "fmt" 7 | "os" 8 | "os/exec" 9 | "path/filepath" 10 | "strings" 11 | 12 | "golang.org/x/tools/go/vcs" 13 | ) 14 | 15 | // VCS represents a version control system. 16 | type VCS struct { 17 | vcs *vcs.Cmd 18 | 19 | IdentifyCmd string 20 | DescribeCmd string 21 | DiffCmd string 22 | ListCmd string 23 | RootCmd string 24 | 25 | // run in sandbox repos 26 | ExistsCmd string 27 | } 28 | 29 | var vcsBzr = &VCS{ 30 | vcs: vcs.ByCmd("bzr"), 31 | 32 | IdentifyCmd: "version-info --custom --template {revision_id}", 33 | DescribeCmd: "revno", // TODO(kr): find tag names if possible 34 | DiffCmd: "diff -r {rev}", 35 | ListCmd: "ls --from-root -R", 36 | RootCmd: "root", 37 | } 38 | 39 | var vcsGit = &VCS{ 40 | vcs: vcs.ByCmd("git"), 41 | 42 | IdentifyCmd: "rev-parse HEAD", 43 | DescribeCmd: "describe --tags", 44 | DiffCmd: "diff {rev}", 45 | ListCmd: "ls-files --full-name", 46 | RootCmd: "rev-parse --show-cdup", 47 | 48 | ExistsCmd: "cat-file -e {rev}", 49 | } 50 | 51 | var vcsHg = &VCS{ 52 | vcs: vcs.ByCmd("hg"), 53 | 54 | IdentifyCmd: "parents --template {node}", 55 | DescribeCmd: "log -r . --template {latesttag}-{latesttagdistance}", 56 | DiffCmd: "diff -r {rev}", 57 | ListCmd: "status --all --no-status", 58 | RootCmd: "root", 59 | 60 | ExistsCmd: "cat -r {rev} .", 61 | } 62 | 63 | var cmd = map[*vcs.Cmd]*VCS{ 64 | vcsBzr.vcs: vcsBzr, 65 | vcsGit.vcs: vcsGit, 66 | vcsHg.vcs: vcsHg, 67 | } 68 | 69 | // VCSFromDir returns a VCS value from a directory. 70 | func VCSFromDir(dir, srcRoot string) (*VCS, string, error) { 71 | vcscmd, reporoot, err := vcs.FromDir(dir, srcRoot) 72 | if err != nil { 73 | return nil, "", fmt.Errorf("error while inspecting %q: %v", dir, err) 74 | } 75 | vcsext := cmd[vcscmd] 76 | if vcsext == nil { 77 | return nil, "", fmt.Errorf("%s is unsupported: %s", vcscmd.Name, dir) 78 | } 79 | return vcsext, reporoot, nil 80 | } 81 | 82 | func (v *VCS) identify(dir string) (string, error) { 83 | out, err := v.runOutput(dir, v.IdentifyCmd) 84 | return string(bytes.TrimSpace(out)), err 85 | } 86 | 87 | func absRoot(dir, out string) string { 88 | if filepath.IsAbs(out) { 89 | return filepath.Clean(out) 90 | } 91 | return filepath.Join(dir, out) 92 | } 93 | 94 | func (v *VCS) root(dir string) (string, error) { 95 | out, err := v.runOutput(dir, v.RootCmd) 96 | return absRoot(dir, string(bytes.TrimSpace(out))), err 97 | } 98 | 99 | func (v *VCS) describe(dir, rev string) string { 100 | out, err := v.runOutputVerboseOnly(dir, v.DescribeCmd, "rev", rev) 101 | if err != nil { 102 | return "" 103 | } 104 | return string(bytes.TrimSpace(out)) 105 | } 106 | 107 | func (v *VCS) isDirty(dir, rev string) bool { 108 | out, err := v.runOutput(dir, v.DiffCmd, "rev", rev) 109 | return err != nil || len(out) != 0 110 | } 111 | 112 | type vcsFiles map[string]bool 113 | 114 | func (vf vcsFiles) Contains(path string) bool { 115 | // Fast path, we have the path 116 | if vf[path] { 117 | return true 118 | } 119 | 120 | // Slow path for case insensitive filesystems 121 | // See #310 122 | for f := range vf { 123 | if pathEqual(f, path) { 124 | return true 125 | } 126 | // git's root command (maybe other vcs as well) resolve symlinks, so try that too 127 | // FIXME: rev-parse --show-cdup + extra logic will fix this for git but also need to validate the other vcs commands. This is maybe temporary. 128 | p, err := filepath.EvalSymlinks(path) 129 | if err != nil { 130 | return false 131 | } 132 | if pathEqual(f, p) { 133 | return true 134 | } 135 | } 136 | 137 | // No matches by either method 138 | return false 139 | } 140 | 141 | // listFiles tracked by the VCS in the repo that contains dir, converted to absolute path. 142 | func (v *VCS) listFiles(dir string) vcsFiles { 143 | root, err := v.root(dir) 144 | debugln("vcs dir", dir) 145 | debugln("vcs root", root) 146 | ppln(v) 147 | if err != nil { 148 | return nil 149 | } 150 | out, err := v.runOutput(dir, v.ListCmd) 151 | if err != nil { 152 | return nil 153 | } 154 | files := make(vcsFiles) 155 | for _, file := range bytes.Split(out, []byte{'\n'}) { 156 | if len(file) > 0 { 157 | path, err := filepath.Abs(filepath.Join(root, string(file))) 158 | if err != nil { 159 | panic(err) // this should not happen 160 | } 161 | 162 | if pathEqual(filepath.Dir(path), dir) { 163 | files[path] = true 164 | } 165 | } 166 | } 167 | return files 168 | } 169 | 170 | func (v *VCS) exists(dir, rev string) bool { 171 | err := v.runVerboseOnly(dir, v.ExistsCmd, "rev", rev) 172 | return err == nil 173 | } 174 | 175 | // RevSync checks out the revision given by rev in dir. 176 | // The dir must exist and rev must be a valid revision. 177 | func (v *VCS) RevSync(dir, rev string) error { 178 | return v.run(dir, v.vcs.TagSyncCmd, "tag", rev) 179 | } 180 | 181 | // run runs the command line cmd in the given directory. 182 | // keyval is a list of key, value pairs. run expands 183 | // instances of {key} in cmd into value, but only after 184 | // splitting cmd into individual arguments. 185 | // If an error occurs, run prints the command line and the 186 | // command's combined stdout+stderr to standard error. 187 | // Otherwise run discards the command's output. 188 | func (v *VCS) run(dir string, cmdline string, kv ...string) error { 189 | _, err := v.run1(dir, cmdline, kv, true) 190 | return err 191 | } 192 | 193 | // runVerboseOnly is like run but only generates error output to standard error in verbose mode. 194 | func (v *VCS) runVerboseOnly(dir string, cmdline string, kv ...string) error { 195 | _, err := v.run1(dir, cmdline, kv, false) 196 | return err 197 | } 198 | 199 | // runOutput is like run but returns the output of the command. 200 | func (v *VCS) runOutput(dir string, cmdline string, kv ...string) ([]byte, error) { 201 | return v.run1(dir, cmdline, kv, true) 202 | } 203 | 204 | // runOutputVerboseOnly is like runOutput but only generates error output to standard error in verbose mode. 205 | func (v *VCS) runOutputVerboseOnly(dir string, cmdline string, kv ...string) ([]byte, error) { 206 | return v.run1(dir, cmdline, kv, false) 207 | } 208 | 209 | // run1 is the generalized implementation of run and runOutput. 210 | func (v *VCS) run1(dir string, cmdline string, kv []string, verbose bool) ([]byte, error) { 211 | m := make(map[string]string) 212 | for i := 0; i < len(kv); i += 2 { 213 | m[kv[i]] = kv[i+1] 214 | } 215 | args := strings.Fields(cmdline) 216 | for i, arg := range args { 217 | args[i] = expand(m, arg) 218 | } 219 | 220 | _, err := exec.LookPath(v.vcs.Cmd) 221 | if err != nil { 222 | fmt.Fprintf(os.Stderr, "godep: missing %s command.\n", v.vcs.Name) 223 | return nil, err 224 | } 225 | 226 | cmd := exec.Command(v.vcs.Cmd, args...) 227 | cmd.Dir = dir 228 | var buf bytes.Buffer 229 | cmd.Stdout = &buf 230 | cmd.Stderr = &buf 231 | err = cmd.Run() 232 | out := buf.Bytes() 233 | if err != nil { 234 | if verbose { 235 | fmt.Fprintf(os.Stderr, "# cd %s; %s %s\n", dir, v.vcs.Cmd, strings.Join(args, " ")) 236 | os.Stderr.Write(out) 237 | } 238 | return nil, err 239 | } 240 | return out, nil 241 | } 242 | 243 | func expand(m map[string]string, s string) string { 244 | for k, v := range m { 245 | s = strings.Replace(s, "{"+k+"}", v, -1) 246 | } 247 | return s 248 | } 249 | 250 | func gitDetached(r string) (bool, error) { 251 | o, err := vcsGit.runOutput(r, "status") 252 | if err != nil { 253 | return false, errors.New("unable to determine git status " + err.Error()) 254 | } 255 | return bytes.Contains(o, []byte("HEAD detached at")), nil 256 | } 257 | 258 | func gitDefaultBranch(r string) (string, error) { 259 | o, err := vcsGit.runOutput(r, "remote show origin") 260 | if err != nil { 261 | return "", errors.New("Running git remote show origin errored with: " + err.Error()) 262 | } 263 | return gitDetermineDefaultBranch(r, string(o)) 264 | } 265 | 266 | func gitDetermineDefaultBranch(r, o string) (string, error) { 267 | e := "Unable to determine HEAD branch: " 268 | hb := "HEAD branch:" 269 | lbcfgp := "Local branch configured for 'git pull':" 270 | s := strings.Index(o, hb) 271 | if s < 0 { 272 | b := strings.Index(o, lbcfgp) 273 | if b < 0 { 274 | return "", errors.New(e + "Remote HEAD is ambiguous. Before godep can pull new commits you will need to:" + ` 275 | cd ` + r + ` 276 | git checkout 277 | Here is what was reported: 278 | ` + o) 279 | } 280 | s = b + len(lbcfgp) 281 | } else { 282 | s += len(hb) 283 | } 284 | f := strings.Fields(o[s:]) 285 | if len(f) < 3 { 286 | return "", errors.New(e + "git output too short") 287 | } 288 | return f[0], nil 289 | } 290 | 291 | func gitCheckout(r, b string) error { 292 | return vcsGit.run(r, "checkout "+b) 293 | } 294 | -------------------------------------------------------------------------------- /vendor/github.com/kr/pretty/formatter.go: -------------------------------------------------------------------------------- 1 | package pretty 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "reflect" 7 | "strconv" 8 | "text/tabwriter" 9 | 10 | "github.com/kr/text" 11 | ) 12 | 13 | const ( 14 | limit = 50 15 | ) 16 | 17 | type formatter struct { 18 | x interface{} 19 | force bool 20 | quote bool 21 | } 22 | 23 | // Formatter makes a wrapper, f, that will format x as go source with line 24 | // breaks and tabs. Object f responds to the "%v" formatting verb when both the 25 | // "#" and " " (space) flags are set, for example: 26 | // 27 | // fmt.Sprintf("%# v", Formatter(x)) 28 | // 29 | // If one of these two flags is not set, or any other verb is used, f will 30 | // format x according to the usual rules of package fmt. 31 | // In particular, if x satisfies fmt.Formatter, then x.Format will be called. 32 | func Formatter(x interface{}) (f fmt.Formatter) { 33 | return formatter{x: x, quote: true} 34 | } 35 | 36 | func (fo formatter) String() string { 37 | return fmt.Sprint(fo.x) // unwrap it 38 | } 39 | 40 | func (fo formatter) passThrough(f fmt.State, c rune) { 41 | s := "%" 42 | for i := 0; i < 128; i++ { 43 | if f.Flag(i) { 44 | s += string(i) 45 | } 46 | } 47 | if w, ok := f.Width(); ok { 48 | s += fmt.Sprintf("%d", w) 49 | } 50 | if p, ok := f.Precision(); ok { 51 | s += fmt.Sprintf(".%d", p) 52 | } 53 | s += string(c) 54 | fmt.Fprintf(f, s, fo.x) 55 | } 56 | 57 | func (fo formatter) Format(f fmt.State, c rune) { 58 | if fo.force || c == 'v' && f.Flag('#') && f.Flag(' ') { 59 | w := tabwriter.NewWriter(f, 4, 4, 1, ' ', 0) 60 | p := &printer{tw: w, Writer: w, visited: make(map[visit]int)} 61 | p.printValue(reflect.ValueOf(fo.x), true, fo.quote) 62 | w.Flush() 63 | return 64 | } 65 | fo.passThrough(f, c) 66 | } 67 | 68 | type printer struct { 69 | io.Writer 70 | tw *tabwriter.Writer 71 | visited map[visit]int 72 | depth int 73 | } 74 | 75 | func (p *printer) indent() *printer { 76 | q := *p 77 | q.tw = tabwriter.NewWriter(p.Writer, 4, 4, 1, ' ', 0) 78 | q.Writer = text.NewIndentWriter(q.tw, []byte{'\t'}) 79 | return &q 80 | } 81 | 82 | func (p *printer) printInline(v reflect.Value, x interface{}, showType bool) { 83 | if showType { 84 | io.WriteString(p, v.Type().String()) 85 | fmt.Fprintf(p, "(%#v)", x) 86 | } else { 87 | fmt.Fprintf(p, "%#v", x) 88 | } 89 | } 90 | 91 | // printValue must keep track of already-printed pointer values to avoid 92 | // infinite recursion. 93 | type visit struct { 94 | v uintptr 95 | typ reflect.Type 96 | } 97 | 98 | func (p *printer) printValue(v reflect.Value, showType, quote bool) { 99 | if p.depth > 10 { 100 | io.WriteString(p, "!%v(DEPTH EXCEEDED)") 101 | return 102 | } 103 | 104 | switch v.Kind() { 105 | case reflect.Bool: 106 | p.printInline(v, v.Bool(), showType) 107 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 108 | p.printInline(v, v.Int(), showType) 109 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 110 | p.printInline(v, v.Uint(), showType) 111 | case reflect.Float32, reflect.Float64: 112 | p.printInline(v, v.Float(), showType) 113 | case reflect.Complex64, reflect.Complex128: 114 | fmt.Fprintf(p, "%#v", v.Complex()) 115 | case reflect.String: 116 | p.fmtString(v.String(), quote) 117 | case reflect.Map: 118 | t := v.Type() 119 | if showType { 120 | io.WriteString(p, t.String()) 121 | } 122 | writeByte(p, '{') 123 | if nonzero(v) { 124 | expand := !canInline(v.Type()) 125 | pp := p 126 | if expand { 127 | writeByte(p, '\n') 128 | pp = p.indent() 129 | } 130 | keys := v.MapKeys() 131 | for i := 0; i < v.Len(); i++ { 132 | showTypeInStruct := true 133 | k := keys[i] 134 | mv := v.MapIndex(k) 135 | pp.printValue(k, false, true) 136 | writeByte(pp, ':') 137 | if expand { 138 | writeByte(pp, '\t') 139 | } 140 | showTypeInStruct = t.Elem().Kind() == reflect.Interface 141 | pp.printValue(mv, showTypeInStruct, true) 142 | if expand { 143 | io.WriteString(pp, ",\n") 144 | } else if i < v.Len()-1 { 145 | io.WriteString(pp, ", ") 146 | } 147 | } 148 | if expand { 149 | pp.tw.Flush() 150 | } 151 | } 152 | writeByte(p, '}') 153 | case reflect.Struct: 154 | t := v.Type() 155 | if v.CanAddr() { 156 | addr := v.UnsafeAddr() 157 | vis := visit{addr, t} 158 | if vd, ok := p.visited[vis]; ok && vd < p.depth { 159 | p.fmtString(t.String()+"{(CYCLIC REFERENCE)}", false) 160 | break // don't print v again 161 | } 162 | p.visited[vis] = p.depth 163 | } 164 | 165 | if showType { 166 | io.WriteString(p, t.String()) 167 | } 168 | writeByte(p, '{') 169 | if nonzero(v) { 170 | expand := !canInline(v.Type()) 171 | pp := p 172 | if expand { 173 | writeByte(p, '\n') 174 | pp = p.indent() 175 | } 176 | for i := 0; i < v.NumField(); i++ { 177 | showTypeInStruct := true 178 | if f := t.Field(i); f.Name != "" { 179 | io.WriteString(pp, f.Name) 180 | writeByte(pp, ':') 181 | if expand { 182 | writeByte(pp, '\t') 183 | } 184 | showTypeInStruct = labelType(f.Type) 185 | } 186 | pp.printValue(getField(v, i), showTypeInStruct, true) 187 | if expand { 188 | io.WriteString(pp, ",\n") 189 | } else if i < v.NumField()-1 { 190 | io.WriteString(pp, ", ") 191 | } 192 | } 193 | if expand { 194 | pp.tw.Flush() 195 | } 196 | } 197 | writeByte(p, '}') 198 | case reflect.Interface: 199 | switch e := v.Elem(); { 200 | case e.Kind() == reflect.Invalid: 201 | io.WriteString(p, "nil") 202 | case e.IsValid(): 203 | pp := *p 204 | pp.depth++ 205 | pp.printValue(e, showType, true) 206 | default: 207 | io.WriteString(p, v.Type().String()) 208 | io.WriteString(p, "(nil)") 209 | } 210 | case reflect.Array, reflect.Slice: 211 | t := v.Type() 212 | if showType { 213 | io.WriteString(p, t.String()) 214 | } 215 | if v.Kind() == reflect.Slice && v.IsNil() && showType { 216 | io.WriteString(p, "(nil)") 217 | break 218 | } 219 | if v.Kind() == reflect.Slice && v.IsNil() { 220 | io.WriteString(p, "nil") 221 | break 222 | } 223 | writeByte(p, '{') 224 | expand := !canInline(v.Type()) 225 | pp := p 226 | if expand { 227 | writeByte(p, '\n') 228 | pp = p.indent() 229 | } 230 | for i := 0; i < v.Len(); i++ { 231 | showTypeInSlice := t.Elem().Kind() == reflect.Interface 232 | pp.printValue(v.Index(i), showTypeInSlice, true) 233 | if expand { 234 | io.WriteString(pp, ",\n") 235 | } else if i < v.Len()-1 { 236 | io.WriteString(pp, ", ") 237 | } 238 | } 239 | if expand { 240 | pp.tw.Flush() 241 | } 242 | writeByte(p, '}') 243 | case reflect.Ptr: 244 | e := v.Elem() 245 | if !e.IsValid() { 246 | writeByte(p, '(') 247 | io.WriteString(p, v.Type().String()) 248 | io.WriteString(p, ")(nil)") 249 | } else { 250 | pp := *p 251 | pp.depth++ 252 | writeByte(pp, '&') 253 | pp.printValue(e, true, true) 254 | } 255 | case reflect.Chan: 256 | x := v.Pointer() 257 | if showType { 258 | writeByte(p, '(') 259 | io.WriteString(p, v.Type().String()) 260 | fmt.Fprintf(p, ")(%#v)", x) 261 | } else { 262 | fmt.Fprintf(p, "%#v", x) 263 | } 264 | case reflect.Func: 265 | io.WriteString(p, v.Type().String()) 266 | io.WriteString(p, " {...}") 267 | case reflect.UnsafePointer: 268 | p.printInline(v, v.Pointer(), showType) 269 | case reflect.Invalid: 270 | io.WriteString(p, "nil") 271 | } 272 | } 273 | 274 | func canInline(t reflect.Type) bool { 275 | switch t.Kind() { 276 | case reflect.Map: 277 | return !canExpand(t.Elem()) 278 | case reflect.Struct: 279 | for i := 0; i < t.NumField(); i++ { 280 | if canExpand(t.Field(i).Type) { 281 | return false 282 | } 283 | } 284 | return true 285 | case reflect.Interface: 286 | return false 287 | case reflect.Array, reflect.Slice: 288 | return !canExpand(t.Elem()) 289 | case reflect.Ptr: 290 | return false 291 | case reflect.Chan, reflect.Func, reflect.UnsafePointer: 292 | return false 293 | } 294 | return true 295 | } 296 | 297 | func canExpand(t reflect.Type) bool { 298 | switch t.Kind() { 299 | case reflect.Map, reflect.Struct, 300 | reflect.Interface, reflect.Array, reflect.Slice, 301 | reflect.Ptr: 302 | return true 303 | } 304 | return false 305 | } 306 | 307 | func labelType(t reflect.Type) bool { 308 | switch t.Kind() { 309 | case reflect.Interface, reflect.Struct: 310 | return true 311 | } 312 | return false 313 | } 314 | 315 | func (p *printer) fmtString(s string, quote bool) { 316 | if quote { 317 | s = strconv.Quote(s) 318 | } 319 | io.WriteString(p, s) 320 | } 321 | 322 | func tryDeepEqual(a, b interface{}) bool { 323 | defer func() { recover() }() 324 | return reflect.DeepEqual(a, b) 325 | } 326 | 327 | func writeByte(w io.Writer, b byte) { 328 | w.Write([]byte{b}) 329 | } 330 | 331 | func getField(v reflect.Value, i int) reflect.Value { 332 | val := v.Field(i) 333 | if val.Kind() == reflect.Interface && !val.IsNil() { 334 | val = val.Elem() 335 | } 336 | return val 337 | } 338 | -------------------------------------------------------------------------------- /Changelog.md: -------------------------------------------------------------------------------- 1 | #v80 (2018/01/26) 2 | 3 | * Address lin/vet feedback. 4 | 5 | #v79 (2017/02/01) 6 | 7 | * Fixes #531: fullPackageInDir didn't capture the error from fillPackage() 8 | 9 | #v78 (2017/01/19) 10 | 11 | * Don't use build.ImportDir when discovering packages for the package spec. Fixes #529 12 | 13 | #v77 (2017/01/13) 14 | 15 | * Don't include quotes around hg revisions 16 | 17 | #v76 (2017/01/10) 18 | 19 | * Default to vendor being on unless older go versions. 20 | 21 | #v75 (2016/11/02) 22 | 23 | * Add "AUTHORS" and "CONTRIBUTORS" to legal files list: https://github.com/tools/godep/pull/522 24 | 25 | #v74 (2016/06/01) 26 | 27 | * Enable vendor/ on go1.7 28 | * No longer use a godep workspace, use vendor/ (yay!) 29 | * Notify that support for Godep workspaces will be removed once go1.8 ships 30 | 31 | #v73 (2016/05/31) 32 | 33 | * Fix permission changes on Windows via @alexbrand. Closes #481. 34 | 35 | #v72 (2016/05/27) 36 | 37 | * Improve handling of git remote show origin. Should help in cases where remote HEAD is ambiguous. 38 | * Add ISSUE_TEMPLATE 39 | 40 | #v71 (2016/05/24) 41 | 42 | * Preserve permissions on copied files. 43 | 44 | #v70 (2016/05/20) 45 | 46 | * Fix the May changelog dates 47 | * No need to call build.Import, we already have the root of the dependency. Fixes an additional comment on #365 48 | 49 | #v69 (2016/05/16) 50 | 51 | * Make sure `devel-` enabled `vendor/` unless there is a classic Godep _workspace already. 52 | 53 | #v68 (2016/05/16) 54 | 55 | * `devel-` is always considered newer than any released go version 56 | 57 | #v67 (2016/05/13) 58 | 59 | * Attempt to handle missing deps a little better. 60 | 61 | #v66 (2016/05/10) 62 | 63 | * Use `git remote show origin` to find the default branch when restoring a git based package repository that is in detached head state 64 | 65 | #v65 (2016/05/09) 66 | 67 | * Rewrite update so that it considers new transitive dependencies, both in the same repo and outside of it. 68 | 69 | #v64 (2016/05/09) 70 | 71 | * godep update golang.org/x/tools/go/vcs 72 | 73 | #v63 (2016/05/03) 74 | 75 | * Support recording devel- so development versions of Go can be matched 76 | 77 | #v62 (2016/04/07) 78 | 79 | * Note new go1.6+ behavior of not checking out master in README / restore help text. 80 | 81 | #v61 (2016/04/06) 82 | 83 | * Obey go version build tags based on recorded major go version. Fixes #448. 84 | 85 | #v60 (2016/03/18) 86 | 87 | * Make the $GOPATH check a warning. 88 | 89 | #v59 (2016/03/18) 90 | 91 | * Enforce requirement to be inside of a go src directory. A lot of time is usually spent 92 | tracking down bug reports where people are doign stuff from outside of their $GOPATH. This 93 | should help with that, at least until there it time to properly test godep use outside of a 94 | $GOPATH and fix the issues. 95 | 96 | #v58 (2016/03/15) 97 | 98 | * Add GodepVersion to Godeps.json file so that as godep changes / adds features / fixes bugs we can know which version of godep most recently wrote out the file. 99 | 100 | #v57 (2016/03/07) 101 | 102 | * Don't use `git rev-parse --show-toplevel` to determine git repo roots as it resolves symlinks: https://github.com/tools/godep/pull/418 103 | 104 | # v56 (2016/02/26) 105 | 106 | * replace path comparisons with case insensitive pathEqual() 107 | * add versionString() to debug output 108 | * Send log output to Stderr 109 | 110 | # v55 2016/02/22 111 | 112 | * re-saved deps to clean out extra stuff (see v54; godep restore; godep save -r=false; rm -rf Godeps; godep save -r). We're still using a workspace with rewrites so users of older go version can still go get this tool. 113 | * Replace simple == with strings.EqualFold in listFiles to avoid problems with case insensitive filesystems ("Code" != "code" when doing a byte by byte comparison) 114 | 115 | # v54 2016/02/22 116 | 117 | * Update some docs around vendor/ 118 | * More precise recording of dependencies. Removed recursive copying of sub directories of a package (precise vendoring). This should allow using `./...` with the go tool for compilation of project using `vendor/`. See https://github.com/tools/godep/pull/415 119 | 120 | # v53 2016/02/11 121 | 122 | * Disable VendorExperiment if a godep workspace already exists. 123 | 124 | # v52 2016/01/27 125 | 126 | * Trim 'rc' out of go version strings when determining major version. 127 | 128 | # v51 2016/01/21 129 | 130 | * Trim 'beta' out of go version strings when determining major version. 131 | 132 | # v50 2016/01/19 133 | 134 | * More verbose output on save -v. 135 | 136 | # v49 2016/01/13 137 | 138 | * Add UK spelling license/licence to the pile + fix up a bunch of typos 139 | * Clarify tag handling in docs 140 | 141 | # v48 2016/01/13 142 | 143 | * Abort restore if there is no $GOPATH set. 144 | 145 | # v47 2016/01/12 146 | 147 | * Dev versions of go should honor the current meaning of GO15VENDOREXPERIMENT 148 | 149 | # v46 2016/01/03 150 | 151 | * Record "devel" when the release is a devel release of go (compiled from git). 152 | 153 | # v45 2015/12/28 154 | 155 | * Upcase windows drive letters before comparing. Fixes #383. 156 | 157 | # v44 2015/12/23 158 | 159 | * Clean package roots when attempting to find a vendor directory so we don't loop forever. 160 | * Fixes 382 161 | 162 | # v43 2015/12/22 163 | 164 | * Better error messages when parsing Godeps.json: Fixes #372 165 | 166 | # v42 2015/12/22 167 | 168 | * Fix a bunch of GO15VENDOREXPERIMENT issues 169 | * Find package directories better. Previously we used build.FindOnly which didn't work the way I expected it to (any dir would work w/o error). 170 | * Set the VendorExperiment bool based on go version as 1.6 defaults to on. 171 | * A bunch of extra debugging for use while sanity checking myself. 172 | * vendor flag for test structs. 173 | * Some tests for vendor/ stuff: 174 | * Basic Test 175 | * Transitive 176 | * Transitive, across GOPATHs + collapse vendor/ directories. 177 | * Should Fix #358 178 | 179 | # v41 2015/12/17 180 | 181 | * Don't rewrite packages outside of the project. This would happen if you specified 182 | an external package for vendoring when you ran `goodep save -r ./... github.com/some/other/package` 183 | 184 | # v40 2015/12/17 185 | 186 | * When downloading a dependency, create the base directory if needed. 187 | 188 | # v39 2015/12/16 189 | 190 | * Record only the major go version (ex. go1.5) instead of the complete string. 191 | 192 | # v38 2015/12/16 193 | 194 | * Replace `go get`, further fix up restore error handling/reporting. 195 | * Fixes #186 196 | * Don't bother restoring/downloading if already done. 197 | 198 | # v37 2015/12/15 199 | 200 | * Change up how download/restore works a little 201 | * Try to load the package after downloading/restoring. Previously 202 | that was done too early in the process. 203 | * make previous verbose output debug output 204 | * report a typed error instead of a string from listPackage so it can 205 | be asserted to provide a nicer error. 206 | * Catch go get errors that say there are no go files found. See code 207 | comment as to why. 208 | * do *all* downloading during download phase. 209 | 210 | # v36 2015/12/14 211 | 212 | * Fixes #358: Using wrong variable. Will add test after release. 213 | 214 | # v35 2015/12/11 215 | 216 | * Fixes #356: Major performance regressions in v34 217 | * Enable cpu profiling via flag on save. 218 | * Cache packages by dir 219 | * Don't do a full import pass on deps for packages in the GOROOT 220 | * create a bit less garbage at times 221 | * Generalize -v & -d flags 222 | 223 | # v34 2015/12/08 224 | 225 | * We now use build.Context to help locate packages only and do our own parsing (via go/ast). 226 | * Fixes reported issues caused by v33 (Removal of `go list`): 227 | * #345: Bug in godep restore 228 | * #346: Fix loading a dot package 229 | * #348: Godep save issue when importing lib/pq 230 | * #350: undefined: build.MultiplePackageError 231 | * #351: stow away helper files 232 | * #353: cannot find package "appengine" 233 | * Don't process imports of `.go` files tagged with the `appengine` build tag. 234 | 235 | # v33 2015/12/07 236 | 237 | * Replace the use of `go list`. This is a large change although all existing tests pass. 238 | * Don't process the imports of `.go` files with the `ignore` build tag. 239 | 240 | # v32 2015/12/02 241 | 242 | * Eval Symlinks in Contains() check. 243 | 244 | # v31 2015/12/02 245 | 246 | * In restore, mention which package had the problem -- @shurcool 247 | 248 | # v30 2015/11/25 249 | 250 | * Add `-t` flag to the `godep get` command. 251 | 252 | # v29 2015/11/17 253 | 254 | * Temp work around to fix issue with LICENSE files. 255 | 256 | # v28 2015/11/09 257 | 258 | * Make `version` an actual command. 259 | 260 | # v27 2015/11/06 261 | 262 | * run command once during restore -v 263 | 264 | # v26 2015/11/05 265 | 266 | * Better fix for the issue fixed in v25: All update paths are now path.Clean()'d 267 | 268 | # v25 2015/11/05 269 | 270 | * `godep update package/` == `godep update package`. Fixes #313 271 | 272 | # v24 2015/11/05 273 | 274 | * Honor -t during update. Fixes #312 275 | 276 | # v23 2015/11/05 277 | 278 | * Do not use --debug to find full revision name for mercurial repositories 279 | 280 | # v22 2015/11/14 281 | 282 | * s/GOVENDOREXPERIMENT/GO15VENDOREXPERIMENT :-( 283 | 284 | # v21 2015/11/13 285 | 286 | * Fix #310: Case insensitive fs issue 287 | 288 | # v20 2015/11/13 289 | 290 | * Attempt to include license files when vendoring. (@client9) 291 | 292 | # v19 2015/11/3 293 | 294 | * Fix conflict error message. Revisions were swapped. Also better selection of package that needs update. 295 | 296 | # v18 2015/10/16 297 | 298 | * Improve error message when trying to save a conflicting revision. 299 | 300 | # v17 2015/10/15 301 | 302 | * Fix for v16 bug. All vcs list commands now produce paths relative to the root of the vcs. 303 | 304 | # v16 2015/10/15 305 | 306 | * Determine repo root using vcs commands and use that instead of dep.dir 307 | 308 | # v15 2015/10/14 309 | 310 | * Update .travis.yml file to do releases to github 311 | 312 | # v14 2015/10/08 313 | 314 | * Don't print out a workspace path when GO15VENDOREXPERIMENT is active. The vendor/ directory is not a valid workspace, so can't be added to your $GOPATH. 315 | 316 | # v13 2015/10/07 317 | 318 | * Do restores in 2 separate steps, first download all deps and then check out the recorded revisions. 319 | * Update Changelog date format 320 | 321 | # v12 2015/09/22 322 | 323 | * Extract errors into separate file. 324 | 325 | # v11 2015/08/22 326 | 327 | * Amend code to pass golint. 328 | 329 | # v10 2015/09/21 330 | 331 | * Analyse vendored package test dependencies. 332 | * Update documentation. 333 | 334 | # v9 2015/09/17 335 | 336 | * Don't save test dependencies by default. 337 | 338 | # v8 2015/09/17 339 | 340 | * Reorganize code. 341 | 342 | # v7 2015/09/09 343 | 344 | * Add verbose flag. 345 | * Skip untracked files. 346 | * Add VCS list command. 347 | 348 | # v6 2015/09/04 349 | 350 | * Revert ignoring testdata directories and instead ignore it while 351 | processing Go files and copy the whole directory unmodified. 352 | 353 | # v5 2015/09/04 354 | 355 | * Fix vcs selection in restore command to work as go get does 356 | 357 | # v4 2015/09/03 358 | 359 | * Remove the deprecated copy option. 360 | 361 | # v3 2015/08/26 362 | 363 | * Ignore testdata directories 364 | 365 | # v2 2015/08/11 366 | 367 | * Include command line packages in the set to copy 368 | 369 | This is a simplification to how we define the behavior 370 | of the save command. Now it has two distinct package 371 | parameters, the "root set" and the "destination", and 372 | they have clearer roles. The packages listed on the 373 | command line form the root set; they and all their 374 | dependencies will be copied into the Godeps directory. 375 | Additionally, the destination (always ".") will form the 376 | initial list of "seen" import paths to exclude from 377 | copying. 378 | 379 | In the common case, the root set is equal to the 380 | destination, so the effective behavior doesn't change. 381 | This is primarily just a simpler definition. However, if 382 | the user specifies a package on the command line that 383 | lives outside of . then that package will be copied. 384 | 385 | As a side effect, there's a simplification to the way we 386 | add packages to the initial "seen" set. Formerly, to 387 | avoid copying dependencies unnecessarily, we would try 388 | to find the root of the VCS repo for each package in the 389 | root set, and mark the import path of the entire repo as 390 | seen. This meant for a repo at path C, if destination 391 | C/S imports C/T, we would not copy C/T into C/S/Godeps. 392 | Now we don't treat the repo root specially, and as 393 | mentioned above, the destination alone is considered 394 | seen. 395 | 396 | This also means we don't require listed packages to be 397 | in VCS unless they're outside of the destination. 398 | 399 | # v1 2015/07/20 400 | 401 | * godep version command 402 | 403 | Output the version as well as some godep runtime information that is 404 | useful for debugging user's issues. 405 | 406 | The version const would be bumped each time a PR is merged into master 407 | to ensure that we'll be able to tell which version someone got when they 408 | did a `go get github.com/tools/godep`. 409 | 410 | # Older changes 411 | 412 | Many and more, see `git log -p` 413 | -------------------------------------------------------------------------------- /save.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "errors" 7 | "fmt" 8 | "go/build" 9 | "io" 10 | "io/ioutil" 11 | "log" 12 | "os" 13 | "path/filepath" 14 | "regexp" 15 | "strings" 16 | 17 | "github.com/kr/fs" 18 | ) 19 | 20 | var cmdSave = &Command{ 21 | Name: "save", 22 | Args: "[-r] [-t] [packages]", 23 | Short: "list and copy dependencies into Godeps", 24 | Long: ` 25 | 26 | Save writes a list of the named packages and their dependencies along 27 | with the exact source control revision of each package, and copies 28 | their source code into a subdirectory. Packages inside "." are excluded 29 | from the list to be copied. 30 | 31 | The list is written to Godeps/Godeps.json, and source code for all 32 | dependencies is copied into either Godeps/_workspace or, if the vendor 33 | experiment is turned on, vendor/. 34 | 35 | The dependency list is a JSON document with the following structure: 36 | 37 | type Godeps struct { 38 | ImportPath string 39 | GoVersion string // Abridged output of 'go version'. 40 | Packages []string // Arguments to godep save, if any. 41 | Deps []struct { 42 | ImportPath string 43 | Comment string // Tag or description of commit. 44 | Rev string // VCS-specific commit ID. 45 | } 46 | } 47 | 48 | Any packages already present in the list will be left unchanged. 49 | To update a dependency to a newer revision, use 'godep update'. 50 | 51 | If -r is given, import statements will be rewritten to refer directly 52 | to the copied source code. This is not compatible with the vendor 53 | experiment. Note that this will not rewrite the statements in the 54 | files outside the project. 55 | 56 | If -t is given, test files (*_test.go files + testdata directories) are 57 | also saved. 58 | 59 | For more about specifying packages, see 'go help packages'. 60 | `, 61 | Run: runSave, 62 | OnlyInGOPATH: true, 63 | } 64 | 65 | var ( 66 | saveR, saveT bool 67 | ) 68 | 69 | func init() { 70 | cmdSave.Flag.BoolVar(&saveR, "r", false, "rewrite import paths") 71 | cmdSave.Flag.BoolVar(&saveT, "t", false, "save test files") 72 | 73 | } 74 | 75 | func runSave(cmd *Command, args []string) { 76 | if VendorExperiment && saveR { 77 | log.Println("flag -r is incompatible with the vendoring experiment") 78 | cmd.UsageExit() 79 | } 80 | err := save(args) 81 | if err != nil { 82 | log.Fatalln(err) 83 | } 84 | } 85 | 86 | func dotPackage() (*build.Package, error) { 87 | dir, err := filepath.Abs(".") 88 | if err != nil { 89 | return nil, err 90 | } 91 | return build.ImportDir(dir, build.FindOnly) 92 | } 93 | 94 | func projectPackages(dDir string, a []*Package) []*Package { 95 | var projPkgs []*Package 96 | dotDir := fmt.Sprintf("%s%c", dDir, filepath.Separator) 97 | for _, p := range a { 98 | pkgDir := fmt.Sprintf("%s%c", p.Dir, filepath.Separator) 99 | if strings.HasPrefix(pkgDir, dotDir) { 100 | projPkgs = append(projPkgs, p) 101 | } 102 | } 103 | return projPkgs 104 | } 105 | 106 | func save(pkgs []string) error { 107 | var err error 108 | dp, err := dotPackage() 109 | if err != nil { 110 | return err 111 | } 112 | debugln("dotPackageImportPath:", dp.ImportPath) 113 | debugln("dotPackageDir:", dp.Dir) 114 | 115 | cv, err := goVersion() 116 | if err != nil { 117 | return err 118 | } 119 | verboseln("Go Version:", cv) 120 | 121 | gold, err := loadDefaultGodepsFile() 122 | if err != nil { 123 | if !os.IsNotExist(err) { 124 | return err 125 | } 126 | verboseln("No old Godeps.json found.") 127 | gold.GoVersion = cv 128 | } 129 | 130 | printVersionWarnings(gold.GoVersion) 131 | if len(gold.GoVersion) == 0 { 132 | gold.GoVersion = majorGoVersion 133 | } else { 134 | majorGoVersion, err = trimGoVersion(gold.GoVersion) 135 | if err != nil { 136 | log.Fatalf("Unable to determine go major version from value specified in %s: %s\n", gold.file(), gold.GoVersion) 137 | } 138 | } 139 | 140 | gnew := &Godeps{ 141 | ImportPath: dp.ImportPath, 142 | GoVersion: gold.GoVersion, 143 | } 144 | 145 | switch len(pkgs) { 146 | case 0: 147 | pkgs = []string{"."} 148 | default: 149 | gnew.Packages = pkgs 150 | } 151 | 152 | verboseln("Finding dependencies for", pkgs) 153 | a, err := LoadPackages(pkgs...) 154 | if err != nil { 155 | return err 156 | } 157 | 158 | for _, p := range a { 159 | verboseln("Found package:", p.ImportPath) 160 | verboseln("\tDeps:", strings.Join(p.Deps, " ")) 161 | } 162 | ppln(a) 163 | 164 | projA := projectPackages(dp.Dir, a) 165 | debugln("Filtered projectPackages") 166 | ppln(projA) 167 | 168 | verboseln("Computing new Godeps.json file") 169 | err = gnew.fill(a, dp.ImportPath) 170 | if err != nil { 171 | return err 172 | } 173 | debugln("New Godeps Filled") 174 | ppln(gnew) 175 | 176 | if gnew.Deps == nil { 177 | gnew.Deps = make([]Dependency, 0) // produce json [], not null 178 | } 179 | gdisk := gnew.copy() 180 | err = carryVersions(&gold, gnew) 181 | if err != nil { 182 | return err 183 | } 184 | 185 | if gold.isOldFile { 186 | // If we are migrating from an old format file, 187 | // we require that the listed version of every 188 | // dependency must be installed in GOPATH, so it's 189 | // available to copy. 190 | if !eqDeps(gnew.Deps, gdisk.Deps) { 191 | return errors.New(strings.TrimSpace(needRestore)) 192 | } 193 | gold = Godeps{} 194 | } 195 | os.Remove("Godeps") // remove regular file if present; ignore error 196 | readme := filepath.Join("Godeps", "Readme") 197 | err = writeFile(readme, strings.TrimSpace(Readme)+"\n") 198 | if err != nil { 199 | log.Println(err) 200 | } 201 | _, err = gnew.save() 202 | if err != nil { 203 | return err 204 | } 205 | 206 | verboseln("Computing diff between old and new deps") 207 | // We use a name starting with "_" so the go tool 208 | // ignores this directory when traversing packages 209 | // starting at the project's root. For example, 210 | // godep go list ./... 211 | srcdir := filepath.FromSlash(strings.Trim(sep, "/")) 212 | rem := subDeps(gold.Deps, gnew.Deps) 213 | ppln(rem) 214 | add := subDeps(gnew.Deps, gold.Deps) 215 | ppln(add) 216 | if len(rem) > 0 { 217 | verboseln("Deps to remove:") 218 | for _, r := range rem { 219 | verboseln("\t", r.ImportPath) 220 | } 221 | verboseln("Removing unused dependencies") 222 | err = removeSrc(srcdir, rem) 223 | if err != nil { 224 | return err 225 | } 226 | } 227 | if len(add) > 0 { 228 | verboseln("Deps to add:") 229 | for _, a := range add { 230 | verboseln("\t", a.ImportPath) 231 | } 232 | verboseln("Adding new dependencies") 233 | err = copySrc(srcdir, add) 234 | if err != nil { 235 | return err 236 | } 237 | } 238 | if !VendorExperiment { 239 | f, _ := filepath.Split(srcdir) 240 | writeVCSIgnore(f) 241 | } 242 | var rewritePaths []string 243 | if saveR { 244 | for _, dep := range gnew.Deps { 245 | rewritePaths = append(rewritePaths, dep.ImportPath) 246 | } 247 | } 248 | verboseln("Rewriting paths (if necessary)") 249 | ppln(rewritePaths) 250 | return rewrite(projA, dp.ImportPath, rewritePaths) 251 | } 252 | 253 | func printVersionWarnings(ov string) { 254 | var warning bool 255 | cv, err := goVersion() 256 | if err != nil { 257 | return 258 | } 259 | // Trim the old version because we may have saved it w/o trimming it 260 | // cv is already trimmed by goVersion() 261 | tov, err := trimGoVersion(ov) 262 | if err != nil { 263 | return 264 | } 265 | 266 | if tov != ov { 267 | log.Printf("WARNING: Recorded go version (%s) with minor version string found.\n", ov) 268 | warning = true 269 | } 270 | if cv != tov { 271 | log.Printf("WARNING: Recorded major go version (%s) and in-use major go version (%s) differ.\n", tov, cv) 272 | warning = true 273 | } 274 | if warning { 275 | log.Println("To record current major go version run `godep update -goversion`.") 276 | } 277 | } 278 | 279 | type revError struct { 280 | ImportPath string 281 | WantRev string 282 | HavePath string 283 | HaveRev string 284 | } 285 | 286 | func (v *revError) Error() string { 287 | return fmt.Sprintf("cannot save %s at revision %s: already have %s at revision %s.\n"+ 288 | "Run `godep update %s' first.", v.ImportPath, v.WantRev, v.HavePath, v.HaveRev, v.HavePath) 289 | } 290 | 291 | // carryVersions copies Rev and Comment from a to b for 292 | // each dependency with an identical ImportPath. For any 293 | // dependency in b that appears to be from the same repo 294 | // as one in a (for example, a parent or child directory), 295 | // the Rev must already match - otherwise it is an error. 296 | func carryVersions(a, b *Godeps) error { 297 | for i := range b.Deps { 298 | err := carryVersion(a, &b.Deps[i]) 299 | if err != nil { 300 | return err 301 | } 302 | } 303 | return nil 304 | } 305 | 306 | func carryVersion(a *Godeps, db *Dependency) error { 307 | // First see if this exact package is already in the list. 308 | for _, da := range a.Deps { 309 | if db.ImportPath == da.ImportPath { 310 | db.Rev = da.Rev 311 | db.Comment = da.Comment 312 | return nil 313 | } 314 | } 315 | // No exact match, check for child or sibling package. 316 | // We can't handle mismatched versions for packages in 317 | // the same repo, so report that as an error. 318 | for _, da := range a.Deps { 319 | if strings.HasPrefix(db.ImportPath, da.ImportPath+"/") || 320 | strings.HasPrefix(da.ImportPath, db.root+"/") { 321 | if da.Rev != db.Rev { 322 | return &revError{ 323 | ImportPath: db.ImportPath, 324 | WantRev: db.Rev, 325 | HavePath: da.ImportPath, 326 | HaveRev: da.Rev, 327 | } 328 | } 329 | } 330 | } 331 | // No related package in the list, must be a new repo. 332 | return nil 333 | } 334 | 335 | // subDeps returns a - b, using ImportPath for equality. 336 | func subDeps(a, b []Dependency) (diff []Dependency) { 337 | Diff: 338 | for _, da := range a { 339 | for _, db := range b { 340 | if da.ImportPath == db.ImportPath { 341 | continue Diff 342 | } 343 | } 344 | diff = append(diff, da) 345 | } 346 | return diff 347 | } 348 | 349 | func removeSrc(srcdir string, deps []Dependency) error { 350 | for _, dep := range deps { 351 | path := filepath.FromSlash(dep.ImportPath) 352 | err := os.RemoveAll(filepath.Join(srcdir, path)) 353 | if err != nil { 354 | return err 355 | } 356 | } 357 | return nil 358 | } 359 | 360 | func copySrc(dir string, deps []Dependency) error { 361 | // mapping to see if we visited a parent directory already 362 | visited := make(map[string]bool) 363 | ok := true 364 | for _, dep := range deps { 365 | debugln("copySrc for", dep.ImportPath) 366 | srcdir := filepath.Join(dep.ws, "src") 367 | rel, err := filepath.Rel(srcdir, dep.dir) 368 | debugln("srcdir", srcdir) 369 | debugln("rel", rel) 370 | debugln("err", err) 371 | if err != nil { // this should never happen 372 | return err 373 | } 374 | dstpkgroot := filepath.Join(dir, rel) 375 | err = os.RemoveAll(dstpkgroot) 376 | if err != nil { 377 | log.Println(err) 378 | ok = false 379 | } 380 | 381 | // copy actual dependency 382 | vf := dep.vcs.listFiles(dep.dir) 383 | debugln("vf", vf) 384 | w := fs.Walk(dep.dir) 385 | for w.Step() { 386 | err = copyPkgFile(vf, dir, srcdir, w) 387 | if err != nil { 388 | log.Println(err) 389 | ok = false 390 | } 391 | } 392 | 393 | // Look for legal files in root 394 | // some packages are imports as a sub-package but license info 395 | // is at root: exampleorg/common has license file in exampleorg 396 | // 397 | if dep.ImportPath == dep.root { 398 | // we are already at root 399 | continue 400 | } 401 | 402 | // prevent copying twice This could happen if we have 403 | // two subpackages listed someorg/common and 404 | // someorg/anotherpack which has their license in 405 | // the parent dir of someorg 406 | rootdir := filepath.Join(srcdir, filepath.FromSlash(dep.root)) 407 | if visited[rootdir] { 408 | continue 409 | } 410 | visited[rootdir] = true 411 | vf = dep.vcs.listFiles(rootdir) 412 | w = fs.Walk(rootdir) 413 | for w.Step() { 414 | fname := filepath.Base(w.Path()) 415 | if IsLegalFile(fname) && !strings.Contains(w.Path(), sep) { 416 | err = copyPkgFile(vf, dir, srcdir, w) 417 | if err != nil { 418 | log.Println(err) 419 | ok = false 420 | } 421 | } 422 | } 423 | } 424 | 425 | if !ok { 426 | return errorCopyingSourceCode 427 | } 428 | 429 | return nil 430 | } 431 | 432 | func copyPkgFile(vf vcsFiles, dstroot, srcroot string, w *fs.Walker) error { 433 | if w.Err() != nil { 434 | return w.Err() 435 | } 436 | name := w.Stat().Name() 437 | if w.Stat().IsDir() { 438 | if name[0] == '.' || name[0] == '_' || (!saveT && name == "testdata") { 439 | // Skip directories starting with '.' or '_' or 440 | // 'testdata' (last is only skipped if saveT is false) 441 | w.SkipDir() 442 | } 443 | return nil 444 | } 445 | rel, err := filepath.Rel(srcroot, w.Path()) 446 | if err != nil { // this should never happen 447 | return err 448 | } 449 | if !saveT && strings.HasSuffix(name, "_test.go") { 450 | if verbose { 451 | log.Printf("save: skipping test file: %s", w.Path()) 452 | } 453 | return nil 454 | } 455 | if !vf.Contains(w.Path()) { 456 | if verbose { 457 | log.Printf("save: skipping untracked file: %s", w.Path()) 458 | } 459 | return nil 460 | } 461 | return copyFile(filepath.Join(dstroot, rel), w.Path()) 462 | } 463 | 464 | // copyFile copies a regular file from src to dst. 465 | // dst is opened with os.Create. 466 | // If the file name ends with .go, 467 | // copyFile strips canonical import path annotations. 468 | // These are comments of the form: 469 | // package foo // import "bar/foo" 470 | // package foo /* import "bar/foo" */ 471 | func copyFile(dst, src string) error { 472 | err := os.MkdirAll(filepath.Dir(dst), 0777) 473 | if err != nil { 474 | return err 475 | } 476 | 477 | linkDst, err := os.Readlink(src) 478 | if err == nil { 479 | return os.Symlink(linkDst, dst) 480 | } 481 | 482 | si, err := stat(src) 483 | if err != nil { 484 | return err 485 | } 486 | 487 | r, err := os.Open(src) 488 | if err != nil { 489 | return err 490 | } 491 | defer r.Close() 492 | 493 | w, err := os.Create(dst) 494 | if err != nil { 495 | return err 496 | } 497 | if err := os.Chmod(dst, si.Mode()); err != nil { 498 | return err 499 | } 500 | 501 | if strings.HasSuffix(dst, ".go") { 502 | debugln("Copy Without Import Comment", w, r) 503 | err = copyWithoutImportComment(w, r) 504 | } else { 505 | debugln("Copy (plain)", w, r) 506 | _, err = io.Copy(w, r) 507 | } 508 | err1 := w.Close() 509 | if err == nil { 510 | err = err1 511 | } 512 | 513 | return err 514 | } 515 | 516 | func copyWithoutImportComment(w io.Writer, r io.Reader) error { 517 | b := bufio.NewReader(r) 518 | for { 519 | l, err := b.ReadBytes('\n') 520 | eof := err == io.EOF 521 | if err != nil && err != io.EOF { 522 | return err 523 | } 524 | 525 | // If we have data then write it out... 526 | if len(l) > 0 { 527 | // Strip off \n if it exists because stripImportComment 528 | _, err := w.Write(append(stripImportComment(bytes.TrimRight(l, "\n")), '\n')) 529 | if err != nil { 530 | return err 531 | } 532 | } 533 | 534 | if eof { 535 | return nil 536 | } 537 | } 538 | } 539 | 540 | const ( 541 | importAnnotation = `import\s+(?:"[^"]*"|` + "`[^`]*`" + `)` 542 | importComment = `(?://\s*` + importAnnotation + `\s*$|/\*\s*` + importAnnotation + `\s*\*/)` 543 | ) 544 | 545 | var ( 546 | importCommentRE = regexp.MustCompile(`^\s*(package\s+\w+)\s+` + importComment + `(.*)`) 547 | pkgPrefix = []byte("package ") 548 | ) 549 | 550 | // stripImportComment returns line with its import comment removed. 551 | // If s is not a package statement containing an import comment, 552 | // it is returned unaltered. 553 | // FIXME: expects lines w/o a \n at the end 554 | // See also http://golang.org/s/go14customimport. 555 | func stripImportComment(line []byte) []byte { 556 | if !bytes.HasPrefix(line, pkgPrefix) { 557 | // Fast path; this will skip all but one line in the file. 558 | // This assumes there is no whitespace before the keyword. 559 | return line 560 | } 561 | if m := importCommentRE.FindSubmatch(line); m != nil { 562 | return append(m[1], m[2]...) 563 | } 564 | return line 565 | } 566 | 567 | // Func writeVCSIgnore writes "ignore" files inside dir for known VCSs, 568 | // so that dir/pkg and dir/bin don't accidentally get committed. 569 | // It logs any errors it encounters. 570 | func writeVCSIgnore(dir string) { 571 | // Currently git is the only VCS for which we know how to do this. 572 | // Mercurial and Bazaar have similar mechanisms, but they apparently 573 | // require writing files outside of dir. 574 | const ignore = "/pkg\n/bin\n" 575 | name := filepath.Join(dir, ".gitignore") 576 | err := writeFile(name, ignore) 577 | if err != nil { 578 | log.Println(err) 579 | } 580 | } 581 | 582 | // writeFile is like ioutil.WriteFile but it creates 583 | // intermediate directories with os.MkdirAll. 584 | func writeFile(name, body string) error { 585 | err := os.MkdirAll(filepath.Dir(name), 0777) 586 | if err != nil { 587 | return err 588 | } 589 | return ioutil.WriteFile(name, []byte(body), 0666) 590 | } 591 | 592 | const ( 593 | // Readme contains the README text. 594 | Readme = ` 595 | This directory tree is generated automatically by godep. 596 | 597 | Please do not edit. 598 | 599 | See https://github.com/tools/godep for more information. 600 | ` 601 | needRestore = ` 602 | mismatched versions while migrating 603 | 604 | It looks like you are switching from the old Godeps format 605 | (from flag -copy=false). The old format is just a file; it 606 | doesn't contain source code. For this migration, godep needs 607 | the appropriate version of each dependency to be installed in 608 | GOPATH, so that the source code is available to copy. 609 | 610 | To fix this, run 'godep restore'. 611 | ` 612 | ) 613 | -------------------------------------------------------------------------------- /list.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "go/build" 7 | "go/parser" 8 | "go/token" 9 | "log" 10 | "os" 11 | "path/filepath" 12 | "regexp" 13 | "strconv" 14 | "strings" 15 | "unicode" 16 | 17 | pathpkg "path" 18 | ) 19 | 20 | var ( 21 | gorootSrc = filepath.Join(build.Default.GOROOT, "src") 22 | ignoreTags = []string{"appengine", "ignore"} //TODO: appengine is a special case for now: https://github.com/tools/godep/issues/353 23 | versionMatch = regexp.MustCompile(`\Ago\d+\.\d+\z`) 24 | versionNegativeMatch = regexp.MustCompile(`\A\!go\d+\.\d+\z`) 25 | ) 26 | 27 | type errorMissingDep struct { 28 | i, dir string // import, dir 29 | } 30 | 31 | func (e errorMissingDep) Error() string { 32 | return "Unable to find dependent package " + e.i + " in context of " + e.dir 33 | } 34 | 35 | // packageContext is used to track an import and which package imported it. 36 | type packageContext struct { 37 | pkg *build.Package // package that imports the import 38 | imp string // import 39 | } 40 | 41 | // depScanner tracks the processed and to be processed packageContexts 42 | type depScanner struct { 43 | processed []packageContext 44 | todo []packageContext 45 | } 46 | 47 | // Next package and import to process 48 | func (ds *depScanner) Next() (*build.Package, string) { 49 | c := ds.todo[0] 50 | ds.processed = append(ds.processed, c) 51 | ds.todo = ds.todo[1:] 52 | return c.pkg, c.imp 53 | } 54 | 55 | // Continue looping? 56 | func (ds *depScanner) Continue() bool { 57 | return len(ds.todo) > 0 58 | } 59 | 60 | // Add a package and imports to the depScanner. Skips already processed/pending package/import combos 61 | func (ds *depScanner) Add(pkg *build.Package, imports ...string) { 62 | NextImport: 63 | for _, i := range imports { 64 | if i == "C" { 65 | i = "runtime/cgo" 66 | } 67 | for _, epc := range ds.processed { 68 | if pkg.Dir == epc.pkg.Dir && i == epc.imp { 69 | debugln("ctxts epc.pkg.Dir == pkg.Dir && i == epc.imp, skipping", epc.pkg.Dir, i) 70 | continue NextImport 71 | } 72 | } 73 | for _, epc := range ds.todo { 74 | if pkg.Dir == epc.pkg.Dir && i == epc.imp { 75 | debugln("ctxts epc.pkg.Dir == pkg.Dir && i == epc.imp, skipping", epc.pkg.Dir, i) 76 | continue NextImport 77 | } 78 | } 79 | pc := packageContext{pkg, i} 80 | debugln("Adding pc:", pc.pkg.Dir, pc.imp) 81 | ds.todo = append(ds.todo, pc) 82 | } 83 | } 84 | 85 | var ( 86 | pkgCache = make(map[string]*build.Package) // dir => *build.Package 87 | ) 88 | 89 | // returns the package in dir either from a cache or by importing it and then caching it 90 | func fullPackageInDir(dir string) (*build.Package, error) { 91 | var err error 92 | pkg, ok := pkgCache[dir] 93 | if !ok { 94 | pkg, _ = build.ImportDir(dir, build.FindOnly) 95 | if pkg.Goroot { 96 | pkg, err = build.ImportDir(pkg.Dir, 0) 97 | } else { 98 | err = fillPackage(pkg) 99 | } 100 | if err == nil { 101 | pkgCache[dir] = pkg 102 | } 103 | } 104 | return pkg, err 105 | } 106 | 107 | // listPackage specified by path 108 | func listPackage(path string) (*Package, error) { 109 | debugln("listPackage", path) 110 | var lp *build.Package 111 | dir, err := findDirForPath(path, nil) 112 | if err != nil { 113 | return nil, err 114 | } 115 | lp, err = fullPackageInDir(dir) 116 | p := &Package{ 117 | Dir: lp.Dir, 118 | Root: lp.Root, 119 | ImportPath: lp.ImportPath, 120 | XTestImports: lp.XTestImports, 121 | TestImports: lp.TestImports, 122 | GoFiles: lp.GoFiles, 123 | CgoFiles: lp.CgoFiles, 124 | TestGoFiles: lp.TestGoFiles, 125 | XTestGoFiles: lp.XTestGoFiles, 126 | IgnoredGoFiles: lp.IgnoredGoFiles, 127 | } 128 | p.Standard = lp.Goroot && lp.ImportPath != "" && !strings.Contains(lp.ImportPath, ".") 129 | if err != nil || p.Standard { 130 | return p, err 131 | } 132 | debugln("Looking For Package:", path, "in", dir) 133 | ppln(lp) 134 | 135 | ds := depScanner{} 136 | ds.Add(lp, lp.Imports...) 137 | for ds.Continue() { 138 | ip, i := ds.Next() 139 | 140 | debugf("Processing import %s for %s\n", i, ip.Dir) 141 | pdir, err := findDirForPath(i, ip) 142 | if err != nil { 143 | return nil, err 144 | } 145 | dp, err := fullPackageInDir(pdir) 146 | if err != nil { // This really should happen in this context though 147 | ppln(err) 148 | return nil, errorMissingDep{i: i, dir: ip.Dir} 149 | } 150 | ppln(dp) 151 | if !dp.Goroot { 152 | // Don't bother adding packages in GOROOT to the dependency scanner, they don't import things from outside of it. 153 | ds.Add(dp, dp.Imports...) 154 | } 155 | debugln("lp:") 156 | ppln(lp) 157 | debugln("ip:") 158 | ppln(ip) 159 | if lp == ip { 160 | debugln("lp == ip") 161 | p.Imports = append(p.Imports, dp.ImportPath) 162 | } 163 | p.Deps = append(p.Deps, dp.ImportPath) 164 | p.Dependencies = addDependency(p.Dependencies, dp) 165 | } 166 | p.Imports = uniq(p.Imports) 167 | p.Deps = uniq(p.Deps) 168 | debugln("Done Looking For Package:", path, "in", dir) 169 | ppln(p) 170 | return p, nil 171 | } 172 | 173 | func addDependency(deps []build.Package, d *build.Package) []build.Package { 174 | for i := range deps { 175 | if deps[i].Dir == d.Dir { 176 | return deps 177 | } 178 | } 179 | return append(deps, *d) 180 | } 181 | 182 | // finds the directory for the given import path in the context of the provided build.Package (if provided) 183 | func findDirForPath(path string, ip *build.Package) (string, error) { 184 | debugln("findDirForPath", path, ip) 185 | var search []string 186 | 187 | if build.IsLocalImport(path) { 188 | dir := path 189 | if !filepath.IsAbs(dir) { 190 | if abs, err := filepath.Abs(dir); err == nil { 191 | // interpret relative to current directory 192 | dir = abs 193 | } 194 | } 195 | return dir, nil 196 | } 197 | 198 | // We need to check to see if the import exists in vendor/ folders up the hierarchy of the importing package 199 | if VendorExperiment && ip != nil { 200 | debugln("resolving vendor posibilities:", ip.Dir, ip.Root) 201 | cr := cleanPath(ip.Root) 202 | 203 | for base := cleanPath(ip.Dir); !pathEqual(base, cr); base = cleanPath(filepath.Dir(base)) { 204 | s := filepath.Join(base, "vendor", path) 205 | debugln("Adding search dir:", s) 206 | search = append(search, s) 207 | } 208 | } 209 | 210 | for _, base := range build.Default.SrcDirs() { 211 | search = append(search, filepath.Join(base, path)) 212 | } 213 | 214 | for _, dir := range search { 215 | debugln("searching", dir) 216 | fi, err := stat(dir) 217 | if err == nil && fi.IsDir() { 218 | return dir, nil 219 | } 220 | } 221 | 222 | return "", errPackageNotFound{path} 223 | } 224 | 225 | type statEntry struct { 226 | fi os.FileInfo 227 | err error 228 | } 229 | 230 | var ( 231 | statCache = make(map[string]statEntry) 232 | ) 233 | 234 | func clearStatCache() { 235 | statCache = make(map[string]statEntry) 236 | } 237 | 238 | func stat(p string) (os.FileInfo, error) { 239 | if e, ok := statCache[p]; ok { 240 | return e.fi, e.err 241 | } 242 | fi, err := os.Stat(p) 243 | statCache[p] = statEntry{fi, err} 244 | return fi, err 245 | } 246 | 247 | // fillPackage full of info. Assumes p.Dir is set at a minimum 248 | func fillPackage(p *build.Package) error { 249 | if p.Goroot { 250 | return nil 251 | } 252 | 253 | if p.SrcRoot == "" { 254 | for _, base := range build.Default.SrcDirs() { 255 | if strings.HasPrefix(p.Dir, base) { 256 | p.SrcRoot = base 257 | } 258 | } 259 | } 260 | 261 | if p.SrcRoot == "" { 262 | return errors.New("Unable to find SrcRoot for package " + p.ImportPath) 263 | } 264 | 265 | if p.Root == "" { 266 | p.Root = filepath.Dir(p.SrcRoot) 267 | } 268 | 269 | var buildMatch = "+build " 270 | var buildFieldSplit = func(r rune) bool { 271 | return unicode.IsSpace(r) || r == ',' 272 | } 273 | 274 | debugln("Filling package:", p.ImportPath, "from", p.Dir) 275 | gofiles, err := filepath.Glob(filepath.Join(p.Dir, "*.go")) 276 | if err != nil { 277 | debugln("Error globbing", err) 278 | return err 279 | } 280 | 281 | if len(gofiles) == 0 { 282 | return &build.NoGoError{Dir: p.Dir} 283 | } 284 | 285 | var testImports []string 286 | var imports []string 287 | NextFile: 288 | for _, file := range gofiles { 289 | debugln(file) 290 | pf, err := parser.ParseFile(token.NewFileSet(), file, nil, parser.ImportsOnly|parser.ParseComments) 291 | if err != nil { 292 | return err 293 | } 294 | testFile := strings.HasSuffix(file, "_test.go") 295 | fname := filepath.Base(file) 296 | for _, c := range pf.Comments { 297 | ct := c.Text() 298 | if i := strings.Index(ct, buildMatch); i != -1 { 299 | for _, t := range strings.FieldsFunc(ct[i+len(buildMatch):], buildFieldSplit) { 300 | for _, tag := range ignoreTags { 301 | if t == tag { 302 | p.IgnoredGoFiles = append(p.IgnoredGoFiles, fname) 303 | continue NextFile 304 | } 305 | } 306 | 307 | if versionMatch.MatchString(t) && !isSameOrNewer(t, majorGoVersion) { 308 | debugln("Adding", fname, "to ignored list because of version tag", t) 309 | p.IgnoredGoFiles = append(p.IgnoredGoFiles, fname) 310 | continue NextFile 311 | } 312 | if versionNegativeMatch.MatchString(t) && isSameOrNewer(t[1:], majorGoVersion) { 313 | debugln("Adding", fname, "to ignored list because of version tag", t) 314 | p.IgnoredGoFiles = append(p.IgnoredGoFiles, fname) 315 | continue NextFile 316 | } 317 | } 318 | } 319 | } 320 | if testFile { 321 | p.TestGoFiles = append(p.TestGoFiles, fname) 322 | } else { 323 | p.GoFiles = append(p.GoFiles, fname) 324 | } 325 | for _, is := range pf.Imports { 326 | name, err := strconv.Unquote(is.Path.Value) 327 | if err != nil { 328 | return err // can't happen? 329 | } 330 | if testFile { 331 | testImports = append(testImports, name) 332 | } else { 333 | imports = append(imports, name) 334 | } 335 | } 336 | } 337 | imports = uniq(imports) 338 | testImports = uniq(testImports) 339 | p.Imports = imports 340 | p.TestImports = testImports 341 | return nil 342 | } 343 | 344 | // All of the following functions were vendored from go proper. Locations are noted in comments, but may change in future Go versions. 345 | 346 | // importPaths returns the import paths to use for the given command line. 347 | // $GOROOT/src/cmd/main.go:366 348 | func importPaths(args []string) []string { 349 | debugf("importPathsNoDotExpansion(%q) == ", args) 350 | args = importPathsNoDotExpansion(args) 351 | debugf("%q\n", args) 352 | var out []string 353 | for _, a := range args { 354 | if strings.Contains(a, "...") { 355 | if build.IsLocalImport(a) { 356 | debugf("build.IsLocalImport(%q) == true\n", a) 357 | pkgs := allPackagesInFS(a) 358 | debugf("allPackagesInFS(%q) == %q\n", a, pkgs) 359 | out = append(out, pkgs...) 360 | } else { 361 | debugf("build.IsLocalImport(%q) == false\n", a) 362 | pkgs := allPackages(a) 363 | debugf("allPackages(%q) == %q\n", a, pkgs) 364 | out = append(out, allPackages(a)...) 365 | } 366 | continue 367 | } 368 | out = append(out, a) 369 | } 370 | return out 371 | } 372 | 373 | // importPathsNoDotExpansion returns the import paths to use for the given 374 | // command line, but it does no ... expansion. 375 | // $GOROOT/src/cmd/main.go:332 376 | func importPathsNoDotExpansion(args []string) []string { 377 | if len(args) == 0 { 378 | return []string{"."} 379 | } 380 | var out []string 381 | for _, a := range args { 382 | // Arguments are supposed to be import paths, but 383 | // as a courtesy to Windows developers, rewrite \ to / 384 | // in command-line arguments. Handles .\... and so on. 385 | if filepath.Separator == '\\' { 386 | a = strings.Replace(a, `\`, `/`, -1) 387 | } 388 | 389 | // Put argument in canonical form, but preserve leading ./. 390 | if strings.HasPrefix(a, "./") { 391 | a = "./" + pathpkg.Clean(a) 392 | if a == "./." { 393 | a = "." 394 | } 395 | } else { 396 | a = pathpkg.Clean(a) 397 | } 398 | if a == "all" || a == "std" || a == "cmd" { 399 | out = append(out, allPackages(a)...) 400 | continue 401 | } 402 | out = append(out, a) 403 | } 404 | return out 405 | } 406 | 407 | // allPackagesInFS is like allPackages but is passed a pattern 408 | // beginning ./ or ../, meaning it should scan the tree rooted 409 | // at the given directory. There are ... in the pattern too. 410 | // $GOROOT/src/cmd/main.go:620 411 | func allPackagesInFS(pattern string) []string { 412 | pkgs := matchPackagesInFS(pattern) 413 | if len(pkgs) == 0 { 414 | fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern) 415 | } 416 | return pkgs 417 | } 418 | 419 | // allPackages returns all the packages that can be found 420 | // under the $GOPATH directories and $GOROOT matching pattern. 421 | // The pattern is either "all" (all packages), "std" (standard packages), 422 | // "cmd" (standard commands), or a path including "...". 423 | // $GOROOT/src/cmd/main.go:542 424 | func allPackages(pattern string) []string { 425 | pkgs := matchPackages(pattern) 426 | if len(pkgs) == 0 { 427 | fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern) 428 | } 429 | return pkgs 430 | } 431 | 432 | // $GOROOT/src/cmd/main.go:554 433 | // This has been changed to not use build.ImportDir 434 | func matchPackages(pattern string) []string { 435 | match := func(string) bool { return true } 436 | treeCanMatch := func(string) bool { return true } 437 | if pattern != "all" && pattern != "std" && pattern != "cmd" { 438 | match = matchPattern(pattern) 439 | treeCanMatch = treeCanMatchPattern(pattern) 440 | } 441 | 442 | have := map[string]bool{ 443 | "builtin": true, // ignore pseudo-package that exists only for documentation 444 | } 445 | if !build.Default.CgoEnabled { 446 | have["runtime/cgo"] = true // ignore during walk 447 | } 448 | var pkgs []string 449 | 450 | for _, src := range build.Default.SrcDirs() { 451 | if (pattern == "std" || pattern == "cmd") && src != gorootSrc { 452 | continue 453 | } 454 | src = filepath.Clean(src) + string(filepath.Separator) 455 | root := src 456 | if pattern == "cmd" { 457 | root += "cmd" + string(filepath.Separator) 458 | } 459 | filepath.Walk(root, func(path string, fi os.FileInfo, err error) error { 460 | if err != nil || !fi.IsDir() || path == src { 461 | return nil 462 | } 463 | 464 | // Avoid .foo, _foo, and testdata directory trees. 465 | _, elem := filepath.Split(path) 466 | if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" { 467 | return filepath.SkipDir 468 | } 469 | 470 | name := filepath.ToSlash(path[len(src):]) 471 | if pattern == "std" && (strings.Contains(name, ".") || name == "cmd") { 472 | // The name "std" is only the standard library. 473 | // If the name has a dot, assume it's a domain name for go get, 474 | // and if the name is cmd, it's the root of the command tree. 475 | return filepath.SkipDir 476 | } 477 | if !treeCanMatch(name) { 478 | return filepath.SkipDir 479 | } 480 | if have[name] { 481 | return nil 482 | } 483 | have[name] = true 484 | if !match(name) { 485 | return nil 486 | } 487 | 488 | ap, err := filepath.Abs(path) 489 | if err != nil { 490 | return nil 491 | } 492 | if _, err = fullPackageInDir(ap); err != nil { 493 | debugf("matchPackage(%q) ap=%q Error: %q\n", ap, pattern, err) 494 | if _, noGo := err.(*build.NoGoError); noGo { 495 | return nil 496 | } 497 | } 498 | pkgs = append(pkgs, name) 499 | return nil 500 | }) 501 | } 502 | return pkgs 503 | } 504 | 505 | // treeCanMatchPattern(pattern)(name) reports whether 506 | // name or children of name can possibly match pattern. 507 | // Pattern is the same limited glob accepted by matchPattern. 508 | // $GOROOT/src/cmd/main.go:527 509 | func treeCanMatchPattern(pattern string) func(name string) bool { 510 | wildCard := false 511 | if i := strings.Index(pattern, "..."); i >= 0 { 512 | wildCard = true 513 | pattern = pattern[:i] 514 | } 515 | return func(name string) bool { 516 | return len(name) <= len(pattern) && hasPathPrefix(pattern, name) || 517 | wildCard && strings.HasPrefix(name, pattern) 518 | } 519 | } 520 | 521 | // hasPathPrefix reports whether the path s begins with the 522 | // elements in prefix. 523 | // $GOROOT/src/cmd/main.go:489 524 | func hasPathPrefix(s, prefix string) bool { 525 | switch { 526 | default: 527 | return false 528 | case len(s) == len(prefix): 529 | return s == prefix 530 | case len(s) > len(prefix): 531 | if prefix != "" && prefix[len(prefix)-1] == '/' { 532 | return strings.HasPrefix(s, prefix) 533 | } 534 | return s[len(prefix)] == '/' && s[:len(prefix)] == prefix 535 | } 536 | } 537 | 538 | // $GOROOT/src/cmd/go/main.go:631 539 | // This has been changed to not use build.ImportDir 540 | func matchPackagesInFS(pattern string) []string { 541 | // Find directory to begin the scan. 542 | // Could be smarter but this one optimization 543 | // is enough for now, since ... is usually at the 544 | // end of a path. 545 | i := strings.Index(pattern, "...") 546 | dir, _ := pathpkg.Split(pattern[:i]) 547 | 548 | // pattern begins with ./ or ../. 549 | // path.Clean will discard the ./ but not the ../. 550 | // We need to preserve the ./ for pattern matching 551 | // and in the returned import paths. 552 | prefix := "" 553 | if strings.HasPrefix(pattern, "./") { 554 | prefix = "./" 555 | } 556 | match := matchPattern(pattern) 557 | 558 | var pkgs []string 559 | filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error { 560 | if err != nil || !fi.IsDir() { 561 | return nil 562 | } 563 | if path == dir { 564 | // filepath.Walk starts at dir and recurses. For the recursive case, 565 | // the path is the result of filepath.Join, which calls filepath.Clean. 566 | // The initial case is not Cleaned, though, so we do this explicitly. 567 | // 568 | // This converts a path like "./io/" to "io". Without this step, running 569 | // "cd $GOROOT/src; go list ./io/..." would incorrectly skip the io 570 | // package, because prepending the prefix "./" to the unclean path would 571 | // result in "././io", and match("././io") returns false. 572 | path = filepath.Clean(path) 573 | } 574 | 575 | // Avoid .foo, _foo, and testdata directory trees, but do not avoid "." or "..". 576 | _, elem := filepath.Split(path) 577 | dot := strings.HasPrefix(elem, ".") && elem != "." && elem != ".." 578 | if dot || strings.HasPrefix(elem, "_") || elem == "testdata" { 579 | return filepath.SkipDir 580 | } 581 | 582 | name := prefix + filepath.ToSlash(path) 583 | if !match(name) { 584 | return nil 585 | } 586 | ap, err := filepath.Abs(path) 587 | if err != nil { 588 | return nil 589 | } 590 | if _, err = fullPackageInDir(ap); err != nil { 591 | debugf("matchPackageInFS(%q) ap=%q Error: %q\n", ap, pattern, err) 592 | if _, noGo := err.(*build.NoGoError); !noGo { 593 | log.Print(err) 594 | } 595 | return nil 596 | } 597 | pkgs = append(pkgs, name) 598 | return nil 599 | }) 600 | return pkgs 601 | } 602 | -------------------------------------------------------------------------------- /update_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "io/ioutil" 6 | "log" 7 | "os" 8 | "path/filepath" 9 | "reflect" 10 | "testing" 11 | ) 12 | 13 | func TestUpdate(t *testing.T) { 14 | var cases = []struct { 15 | cwd string 16 | args []string 17 | vendor bool 18 | start []*node 19 | want []*node 20 | wdep Godeps 21 | werr bool 22 | }{ 23 | { // 0 - simple case, update one dependency 24 | cwd: "C", 25 | args: []string{"D"}, 26 | start: []*node{ 27 | { 28 | "D", 29 | "", 30 | []*node{ 31 | {"main.go", pkg("D") + decl("D1"), nil}, 32 | {"+git", "D1", nil}, 33 | {"main.go", pkg("D") + decl("D2"), nil}, 34 | {"+git", "D2", nil}, 35 | }, 36 | }, 37 | { 38 | "C", 39 | "", 40 | []*node{ 41 | {"main.go", pkg("main", "D"), nil}, 42 | {"Godeps/Godeps.json", godeps("C", "D", "D1"), nil}, 43 | {"Godeps/_workspace/src/D/main.go", pkg("D") + decl("D1"), nil}, 44 | {"+git", "", nil}, 45 | }, 46 | }, 47 | }, 48 | want: []*node{ 49 | {"C/Godeps/_workspace/src/D/main.go", pkg("D") + decl("D2"), nil}, 50 | }, 51 | wdep: Godeps{ 52 | ImportPath: "C", 53 | Deps: []Dependency{ 54 | {ImportPath: "D", Comment: "D2"}, 55 | }, 56 | }, 57 | }, 58 | { // 1 - simple case, update one dependency, trailing slash 59 | cwd: "C", 60 | args: []string{"D/"}, 61 | start: []*node{ 62 | { 63 | "D", 64 | "", 65 | []*node{ 66 | {"main.go", pkg("D") + decl("D1"), nil}, 67 | {"+git", "D1", nil}, 68 | {"main.go", pkg("D") + decl("D2"), nil}, 69 | {"+git", "D2", nil}, 70 | }, 71 | }, 72 | { 73 | "C", 74 | "", 75 | []*node{ 76 | {"main.go", pkg("main", "D"), nil}, 77 | {"Godeps/Godeps.json", godeps("C", "D", "D1"), nil}, 78 | {"Godeps/_workspace/src/D/main.go", pkg("D") + decl("D1"), nil}, 79 | {"+git", "", nil}, 80 | }, 81 | }, 82 | }, 83 | want: []*node{ 84 | {"C/Godeps/_workspace/src/D/main.go", pkg("D") + decl("D2"), nil}, 85 | }, 86 | wdep: Godeps{ 87 | ImportPath: "C", 88 | Deps: []Dependency{ 89 | {ImportPath: "D", Comment: "D2"}, 90 | }, 91 | }, 92 | }, 93 | { // 2 - update one dependency, keep other one, no rewrite 94 | cwd: "C", 95 | args: []string{"D"}, 96 | start: []*node{ 97 | { 98 | "D", 99 | "", 100 | []*node{ 101 | {"main.go", pkg("D", "E") + decl("D1"), nil}, 102 | {"+git", "D1", nil}, 103 | {"main.go", pkg("D", "E") + decl("D2"), nil}, 104 | {"+git", "D2", nil}, 105 | }, 106 | }, 107 | { 108 | "E", 109 | "", 110 | []*node{ 111 | {"main.go", pkg("E") + decl("E1"), nil}, 112 | {"+git", "E1", nil}, 113 | {"main.go", pkg("E") + decl("E2"), nil}, 114 | {"+git", "E2", nil}, 115 | }, 116 | }, 117 | { 118 | "C", 119 | "", 120 | []*node{ 121 | {"main.go", pkg("main", "D", "E"), nil}, 122 | {"Godeps/Godeps.json", godeps("C", "D", "D1", "E", "E1"), nil}, 123 | {"Godeps/_workspace/src/D/main.go", pkg("D", "E") + decl("D1"), nil}, 124 | {"Godeps/_workspace/src/E/main.go", pkg("E") + decl("E1"), nil}, 125 | {"+git", "", nil}, 126 | }, 127 | }, 128 | }, 129 | want: []*node{ 130 | {"C/Godeps/_workspace/src/D/main.go", pkg("D", "E") + decl("D2"), nil}, 131 | {"C/Godeps/_workspace/src/E/main.go", pkg("E") + decl("E1"), nil}, 132 | }, 133 | wdep: Godeps{ 134 | ImportPath: "C", 135 | Deps: []Dependency{ 136 | {ImportPath: "D", Comment: "D2"}, 137 | {ImportPath: "E", Comment: "E1"}, 138 | }, 139 | }, 140 | }, 141 | { // 3 - update one dependency, keep other one, with rewrite 142 | cwd: "C", 143 | args: []string{"D"}, 144 | start: []*node{ 145 | { 146 | "D", 147 | "", 148 | []*node{ 149 | {"main.go", pkg("D", "E") + decl("D1"), nil}, 150 | {"+git", "D1", nil}, 151 | {"main.go", pkg("D", "E") + decl("D2"), nil}, 152 | {"+git", "D2", nil}, 153 | }, 154 | }, 155 | { 156 | "E", 157 | "", 158 | []*node{ 159 | {"main.go", pkg("E") + decl("E1"), nil}, 160 | {"+git", "E1", nil}, 161 | {"main.go", pkg("E") + decl("E2"), nil}, 162 | {"+git", "E2", nil}, 163 | }, 164 | }, 165 | { 166 | "C", 167 | "", 168 | []*node{ 169 | {"main.go", pkg("main", "C/Godeps/_workspace/src/D", "C/Godeps/_workspace/src/E"), nil}, 170 | {"Godeps/Godeps.json", godeps("C", "D", "D1", "E", "E1"), nil}, 171 | {"Godeps/_workspace/src/D/main.go", pkg("D", "C/Godeps/_workspace/src/E") + decl("D1"), nil}, 172 | {"Godeps/_workspace/src/E/main.go", pkg("E") + decl("E1"), nil}, 173 | {"+git", "", nil}, 174 | }, 175 | }, 176 | }, 177 | want: []*node{ 178 | {"C/main.go", pkg("main", "C/Godeps/_workspace/src/D", "C/Godeps/_workspace/src/E"), nil}, 179 | {"C/Godeps/_workspace/src/D/main.go", pkg("D", "C/Godeps/_workspace/src/E") + "\n" + decl("D2"), nil}, 180 | {"C/Godeps/_workspace/src/E/main.go", pkg("E") + decl("E1"), nil}, 181 | }, 182 | wdep: Godeps{ 183 | ImportPath: "C", 184 | Deps: []Dependency{ 185 | {ImportPath: "D", Comment: "D2"}, 186 | {ImportPath: "E", Comment: "E1"}, 187 | }, 188 | }, 189 | }, 190 | { // 4 - update all dependencies 191 | cwd: "C", 192 | args: []string{"..."}, 193 | start: []*node{ 194 | { 195 | "D", 196 | "", 197 | []*node{ 198 | {"main.go", pkg("D") + decl("D1"), nil}, 199 | {"+git", "D1", nil}, 200 | {"main.go", pkg("D") + decl("D2"), nil}, 201 | {"+git", "D2", nil}, 202 | }, 203 | }, 204 | { 205 | "E", 206 | "", 207 | []*node{ 208 | {"main.go", pkg("E") + decl("E1"), nil}, 209 | {"+git", "E1", nil}, 210 | {"main.go", pkg("E") + decl("E2"), nil}, 211 | {"+git", "E2", nil}, 212 | }, 213 | }, 214 | { 215 | "C", 216 | "", 217 | []*node{ 218 | {"main.go", pkg("main", "D", "E"), nil}, 219 | {"Godeps/Godeps.json", godeps("C", "D", "D1", "E", "E1"), nil}, 220 | {"Godeps/_workspace/src/D/main.go", pkg("D") + decl("D1"), nil}, 221 | {"Godeps/_workspace/src/E/main.go", pkg("E") + decl("E1"), nil}, 222 | {"+git", "", nil}, 223 | }, 224 | }, 225 | }, 226 | want: []*node{ 227 | {"C/Godeps/_workspace/src/D/main.go", pkg("D") + decl("D2"), nil}, 228 | {"C/Godeps/_workspace/src/E/main.go", pkg("E") + decl("E2"), nil}, 229 | }, 230 | wdep: Godeps{ 231 | ImportPath: "C", 232 | Deps: []Dependency{ 233 | {ImportPath: "D", Comment: "D2"}, 234 | {ImportPath: "E", Comment: "E2"}, 235 | }, 236 | }, 237 | }, 238 | { // 5 - one match of two patterns 239 | cwd: "C", 240 | args: []string{"D", "X"}, 241 | start: []*node{ 242 | { 243 | "D", 244 | "", 245 | []*node{ 246 | {"main.go", pkg("D") + decl("D1"), nil}, 247 | {"+git", "D1", nil}, 248 | {"main.go", pkg("D") + decl("D2"), nil}, 249 | {"+git", "D2", nil}, 250 | }, 251 | }, 252 | { 253 | "C", 254 | "", 255 | []*node{ 256 | {"main.go", pkg("main", "D"), nil}, 257 | {"Godeps/Godeps.json", godeps("C", "D", "D1"), nil}, 258 | {"Godeps/_workspace/src/D/main.go", pkg("D") + decl("D1"), nil}, 259 | {"+git", "", nil}, 260 | }, 261 | }, 262 | }, 263 | want: []*node{ 264 | {"C/Godeps/_workspace/src/D/main.go", pkg("D") + decl("D2"), nil}, 265 | }, 266 | wdep: Godeps{ 267 | ImportPath: "C", 268 | Deps: []Dependency{ 269 | {ImportPath: "D", Comment: "D2"}, 270 | }, 271 | }, 272 | }, 273 | { // 6 - no matches 274 | cwd: "C", 275 | args: []string{"X"}, 276 | start: []*node{ 277 | { 278 | "D", 279 | "", 280 | []*node{ 281 | {"main.go", pkg("D") + decl("D1"), nil}, 282 | {"+git", "D1", nil}, 283 | {"main.go", pkg("D") + decl("D2"), nil}, 284 | {"+git", "D2", nil}, 285 | }, 286 | }, 287 | { 288 | "C", 289 | "", 290 | []*node{ 291 | {"main.go", pkg("main", "D"), nil}, 292 | {"Godeps/Godeps.json", godeps("C", "D", "D1"), nil}, 293 | {"Godeps/_workspace/src/D/main.go", pkg("D") + decl("D1"), nil}, 294 | {"+git", "", nil}, 295 | }, 296 | }, 297 | }, 298 | want: []*node{ 299 | {"C/Godeps/_workspace/src/D/main.go", pkg("D") + decl("D1"), nil}, 300 | }, 301 | wdep: Godeps{ 302 | ImportPath: "C", 303 | Deps: []Dependency{ 304 | {ImportPath: "D", Comment: "D1"}, 305 | }, 306 | }, 307 | werr: true, 308 | }, 309 | { // 7 - update just one package of two in a repo skips it 310 | cwd: "C", 311 | args: []string{"D/A", "E"}, 312 | start: []*node{ 313 | { 314 | "D", 315 | "", 316 | []*node{ 317 | {"A/main.go", pkg("A") + decl("D1"), nil}, 318 | {"B/main.go", pkg("B") + decl("D1"), nil}, 319 | {"+git", "D1", nil}, 320 | {"A/main.go", pkg("A") + decl("D2"), nil}, 321 | {"B/main.go", pkg("B") + decl("D2"), nil}, 322 | {"+git", "D2", nil}, 323 | }, 324 | }, 325 | { 326 | "E", 327 | "", 328 | []*node{ 329 | {"main.go", pkg("E") + decl("E1"), nil}, 330 | {"+git", "E1", nil}, 331 | {"main.go", pkg("E") + decl("E2"), nil}, 332 | {"+git", "E2", nil}, 333 | }, 334 | }, 335 | { 336 | "C", 337 | "", 338 | []*node{ 339 | {"main.go", pkg("main", "D/A", "D/B", "E"), nil}, 340 | {"Godeps/Godeps.json", godeps("C", "D/A", "D1", "D/B", "D1", "E", "E1"), nil}, 341 | {"Godeps/_workspace/src/D/A/main.go", pkg("A") + decl("D1"), nil}, 342 | {"Godeps/_workspace/src/D/B/main.go", pkg("B") + decl("D1"), nil}, 343 | {"Godeps/_workspace/src/E/main.go", pkg("E") + decl("E1"), nil}, 344 | {"+git", "", nil}, 345 | }, 346 | }, 347 | }, 348 | want: []*node{ 349 | {"C/Godeps/_workspace/src/D/A/main.go", pkg("A") + decl("D1"), nil}, 350 | {"C/Godeps/_workspace/src/D/B/main.go", pkg("B") + decl("D1"), nil}, 351 | {"C/Godeps/_workspace/src/E/main.go", pkg("E") + decl("E2"), nil}, 352 | }, 353 | wdep: Godeps{ 354 | ImportPath: "C", 355 | Deps: []Dependency{ 356 | {ImportPath: "D/A", Comment: "D1"}, 357 | {ImportPath: "D/B", Comment: "D1"}, 358 | {ImportPath: "E", Comment: "E2"}, 359 | }, 360 | }, 361 | }, 362 | { // 8 - update just one package of two in a repo, none left 363 | cwd: "C", 364 | args: []string{"D/A"}, 365 | start: []*node{ 366 | { 367 | "D", 368 | "", 369 | []*node{ 370 | {"A/main.go", pkg("A") + decl("D1"), nil}, 371 | {"B/main.go", pkg("B") + decl("D1"), nil}, 372 | {"+git", "D1", nil}, 373 | {"A/main.go", pkg("A") + decl("D2"), nil}, 374 | {"B/main.go", pkg("B") + decl("D2"), nil}, 375 | {"+git", "D2", nil}, 376 | }, 377 | }, 378 | { 379 | "C", 380 | "", 381 | []*node{ 382 | {"main.go", pkg("main", "D/A", "D/B"), nil}, 383 | {"Godeps/Godeps.json", godeps("C", "D/A", "D1", "D/B", "D1"), nil}, 384 | {"Godeps/_workspace/src/D/A/main.go", pkg("A") + decl("D1"), nil}, 385 | {"Godeps/_workspace/src/D/B/main.go", pkg("B") + decl("D1"), nil}, 386 | {"+git", "", nil}, 387 | }, 388 | }, 389 | }, 390 | want: []*node{ 391 | {"C/Godeps/_workspace/src/D/A/main.go", pkg("A") + decl("D1"), nil}, 392 | {"C/Godeps/_workspace/src/D/B/main.go", pkg("B") + decl("D1"), nil}, 393 | }, 394 | wdep: Godeps{ 395 | ImportPath: "C", 396 | Deps: []Dependency{ 397 | {ImportPath: "D/A", Comment: "D1"}, 398 | {ImportPath: "D/B", Comment: "D1"}, 399 | }, 400 | }, 401 | werr: true, 402 | }, 403 | { // 9 - package/..., just version bump 404 | vendor: true, 405 | cwd: "C", 406 | args: []string{"D/..."}, 407 | start: []*node{ 408 | { 409 | "D", 410 | "", 411 | []*node{ 412 | {"A/main.go", pkg("A") + decl("D1"), nil}, 413 | {"B/main.go", pkg("B") + decl("D1"), nil}, 414 | {"+git", "D1", nil}, 415 | {"A/main.go", pkg("A") + decl("D2"), nil}, 416 | {"B/main.go", pkg("B") + decl("D2"), nil}, 417 | {"+git", "D2", nil}, 418 | }, 419 | }, 420 | { 421 | "C", 422 | "", 423 | []*node{ 424 | {"main.go", pkg("main", "D/A", "D/B"), nil}, 425 | {"Godeps/Godeps.json", godeps("C", "D/A", "D1", "D/B", "D1"), nil}, 426 | {"vendor/D/A/main.go", pkg("A") + decl("D1"), nil}, 427 | {"vendor/D/B/main.go", pkg("B") + decl("D1"), nil}, 428 | {"+git", "", nil}, 429 | }, 430 | }, 431 | }, 432 | want: []*node{ 433 | {"C/vendor/D/A/main.go", pkg("A") + decl("D2"), nil}, 434 | {"C/vendor/D/B/main.go", pkg("B") + decl("D2"), nil}, 435 | }, 436 | wdep: Godeps{ 437 | ImportPath: "C", 438 | Deps: []Dependency{ 439 | {ImportPath: "D/A", Comment: "D2"}, 440 | {ImportPath: "D/B", Comment: "D2"}, 441 | }, 442 | }, 443 | }, 444 | { // 10 - package/..., new unrelated package that's not imported 445 | vendor: true, 446 | cwd: "C", 447 | args: []string{"D/..."}, 448 | start: []*node{ 449 | { 450 | "D", 451 | "", 452 | []*node{ 453 | {"A/main.go", pkg("A") + decl("D1"), nil}, 454 | {"B/main.go", pkg("B") + decl("D1"), nil}, 455 | {"+git", "D1", nil}, 456 | {"A/main.go", pkg("A") + decl("D2"), nil}, 457 | {"B/main.go", pkg("B") + decl("D2"), nil}, 458 | {"E/main.go", pkg("E") + decl("D2"), nil}, 459 | {"+git", "D2", nil}, 460 | }, 461 | }, 462 | { 463 | "C", 464 | "", 465 | []*node{ 466 | {"main.go", pkg("main", "D/A", "D/B"), nil}, 467 | {"Godeps/Godeps.json", godeps("C", "D/A", "D1", "D/B", "D1"), nil}, 468 | {"vendor/D/A/main.go", pkg("A") + decl("D1"), nil}, 469 | {"vendor/D/B/main.go", pkg("B") + decl("D1"), nil}, 470 | {"+git", "", nil}, 471 | }, 472 | }, 473 | }, 474 | want: []*node{ 475 | {"C/vendor/D/A/main.go", pkg("A") + decl("D2"), nil}, 476 | {"C/vendor/D/B/main.go", pkg("B") + decl("D2"), nil}, 477 | {"C/vendor/D/E/main.go", "(absent)", nil}, 478 | }, 479 | wdep: Godeps{ 480 | ImportPath: "C", 481 | Deps: []Dependency{ 482 | {ImportPath: "D/A", Comment: "D2"}, 483 | {ImportPath: "D/B", Comment: "D2"}, 484 | }, 485 | }, 486 | }, 487 | { // 11 - package/..., new transitive package, same repo 488 | vendor: true, 489 | cwd: "C", 490 | args: []string{"D/..."}, 491 | start: []*node{ 492 | { 493 | "D", 494 | "", 495 | []*node{ 496 | {"A/main.go", pkg("A") + decl("D1"), nil}, 497 | {"B/main.go", pkg("B") + decl("D1"), nil}, 498 | {"+git", "D1", nil}, 499 | {"A/main.go", pkg("A") + decl("D2"), nil}, 500 | {"B/main.go", pkg("B", "D/E") + decl("D2"), nil}, 501 | {"E/main.go", pkg("E") + decl("D2"), nil}, 502 | {"+git", "D2", nil}, 503 | }, 504 | }, 505 | { 506 | "C", 507 | "", 508 | []*node{ 509 | {"main.go", pkg("main", "D/A", "D/B"), nil}, 510 | {"Godeps/Godeps.json", godeps("C", "D/A", "D1", "D/B", "D1"), nil}, 511 | {"vendor/D/A/main.go", pkg("A") + decl("D1"), nil}, 512 | {"vendor/D/B/main.go", pkg("B") + decl("D1"), nil}, 513 | {"+git", "", nil}, 514 | }, 515 | }, 516 | }, 517 | want: []*node{ 518 | {"C/vendor/D/A/main.go", pkg("A") + decl("D2"), nil}, 519 | {"C/vendor/D/B/main.go", pkg("B", "D/E") + decl("D2"), nil}, 520 | {"C/vendor/D/E/main.go", pkg("E") + decl("D2"), nil}, 521 | }, 522 | wdep: Godeps{ 523 | ImportPath: "C", 524 | Deps: []Dependency{ 525 | {ImportPath: "D/A", Comment: "D2"}, 526 | {ImportPath: "D/B", Comment: "D2"}, 527 | {ImportPath: "D/E", Comment: "D2"}, 528 | }, 529 | }, 530 | }, 531 | { // 12 - package/..., new transitive package, different repo 532 | vendor: true, 533 | cwd: "C", 534 | args: []string{"D/..."}, 535 | start: []*node{ 536 | { 537 | "D", 538 | "", 539 | []*node{ 540 | {"A/main.go", pkg("A") + decl("D1"), nil}, 541 | {"B/main.go", pkg("B") + decl("D1"), nil}, 542 | {"+git", "D1", nil}, 543 | {"A/main.go", pkg("A") + decl("D2"), nil}, 544 | {"B/main.go", pkg("B", "E") + decl("D2"), nil}, 545 | {"+git", "D2", nil}, 546 | }, 547 | }, 548 | { 549 | "E", 550 | "", 551 | []*node{ 552 | {"main.go", pkg("E") + decl("E1"), nil}, 553 | {"+git", "E1", nil}, 554 | }, 555 | }, 556 | { 557 | "C", 558 | "", 559 | []*node{ 560 | {"main.go", pkg("main", "D/A", "D/B"), nil}, 561 | {"Godeps/Godeps.json", godeps("C", "D/A", "D1", "D/B", "D1"), nil}, 562 | {"vendor/D/A/main.go", pkg("A") + decl("D1"), nil}, 563 | {"vendor/D/B/main.go", pkg("B") + decl("D1"), nil}, 564 | {"+git", "", nil}, 565 | }, 566 | }, 567 | }, 568 | want: []*node{ 569 | {"C/vendor/D/A/main.go", pkg("A") + decl("D2"), nil}, 570 | {"C/vendor/D/B/main.go", pkg("B", "E") + decl("D2"), nil}, 571 | {"C/vendor/E/main.go", pkg("E") + decl("E1"), nil}, 572 | }, 573 | wdep: Godeps{ 574 | ImportPath: "C", 575 | Deps: []Dependency{ 576 | {ImportPath: "D/A", Comment: "D2"}, 577 | {ImportPath: "D/B", Comment: "D2"}, 578 | {ImportPath: "E", Comment: "E1"}, 579 | }, 580 | }, 581 | }, 582 | { // 13 - package/..., missing packages 583 | vendor: true, 584 | cwd: "C", 585 | args: []string{"D/..."}, 586 | start: []*node{ 587 | { 588 | "D", 589 | "", 590 | []*node{ 591 | {"A/main.go", pkg("A") + decl("D1"), nil}, 592 | {"B/main.go", pkg("B") + decl("D1"), nil}, 593 | {"+git", "D1", nil}, 594 | }, 595 | }, 596 | { 597 | "C", 598 | "", 599 | []*node{ 600 | {"main.go", pkg("main", "D/A"), nil}, 601 | {"Godeps/Godeps.json", godeps("C", "D/A", "D1", "D/B", "D1"), nil}, 602 | {"vendor/D/A/main.go", pkg("A") + decl("D1"), nil}, 603 | {"vendor/D/B/main.go", pkg("B") + decl("D1"), nil}, 604 | {"+git", "", nil}, 605 | }, 606 | }, 607 | {"D", 608 | "", 609 | []*node{ 610 | {"A/main.go", pkg("A") + decl("D2"), nil}, 611 | {"B", "(rm)", nil}, 612 | {"+git", "D2", nil}, 613 | }, 614 | }, 615 | }, 616 | want: []*node{ 617 | {"C/vendor/D/A/main.go", pkg("A") + decl("D2"), nil}, 618 | {"C/vendor/D/B/main.go", "(absent)", nil}, 619 | {"C/vendor/D/E/main.go", "(absent)", nil}, 620 | }, 621 | wdep: Godeps{ 622 | ImportPath: "C", 623 | Deps: []Dependency{ 624 | {ImportPath: "D/A", Comment: "D2"}, 625 | }, 626 | }, 627 | }, 628 | { // 14 - Update package A, but not package B, which is missing from $GOPATH 629 | vendor: true, 630 | cwd: "C", 631 | args: []string{"A"}, 632 | start: []*node{ 633 | { 634 | "A", 635 | "", 636 | []*node{ 637 | {"main.go", pkg("A") + decl("A1"), nil}, 638 | {"+git", "A1", nil}, 639 | {"main.go", pkg("A") + decl("A2"), nil}, 640 | {"+git", "A2", nil}, 641 | }, 642 | }, 643 | { // Create B so makeTree can resolve the rev for Godeps.json 644 | "B", 645 | "", 646 | []*node{ 647 | {"main.go", pkg("B") + decl("B1"), nil}, 648 | {"+git", "B1", nil}, 649 | }, 650 | }, 651 | { 652 | "C", 653 | "", 654 | []*node{ 655 | {"main.go", pkg("main", "A", "B"), nil}, 656 | {"Godeps/Godeps.json", godeps("C", "A", "A1", "B", "B1"), nil}, 657 | {"vendor/A/main.go", pkg("A") + decl("A1"), nil}, 658 | {"vendor/B/main.go", pkg("B") + decl("B1"), nil}, 659 | {"+git", "", nil}, 660 | }, 661 | }, 662 | { // Remove B so it's not in the $GOPATH 663 | "", 664 | "", 665 | []*node{ 666 | {"B", "(rm)", nil}, 667 | }, 668 | }, 669 | }, 670 | want: []*node{ 671 | {"C/vendor/A/main.go", pkg("A") + decl("A2"), nil}, 672 | {"C/vendor/B/main.go", pkg("B") + decl("B1"), nil}, 673 | }, 674 | wdep: Godeps{ 675 | ImportPath: "C", 676 | Deps: []Dependency{ 677 | {ImportPath: "A", Comment: "A2"}, 678 | {ImportPath: "B", Comment: "B1"}, 679 | }, 680 | }, 681 | }, 682 | } 683 | 684 | wd, err := os.Getwd() 685 | if err != nil { 686 | t.Fatal(err) 687 | } 688 | const gopath = "godeptest" 689 | defer os.RemoveAll(gopath) 690 | for pos, test := range cases { 691 | setGlobals(test.vendor) 692 | err = os.RemoveAll(gopath) 693 | if err != nil { 694 | t.Fatal(err) 695 | } 696 | src := filepath.Join(gopath, "src") 697 | makeTree(t, &node{src, "", test.start}, "") 698 | 699 | dir := filepath.Join(wd, src, test.cwd) 700 | err = os.Chdir(dir) 701 | if err != nil { 702 | panic(err) 703 | } 704 | setGOPATH(filepath.Join(wd, gopath)) 705 | log.SetOutput(ioutil.Discard) 706 | err = update(test.args) 707 | log.SetOutput(os.Stderr) 708 | if err != nil { 709 | t.Log(pos, "Err:", err) 710 | } 711 | if g := err != nil; g != test.werr { 712 | t.Errorf("update err = %v (%v) want %v", g, err, test.werr) 713 | } 714 | err = os.Chdir(wd) 715 | if err != nil { 716 | panic(err) 717 | } 718 | 719 | checkTree(t, pos, &node{src, "", test.want}) 720 | 721 | f, err := os.Open(filepath.Join(dir, "Godeps/Godeps.json")) 722 | if err != nil { 723 | t.Error(err) 724 | } 725 | g := new(Godeps) 726 | err = json.NewDecoder(f).Decode(g) 727 | if err != nil { 728 | t.Error(err) 729 | } 730 | f.Close() 731 | 732 | if g.ImportPath != test.wdep.ImportPath { 733 | t.Errorf("ImportPath = %s want %s", g.ImportPath, test.wdep.ImportPath) 734 | } 735 | for i := range g.Deps { 736 | g.Deps[i].Rev = "" 737 | } 738 | if !reflect.DeepEqual(g.Deps, test.wdep.Deps) { 739 | t.Errorf("Deps = %v want %v", g.Deps, test.wdep.Deps) 740 | } 741 | } 742 | } 743 | --------------------------------------------------------------------------------