Add self-contained gometalinter build tooling.

This commit is contained in:
Will Rouesnel
2017-06-06 21:39:41 +10:00
parent 0de0311c22
commit e2b6c973a1
710 changed files with 277204 additions and 35 deletions

27
tools/vendor/github.com/kardianos/govendor/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,27 @@
Copyright (c) 2015 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

182
tools/vendor/github.com/kardianos/govendor/README.md generated vendored Normal file
View File

@@ -0,0 +1,182 @@
# The Vendor Tool for Go
`go get -u github.com/kardianos/govendor`
New users please read the [FAQ](doc/faq.md)
Package developers should read the [developer guide](doc/dev-guide.md).
For a high level overview read the [whitepaper](doc/whitepaper.md)
Uses the go1.5+ vendor folder. Multiple workflows supported, single tool.
[![Build Status](https://travis-ci.org/kardianos/govendor.svg?branch=master)](https://travis-ci.org/kardianos/govendor)
[![Build status](https://ci.appveyor.com/api/projects/status/skf1t3363y6tycuc/branch/master?svg=true)](https://ci.appveyor.com/project/kardianos/govendor/branch/master)
[![GoDoc](https://godoc.org/github.com/kardianos/govendor?status.svg)](https://godoc.org/github.com/kardianos/govendor)
* Copy existing dependencies from $GOPATH with `govendor add/update`.
* If you ignore `vendor/*/`, restore dependencies with `govendor sync`.
* Pull in new dependencies or update existing dependencies directly from
remotes with `govendor fetch`.
* Migrate from legacy systems with `govendor migrate`.
* Supports Linux, OS X, Windows, probably all others.
* Supports git, hg, svn, bzr (must be installed an on the PATH).
## Notes
* The project must be within a $GOPATH/src.
* If using go1.5, ensure you `set GO15VENDOREXPERIMENT=1`.
### Quick Start, also see the [FAQ](doc/faq.md)
```
# Setup your project.
cd "my project in GOPATH"
govendor init
# Add existing GOPATH files to vendor.
govendor add +external
# View your work.
govendor list
# Look at what is using a package
govendor list -v fmt
# Specify a specific version or revision to fetch
govendor fetch golang.org/x/net/context@a4bbce9fcae005b22ae5443f6af064d80a6f5a55
govendor fetch golang.org/x/net/context@v1 # Get latest v1.*.* tag or branch.
govendor fetch golang.org/x/net/context@=v1 # Get the tag or branch named "v1".
# Update a package to latest, given any prior version constraint
govendor fetch golang.org/x/net/context
# Format your repository only
govendor fmt +local
# Build everything in your repository only
govendor install +local
# Test your repository only
govendor test +local
```
## Sub-commands
```
init Create the "vendor" folder and the "vendor.json" file.
list List and filter existing dependencies and packages.
add Add packages from $GOPATH.
update Update packages from $GOPATH.
remove Remove packages from the vendor folder.
status Lists any packages missing, out-of-date, or modified locally.
fetch Add new or update vendor folder packages from remote repository.
sync Pull packages into vendor folder from remote repository with revisions
from vendor.json file.
migrate Move packages from a legacy tool to the vendor folder with metadata.
get Like "go get" but copies dependencies into a "vendor" folder.
license List discovered licenses for the given status or import paths.
shell Run a "shell" to make multiple sub-commands more efficient for large
projects.
go tool commands that are wrapped:
`+<status>` package selection may be used with them
fmt, build, install, clean, test, vet, generate, tool
```
## Status
Packages can be specified by their "status".
```
+local (l) packages in your project
+external (e) referenced packages in GOPATH but not in current project
+vendor (v) packages in the vendor folder
+std (s) packages in the standard library
+excluded (x) external packages explicitly excluded from vendoring
+unused (u) packages in the vendor folder, but unused
+missing (m) referenced packages but not found
+program (p) package is a main package
+outside +external +missing
+all +all packages
```
Status can be referenced by their initial letters.
* `+std` same as `+s`
* `+external` same as `+ext` same as `+e`
* `+excluded` same as `+exc` same as `+x`
Status can be logically composed:
* `+local,program` (local AND program) local packages that are also programs
* `+local +vendor` (local OR vendor) local packages or vendor packages
* `+vendor,program +std` ((vendor AND program) OR std) vendor packages that are also programs
or std library packages
* `+vendor,^program` (vendor AND NOT program) vendor package that are not "main" packages.
## Package specifier
The full package-spec is:
`<path>[::<origin>][{/...|/^}][@[<version-spec>]]`
Some examples:
* `github.com/kardianos/govendor` specifies a single package and single folder.
* `github.com/kardianos/govendor/...` specifies `govendor` and all referenced
packages under that path.
* `github.com/kardianos/govendor/^` specifies the `govendor` folder and all
sub-folders. Useful for resources or if you don't want a partial repository.
* `github.com/kardianos/govendor/^::github.com/myself/govendor` same as above
but fetch from user "myself".
* `github.com/kardianos/govendor/...@abc12032` all referenced packages at
revision `abc12032`.
* `github.com/kardianos/govendor/...@v1` same as above, but get the most recent
"v1" tag, such as "v1.4.3".
* `github.com/kardianos/govendor/...@=v1` get the exact version "v1".
## Packages and Status
You may specify multiple package-specs and multiple status in a single command.
Commands that accept status and package-spec:
* list
* add
* update
* remove
* fetch
You may pass arguments to govendor through stdin if the last argument is a "-".
For example `echo +vendor | govendor list -` will list all vendor packages.
## Ignoring build tags and excluding packages
Ignoring build tags is opt-out and is designed to be the opposite of the build
file directives which are opt-in when specified. Typically a developer will
want to support cross platform builds, but selectively opt out of tags, tests,
and architectures as desired.
To ignore additional tags edit the "vendor.json" file and add tag to the vendor
"ignore" file field. The field uses spaces to separate tags to ignore.
For example the following will ignore both test and appengine files.
```
{
"ignore": "test appengine",
}
```
Similarly, some specific packages can be excluded from the vendoring process.
These packages will be listed as `excluded` (`x`), and will not be copied to the
"vendor" folder when running `govendor add|fetch|update`.
Any sub-package `foo/bar` of an excluded package `foo` is also excluded (but
package `bar/foo` is not). The import dependencies of excluded packages are not
listed, and thus not vendored.
To exclude packages, also use the "ignore" field of the "vendor.json" file.
Packages are identified by their name, they should contain a "/" character
(possibly at the end):
```
{
"ignore": "test appengine foo/",
}
```

View File

@@ -0,0 +1,24 @@
version: "{build}"
os: Windows Server 2012 R2
environment:
GOPATH: c:\gopath
GOVERSION: 1.7
clone_folder: c:\gopath\src\github.com\kardianos\govendor
install:
- mkdir c:\tmp
- set TMP=C:\tmp
- go version
- set PATH=%GOPATH%\bin;c:\go\bin;%PATH%
- rmdir c:\go /s /q
- appveyor DownloadFile https://storage.googleapis.com/golang/go%GOVERSION%.windows-amd64.zip
- 7z x go%GOVERSION%.windows-amd64.zip -y -oC:\ > NUL
- go version
- go env
build_script:
- go test -i ./...
- go test ./...

View File

@@ -0,0 +1,154 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package cliprompt uses the CLI to prompt for user feedback.
package cliprompt
import (
"fmt"
"strconv"
"strings"
"github.com/kardianos/govendor/prompt"
cp "github.com/Bowery/prompt"
)
type Prompt struct{}
// Ask the user a question based on the CLI.
// TODO (DT): Currently can't handle fetching empty responses do to cancel method.
func (p *Prompt) Ask(q *prompt.Question) (prompt.Response, error) {
term, err := cp.NewTerminal()
if err != nil {
return prompt.RespCancel, err
}
if len(q.Error) > 0 {
fmt.Fprintf(term.Out, "%s\n\n", q.Error)
}
switch q.Type {
default:
panic("Unknown question type")
case prompt.TypeSelectMultiple:
return prompt.RespCancel, fmt.Errorf("Selecting multiple isn't currently supported")
case prompt.TypeSelectOne:
return getSingle(term, q)
}
}
func getSingle(term *cp.Terminal, q *prompt.Question) (prompt.Response, error) {
if len(q.Options) == 1 && q.Options[0].Other() {
opt := &q.Options[0]
opt.Choosen = true
return setOther(term, q, opt)
}
choosen := q.AnswerSingle(false)
if choosen == nil {
return setOption(term, q)
}
resp, err := setOther(term, q, choosen)
if err != nil {
return prompt.RespCancel, err
}
if resp == prompt.RespCancel {
choosen.Choosen = false
return setOption(term, q)
}
return resp, nil
}
func setOther(term *cp.Terminal, q *prompt.Question, opt *prompt.Option) (prompt.Response, error) {
var blankCount = 0
var internalMessage = ""
for {
// Write out messages
if len(internalMessage) > 0 {
fmt.Fprintf(term.Out, "%s\n\n", internalMessage)
}
if len(q.Prompt) > 0 {
fmt.Fprintf(term.Out, "%s\n", q.Prompt)
}
if len(opt.Validation()) > 0 {
fmt.Fprintf(term.Out, " ** %s\n", opt.Validation())
}
// Reset message.
internalMessage = ""
ln, err := term.Basic(" > ", false)
if err != nil {
return prompt.RespCancel, err
}
if len(ln) == 0 && blankCount > 0 {
return prompt.RespCancel, nil
}
if len(ln) == 0 {
internalMessage = "Press enter again to cancel"
blankCount++
continue
}
blankCount = 0
opt.Value = strings.TrimSpace(ln)
return prompt.RespAnswer, nil
}
}
func setOption(term *cp.Terminal, q *prompt.Question) (prompt.Response, error) {
var blankCount = 0
var internalMessage = ""
for {
// Write out messages
if len(internalMessage) > 0 {
fmt.Fprintf(term.Out, "%s\n\n", internalMessage)
}
if len(q.Prompt) > 0 {
fmt.Fprintf(term.Out, "%s\n", q.Prompt)
}
for index, opt := range q.Options {
fmt.Fprintf(term.Out, " (%d) %s\n", index+1, opt.Prompt())
if len(opt.Validation()) > 0 {
fmt.Fprintf(term.Out, " ** %s\n", opt.Validation())
}
}
// Reset message.
internalMessage = ""
ln, err := term.Basic(" # ", false)
if err != nil {
return prompt.RespCancel, err
}
if len(ln) == 0 && blankCount > 0 {
return prompt.RespCancel, nil
}
if len(ln) == 0 {
internalMessage = "Press enter again to cancel"
blankCount++
continue
}
blankCount = 0
choice, err := strconv.ParseInt(ln, 10, 32)
if err != nil {
internalMessage = "Not a valid number"
continue
}
index := int(choice - 1)
if index < 0 || index >= len(q.Options) {
internalMessage = "Not a valid choice."
continue
}
opt := &q.Options[index]
opt.Choosen = true
if opt.Other() {
res, err := setOther(term, q, opt)
if err != nil {
return prompt.RespCancel, err
}
if res == prompt.RespCancel {
opt.Choosen = false
continue
}
}
return prompt.RespAnswer, nil
}
}

View File

@@ -0,0 +1,403 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package context gathers the status of packages and stores it in Context.
// A new Context needs to be pointed to the root of the project and any
// project owned vendor file.
package context
import (
"fmt"
"io"
"os/exec"
"path"
"path/filepath"
"strings"
"github.com/kardianos/govendor/internal/pathos"
os "github.com/kardianos/govendor/internal/vos"
"github.com/kardianos/govendor/pkgspec"
"github.com/kardianos/govendor/vendorfile"
)
const (
debug = false
looplimit = 10000
vendorFilename = "vendor.json"
)
func dprintf(f string, v ...interface{}) {
if debug {
fmt.Printf(f, v...)
}
}
// Context represents the current project context.
type Context struct {
Logger io.Writer // Write to the verbose log.
Insecure bool // Allow insecure network operations
GopathList []string // List of GOPATHs in environment. Includes "src" dir.
Goroot string // The path to the standard library.
RootDir string // Full path to the project root.
RootGopath string // The GOPATH the project is in.
RootImportPath string // The import path to the project.
VendorFile *vendorfile.File
VendorFilePath string // File path to vendor file.
VendorFolder string // Store vendor packages in this folder.
RootToVendorFile string // The relative path from the project root to the vendor file directory.
VendorDiscoverFolder string // Normally auto-set to "vendor"
// Package is a map where the import path is the key.
// Populated with LoadPackage.
Package map[string]*Package
// Change to unkown structure (rename). Maybe...
// MoveRule provides the translation from origional import path to new import path.
RewriteRule map[string]string // map[from]to
Operation []*Operation
loaded, dirty bool
rewriteImports bool
ignoreTag []string // list of tags to ignore
excludePackage []string // list of package prefixes to exclude
statusCache []StatusItem
added map[string]bool
}
// Package maintains information pertaining to a package.
type Package struct {
OriginDir string // Origin directory
Dir string // Physical directory path of the package.
Status Status // Status and location of the package.
*pkgspec.Pkg
Local string // Current location of a package relative to $GOPATH/src.
Gopath string // Includes trailing "src".
Files []*File
inVendor bool // Different then Status.Location, this is in *any* vendor tree.
inTree bool
ignoreFile []string
// used in resolveUnknown function. Not persisted.
referenced map[string]*Package
}
// File holds a reference to the imports in a file and the file locaiton.
type File struct {
Package *Package
Path string
Imports []string
ImportComment string
}
type RootType byte
const (
RootVendor RootType = iota
RootWD
RootVendorOrWD
)
func (pkg *Package) String() string {
return pkg.Local
}
type packageList []*Package
func (li packageList) Len() int { return len(li) }
func (li packageList) Swap(i, j int) { li[i], li[j] = li[j], li[i] }
func (li packageList) Less(i, j int) bool {
if li[i].Path != li[j].Path {
return li[i].Path < li[j].Path
}
return li[i].Local < li[j].Local
}
// NewContextWD creates a new context. It looks for a root folder by finding
// a vendor file.
func NewContextWD(rt RootType) (*Context, error) {
wd, err := os.Getwd()
if err != nil {
return nil, err
}
pathToVendorFile := filepath.Join("vendor", vendorFilename)
rootIndicator := "vendor"
vendorFolder := "vendor"
root := wd
if rt == RootVendor || rt == RootVendorOrWD {
tryRoot, err := findRoot(wd, rootIndicator)
switch rt {
case RootVendor:
if err != nil {
return nil, err
}
root = tryRoot
case RootVendorOrWD:
if err == nil {
root = tryRoot
}
}
}
// Check for old vendor file location.
oldLocation := filepath.Join(root, vendorFilename)
if _, err := os.Stat(oldLocation); err == nil {
return nil, ErrOldVersion{`Use the "migrate" command to update.`}
}
return NewContext(root, pathToVendorFile, vendorFolder, false)
}
// NewContext creates new context from a given root folder and vendor file path.
// The vendorFolder is where vendor packages should be placed.
func NewContext(root, vendorFilePathRel, vendorFolder string, rewriteImports bool) (*Context, error) {
dprintf("CTX: %s\n", root)
var err error
// Get GOROOT. First check ENV, then run "go env" and find the GOROOT line.
goroot := os.Getenv("GOROOT")
if len(goroot) == 0 {
// If GOROOT is not set, get from go cmd.
cmd := exec.Command("go", "env")
var goEnv []byte
goEnv, err = cmd.CombinedOutput()
if err != nil {
return nil, err
}
for _, line := range strings.Split(string(goEnv), "\n") {
if v, ok := pathos.GoEnv("GOROOT", line); ok {
goroot = v
break
}
}
}
if goroot == "" {
return nil, ErrMissingGOROOT
}
goroot = filepath.Join(goroot, "src")
// Get the GOPATHs. Prepend the GOROOT to the list.
all := os.Getenv("GOPATH")
if len(all) == 0 {
return nil, ErrMissingGOPATH
}
gopathList := filepath.SplitList(all)
gopathGoroot := make([]string, 0, len(gopathList)+1)
gopathGoroot = append(gopathGoroot, goroot)
for _, gopath := range gopathList {
srcPath := filepath.Join(gopath, "src") + string(filepath.Separator)
srcPathEvaled, err := filepath.EvalSymlinks(srcPath)
if err != nil {
return nil, err
}
gopathGoroot = append(gopathGoroot, srcPath, srcPathEvaled+string(filepath.Separator))
}
rootToVendorFile, _ := filepath.Split(vendorFilePathRel)
vendorFilePath := filepath.Join(root, vendorFilePathRel)
ctx := &Context{
RootDir: root,
GopathList: gopathGoroot,
Goroot: goroot,
VendorFilePath: vendorFilePath,
VendorFolder: vendorFolder,
RootToVendorFile: pathos.SlashToImportPath(rootToVendorFile),
VendorDiscoverFolder: "vendor",
Package: make(map[string]*Package),
RewriteRule: make(map[string]string, 3),
rewriteImports: rewriteImports,
}
ctx.RootImportPath, ctx.RootGopath, err = ctx.findImportPath(root)
if err != nil {
return nil, err
}
vf, err := readVendorFile(path.Join(ctx.RootImportPath, vendorFolder)+"/", vendorFilePath)
if err != nil {
if !os.IsNotExist(err) {
return nil, err
}
vf = &vendorfile.File{}
}
ctx.VendorFile = vf
ctx.IgnoreBuildAndPackage(vf.Ignore)
return ctx, nil
}
// IgnoreBuildAndPackage takes a space separated list of tags or package prefixes
// to ignore.
// Tags are words, packages are folders, containing or ending with a "/".
// "a b c" will ignore tags "a" OR "b" OR "c".
// "p/x q/" will ignore packages "p/x" OR "p/x/y" OR "q" OR "q/z", etc.
func (ctx *Context) IgnoreBuildAndPackage(ignore string) {
ctx.dirty = true
ors := strings.Fields(ignore)
ctx.ignoreTag = make([]string, 0, len(ors))
ctx.excludePackage = make([]string, 0, len(ors))
for _, or := range ors {
if len(or) == 0 {
continue
}
if strings.Index(or, "/") != -1 {
// package
ctx.excludePackage = append(ctx.excludePackage, strings.Trim(or, "./"))
} else {
// tag
ctx.ignoreTag = append(ctx.ignoreTag, or)
}
}
}
// Write to the set io.Writer for logging.
func (ctx *Context) Write(s []byte) (int, error) {
if ctx.Logger != nil {
return ctx.Logger.Write(s)
}
return len(s), nil
}
// VendorFilePackagePath finds a given vendor file package give the import path.
func (ctx *Context) VendorFilePackagePath(path string) *vendorfile.Package {
for _, pkg := range ctx.VendorFile.Package {
if pkg.Remove {
continue
}
if pkg.Path == path {
return pkg
}
}
return nil
}
// findPackageChild finds any package under the current package.
// Used for finding tree overlaps.
func (ctx *Context) findPackageChild(ck *Package) []*Package {
out := make([]*Package, 0, 3)
for _, pkg := range ctx.Package {
if pkg == ck {
continue
}
if pkg.inVendor == false {
continue
}
if pkg.Status.Presence == PresenceTree {
continue
}
if strings.HasPrefix(pkg.Path, ck.Path+"/") {
out = append(out, pkg)
}
}
return out
}
// findPackageParentTree finds any parent tree package that would
// include the given canonical path.
func (ctx *Context) findPackageParentTree(ck *Package) []string {
out := make([]string, 0, 1)
for _, pkg := range ctx.Package {
if pkg.inVendor == false {
continue
}
if pkg.IncludeTree == false || pkg == ck {
continue
}
// pkg.Path = github.com/usera/pkg, tree = true
// ck.Path = github.com/usera/pkg/dance
if strings.HasPrefix(ck.Path, pkg.Path+"/") {
out = append(out, pkg.Local)
}
}
return out
}
// updatePackageReferences populates the referenced field in each Package.
func (ctx *Context) updatePackageReferences() {
pathUnderDirLookup := make(map[string]map[string]*Package)
findCanonicalUnderDir := func(dir, path string) *Package {
if importMap, found := pathUnderDirLookup[dir]; found {
if pkg, found2 := importMap[path]; found2 {
return pkg
}
} else {
pathUnderDirLookup[dir] = make(map[string]*Package)
}
for _, pkg := range ctx.Package {
if !pkg.inVendor {
continue
}
removeFromEnd := len(pkg.Path) + len(ctx.VendorDiscoverFolder) + 2
nextLen := len(pkg.Dir) - removeFromEnd
if nextLen < 0 {
continue
}
checkDir := pkg.Dir[:nextLen]
if !pathos.FileHasPrefix(dir, checkDir) {
continue
}
if pkg.Path != path {
continue
}
pathUnderDirLookup[dir][path] = pkg
return pkg
}
pathUnderDirLookup[dir][path] = nil
return nil
}
for _, pkg := range ctx.Package {
pkg.referenced = make(map[string]*Package, len(pkg.referenced))
}
for _, pkg := range ctx.Package {
for _, f := range pkg.Files {
for _, imp := range f.Imports {
if vpkg := findCanonicalUnderDir(pkg.Dir, imp); vpkg != nil {
vpkg.referenced[pkg.Local] = pkg
continue
}
if other, found := ctx.Package[imp]; found {
other.referenced[pkg.Local] = pkg
continue
}
}
}
}
// Transfer all references from the child to the top parent.
for _, pkg := range ctx.Package {
if parentTrees := ctx.findPackageParentTree(pkg); len(parentTrees) > 0 {
if parentPkg := ctx.Package[parentTrees[0]]; parentPkg != nil {
for opath, opkg := range pkg.referenced {
// Do not transfer internal references.
if strings.HasPrefix(opkg.Path, parentPkg.Path+"/") {
continue
}
parentPkg.referenced[opath] = opkg
}
pkg.referenced = make(map[string]*Package, 0)
}
}
}
}

View File

@@ -0,0 +1,203 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package context
import (
"fmt"
"hash"
"io"
"os"
"path"
"path/filepath"
"sort"
"strings"
"github.com/kardianos/govendor/internal/pathos"
"github.com/pkg/errors"
)
type fileInfoSort []os.FileInfo
func (l fileInfoSort) Len() int {
return len(l)
}
func (l fileInfoSort) Less(i, j int) bool {
a := l[i]
b := l[j]
if a.IsDir() == b.IsDir() {
return l[i].Name() < l[j].Name()
}
return !a.IsDir()
}
func (l fileInfoSort) Swap(i, j int) {
l[i], l[j] = l[j], l[i]
}
// CopyPackage copies the files from the srcPath to the destPath, destPath
// folder and parents are are created if they don't already exist.
func (ctx *Context) CopyPackage(destPath, srcPath, lookRoot, pkgPath string, ignoreFiles []string, tree bool, h hash.Hash, beforeCopy func(deps []string) error) error {
if pathos.FileStringEquals(destPath, srcPath) {
return fmt.Errorf("Attempting to copy package to same location %q.", destPath)
}
err := os.MkdirAll(destPath, 0777)
if err != nil {
return err
}
// Ensure the dest is empty of files.
destDir, err := os.Open(destPath)
if err != nil {
return err
}
ignoreTest := false
for _, ignore := range ctx.ignoreTag {
if ignore == "test" {
ignoreTest = true
break
}
}
fl, err := destDir.Readdir(-1)
destDir.Close()
if err != nil {
return err
}
for _, fi := range fl {
if fi.IsDir() {
if tree {
err = errors.Wrap(os.RemoveAll(filepath.Join(destPath, fi.Name())), "remove all existing tree entries")
if err != nil {
return err
}
}
continue
}
err = errors.Wrap(os.Remove(filepath.Join(destPath, fi.Name())), "remove existing file")
if err != nil {
return err
}
}
// Copy files into dest.
srcDir, err := os.Open(srcPath)
if err != nil {
return errors.Wrap(err, "open srcPath directory")
}
fl, err = srcDir.Readdir(-1)
srcDir.Close()
if err != nil {
return errors.Wrap(err, "src readdir")
}
if h != nil {
// Write relative path to GOPATH.
h.Write([]byte(strings.Trim(pkgPath, "/")))
// Sort file list to present a stable hash.
sort.Sort(fileInfoSort(fl))
}
fileLoop:
for _, fi := range fl {
name := fi.Name()
if name[0] == '.' {
continue
}
if fi.IsDir() {
isTestdata := name == "testdata"
if !tree && !isTestdata {
continue
}
if name[0] == '_' {
continue
}
if ignoreTest {
if strings.HasSuffix(name, "_test") || isTestdata {
continue
}
}
nextDestPath := filepath.Join(destPath, name)
nextSrcPath := filepath.Join(srcPath, name)
var nextIgnoreFiles, deps []string
if !isTestdata && !strings.Contains(pkgPath, "/testdata/") {
nextIgnoreFiles, deps, err = ctx.getIngoreFiles(nextSrcPath)
if err != nil {
return err
}
}
if beforeCopy != nil {
err = beforeCopy(deps)
if err != nil {
return errors.Wrap(err, "beforeCopy")
}
}
err = ctx.CopyPackage(nextDestPath, nextSrcPath, lookRoot, path.Join(pkgPath, name), nextIgnoreFiles, true, h, beforeCopy)
if err != nil {
return errors.Wrapf(err,
"CopyPackage dest=%q src=%q lookRoot=%q pkgPath=%q ignoreFiles=%q tree=%t has beforeCopy=%t",
nextDestPath, nextSrcPath, lookRoot, path.Join(pkgPath, name), nextIgnoreFiles, true, beforeCopy != nil,
)
}
continue
}
for _, ignore := range ignoreFiles {
if pathos.FileStringEquals(name, ignore) {
continue fileLoop
}
}
if h != nil {
h.Write([]byte(name))
}
err = copyFile(
filepath.Join(destPath, name),
filepath.Join(srcPath, name),
h,
)
if err != nil {
return errors.Wrapf(err, "copyFile dest=%q src=%q", filepath.Join(destPath, name), filepath.Join(srcPath, name))
}
}
return errors.Wrapf(licenseCopy(lookRoot, srcPath, filepath.Join(ctx.RootDir, ctx.VendorFolder), pkgPath), "licenseCopy srcPath=%q", srcPath)
}
func copyFile(destPath, srcPath string, h hash.Hash) error {
ss, err := os.Stat(srcPath)
if err != nil {
return errors.Wrap(err, "copyFile Stat")
}
src, err := os.Open(srcPath)
if err != nil {
return errors.Wrapf(err, "open src=%q", srcPath)
}
defer src.Close()
// Ensure we are not trying to copy a directory. May happen with symlinks.
if st, err := src.Stat(); err == nil {
if st.IsDir() {
return nil
}
}
dest, err := os.Create(destPath)
if err != nil {
return errors.Wrapf(err, "create dest=%q", destPath)
}
r := io.Reader(src)
if h != nil {
r = io.TeeReader(src, h)
}
_, err = io.Copy(dest, r)
// Close before setting mod and time.
dest.Close()
if err != nil {
return errors.Wrap(err, "copy")
}
err = os.Chmod(destPath, ss.Mode())
if err != nil {
return err
}
return os.Chtimes(destPath, ss.ModTime(), ss.ModTime())
}

View File

@@ -0,0 +1,80 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package context
import (
"errors"
"fmt"
)
var (
// ErrMissingGOROOT returns if the GOROOT was not found.
ErrMissingGOROOT = errors.New("Unable to determine GOROOT.")
// ErrMissingGOPATH returns if no GOPATH was found.
ErrMissingGOPATH = errors.New("Missing GOPATH. Check your environment variable GOPATH.")
)
// ErrNotInGOPATH returns if not currently in the GOPATH.
type ErrNotInGOPATH struct {
Missing string
}
func (err ErrNotInGOPATH) Error() string {
return fmt.Sprintf("Package %q not a go package or not in GOPATH.", err.Missing)
}
// ErrDirtyPackage returns if package is in dirty version control.
type ErrDirtyPackage struct {
ImportPath string
}
func (err ErrDirtyPackage) Error() string {
return fmt.Sprintf("Package %q has uncommitted changes in the vcs.", err.ImportPath)
}
// ErrPackageExists returns if package already exists.
type ErrPackageExists struct {
Package string
}
func (err ErrPackageExists) Error() string {
return fmt.Sprintf("Package %q already in vendor.", err.Package)
}
// ErrMissingVendorFile returns if package already exists.
type ErrMissingVendorFile struct {
Path string
}
func (err ErrMissingVendorFile) Error() string {
return fmt.Sprintf("Vendor file at %q not found.", err.Path)
}
// ErrOldVersion returns if vendor file is not in the vendor folder.
type ErrOldVersion struct {
Message string
}
func (err ErrOldVersion) Error() string {
return fmt.Sprintf("The vendor file or is old. %s", err.Message)
}
type ErrTreeChildren struct {
path string
children []*Package
}
func (err ErrTreeChildren) Error() string {
return fmt.Sprintf("Cannot have a sub-tree %q contain sub-packages %q", err.path, err.children)
}
type ErrTreeParents struct {
path string
parents []string
}
func (err ErrTreeParents) Error() string {
return fmt.Sprintf("Cannot add package %q which is already found in sub-tree %q", err.path, err.parents)
}

View File

@@ -0,0 +1,291 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package context
import (
"fmt"
"os"
"path/filepath"
"strings"
"time"
"github.com/kardianos/govendor/internal/pathos"
"github.com/kardianos/govendor/pkgspec"
gvvcs "github.com/kardianos/govendor/vcs"
"github.com/kardianos/govendor/vendorfile"
"golang.org/x/tools/go/vcs"
)
type fetcher struct {
Ctx *Context
CacheRoot string
HavePkg map[string]bool
}
func newFetcher(ctx *Context) (*fetcher, error) {
// GOPATH here includes the "src" dir, go up one level.
cacheRoot := filepath.Join(ctx.RootGopath, "..", ".cache", "govendor")
err := os.MkdirAll(cacheRoot, 0700)
if err != nil {
return nil, err
}
return &fetcher{
Ctx: ctx,
CacheRoot: cacheRoot,
HavePkg: make(map[string]bool, 30),
}, nil
}
// op fetches the repo locally if not already present.
// Transform the fetch op into a copy op.
func (f *fetcher) op(op *Operation) ([]*Operation, error) {
// vcs.ShowCmd = true
var nextOps []*Operation
vpkg := f.Ctx.VendorFilePackagePath(op.Pkg.Path)
if vpkg == nil {
return nextOps, fmt.Errorf("Could not find vendor file package for %q. Internal error.", op.Pkg.Path)
}
op.Type = OpCopy
ps, err := pkgspec.Parse("", op.Src)
if err != nil {
return nextOps, err
}
if len(ps.Version) == 0 {
longest := ""
for _, pkg := range f.Ctx.Package {
if strings.HasPrefix(ps.Path, pkg.Path+"/") && len(pkg.Path) > len(longest) && pkg.HasVersion {
longest = pkg.Path
ps.Version = pkg.Version
ps.HasVersion = true
}
}
}
// Don't check for bundle, rather check physical directory.
// If no repo in dir, clone.
// If there is a repo in dir, update to latest.
// Get any tags.
// If we have a specific revision, update to that revision.
pkgDir := filepath.Join(f.CacheRoot, pathos.SlashToFilepath(ps.PathOrigin()))
sysVcsCmd, repoRoot, err := vcs.FromDir(pkgDir, f.CacheRoot)
var vcsCmd *VCSCmd
repoRootDir := filepath.Join(f.CacheRoot, repoRoot)
if err != nil {
rr, err := vcs.RepoRootForImportPath(ps.PathOrigin(), false)
if err != nil {
if strings.Contains(err.Error(), "unrecognized import path") {
return nextOps, nil
}
return nextOps, err
}
if !f.Ctx.Insecure && !vcsIsSecure(rr.Repo) {
return nextOps, fmt.Errorf("repo remote not secure")
}
vcsCmd = updateVcsCmd(rr.VCS)
repoRoot = rr.Root
repoRootDir = filepath.Join(f.CacheRoot, repoRoot)
err = vcsCmd.Create(repoRootDir, rr.Repo)
if err != nil {
return nextOps, fmt.Errorf("failed to create repo %q in %q %v", rr.Repo, repoRootDir, err)
}
} else {
vcsCmd = updateVcsCmd(sysVcsCmd)
err = vcsCmd.Download(repoRootDir)
if err != nil {
return nextOps, fmt.Errorf("failed to download repo into %q %v", repoRootDir, err)
}
}
revision := ""
if ps.HasVersion {
switch {
case len(ps.Version) == 0:
vpkg.Version = ""
case isVersion(ps.Version):
vpkg.Version = ps.Version
default:
revision = ps.Version
}
}
switch {
case len(revision) == 0 && len(vpkg.Version) > 0:
fmt.Fprintf(f.Ctx, "Get version %q@%s\n", vpkg.Path, vpkg.Version)
// Get a list of tags, match to version if possible.
var tagNames []string
tagNames, err = vcsCmd.Tags(repoRootDir)
if err != nil {
return nextOps, fmt.Errorf("failed to fetch tags %v", err)
}
labels := make([]Label, len(tagNames))
for i, tag := range tagNames {
labels[i].Source = LabelTag
labels[i].Text = tag
}
result := FindLabel(vpkg.Version, labels)
if result.Source == LabelNone {
return nextOps, fmt.Errorf("No label found for specified version %q from %s", vpkg.Version, ps.String())
}
vpkg.VersionExact = result.Text
fmt.Fprintf(f.Ctx, "\tFound exact version %q\n", vpkg.VersionExact)
err = vcsCmd.TagSync(repoRootDir, result.Text)
if err != nil {
return nextOps, fmt.Errorf("failed to sync repo to tag %q %v", result.Text, err)
}
case len(revision) > 0:
fmt.Fprintf(f.Ctx, "Get specific revision %q@%s\n", vpkg.Path, revision)
// Get specific version.
vpkg.Version = ""
vpkg.VersionExact = ""
err = vcsCmd.RevisionSync(repoRootDir, revision)
if err != nil {
return nextOps, fmt.Errorf("failed to sync repo to revision %q %v", revision, err)
}
default:
fmt.Fprintf(f.Ctx, "Get latest revision %q\n", vpkg.Path)
// Get latest version.
err = vcsCmd.TagSync(repoRootDir, "")
if err != nil {
return nextOps, fmt.Errorf("failed to sync to latest revision %v", err)
}
}
// set op.Src to download dir.
// /tmp/cache/1/[[github.com/kardianos/govendor]]context
op.Src = pkgDir
var deps []string
op.IgnoreFile, deps, err = f.Ctx.getIngoreFiles(op.Src)
if err != nil {
if os.IsNotExist(err) {
return nextOps, nil
}
return nextOps, fmt.Errorf("failed to get ignore files and deps from %q %v", op.Src, err)
}
f.HavePkg[ps.Path] = true
// Once downloaded, be sure to set the revision and revisionTime
// in the vendor file package.
// Find the VCS information.
system, err := gvvcs.FindVcs(f.CacheRoot, op.Src)
if err != nil {
return nextOps, fmt.Errorf("failed to find vcs in %q %v", op.Src, err)
}
if system != nil {
if system.Dirty {
return nextOps, ErrDirtyPackage{ps.PathOrigin()}
}
vpkg.Revision = system.Revision
if system.RevisionTime != nil {
vpkg.RevisionTime = system.RevisionTime.UTC().Format(time.RFC3339)
}
}
processDeps := func(deps []string) error {
// Queue up any missing package deps.
depLoop:
for _, dep := range deps {
dep = strings.TrimSpace(dep)
if len(dep) == 0 {
continue
}
// Check for deps we already have.
if f.HavePkg[dep] {
continue
}
for _, test := range f.Ctx.Package {
if test.Path == dep {
switch test.Status.Location {
case LocationVendor, LocationLocal:
continue depLoop
}
}
}
// Look for std lib deps
var yes bool
yes, err = f.Ctx.isStdLib(dep)
if err != nil {
return fmt.Errorf("Failed to check if in stdlib: %v", err)
}
if yes {
continue
}
// Look for tree deps.
if op.Pkg.IncludeTree && strings.HasPrefix(dep, op.Pkg.Path+"/") {
continue
}
version := ""
hasVersion := false
revision := ""
for _, vv := range f.Ctx.VendorFile.Package {
if vv.Remove {
continue
}
if strings.HasPrefix(dep, vv.Path+"/") {
if len(vv.Version) > 0 {
version = vv.Version
hasVersion = true
revision = vv.Revision
break
}
if len(vv.Revision) > 0 {
revision = vv.Revision
}
}
}
f.HavePkg[dep] = true
dest := filepath.Join(f.Ctx.RootDir, f.Ctx.VendorFolder, dep)
// Update vendor file with correct Local field.
vp := f.Ctx.VendorFilePackagePath(dep)
if vp == nil {
vp = &vendorfile.Package{
Add: true,
Path: dep,
Revision: revision,
Version: version,
}
f.Ctx.VendorFile.Package = append(f.Ctx.VendorFile.Package, vp)
}
if hasVersion {
vp.Version = version
}
if len(vp.Revision) == 0 {
vp.Revision = revision
}
spec := &pkgspec.Pkg{Path: dep, Version: version, HasVersion: hasVersion}
nextOps = append(nextOps, &Operation{
Type: OpFetch,
Pkg: &Package{Pkg: spec},
Src: spec.String(),
Dest: dest,
})
}
return nil
}
err = processDeps(deps)
if err != nil {
return nextOps, err
}
err = f.Ctx.copyOperation(op, processDeps)
if err != nil {
return nextOps, err
}
return nextOps, nil
}

View File

@@ -0,0 +1,94 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package context
import (
"fmt"
"io"
"os"
"path/filepath"
"github.com/kardianos/govendor/pkgspec"
"golang.org/x/tools/go/vcs"
)
func Get(logger io.Writer, pkgspecName string, insecure bool) (*pkgspec.Pkg, error) {
// Get the GOPATHs.
all := os.Getenv("GOPATH")
if len(all) == 0 {
return nil, ErrMissingGOPATH
}
gopathList := filepath.SplitList(all)
gopath := gopathList[0]
cwd, err := os.Getwd()
if err != nil {
return nil, err
}
ps, err := pkgspec.Parse(cwd, pkgspecName)
if err != nil {
return nil, err
}
return ps, get(logger, filepath.Join(gopath, "src"), ps, insecure)
}
func get(logger io.Writer, gopath string, ps *pkgspec.Pkg, insecure bool) error {
pkgDir := filepath.Join(gopath, ps.Path)
sysVcsCmd, repoRoot, err := vcs.FromDir(pkgDir, gopath)
var vcsCmd *VCSCmd
repoRootDir := filepath.Join(gopath, repoRoot)
if err != nil {
rr, err := vcs.RepoRootForImportPath(ps.PathOrigin(), false)
if err != nil {
return err
}
if !insecure && !vcsIsSecure(rr.Repo) {
return fmt.Errorf("repo remote not secure")
}
vcsCmd = updateVcsCmd(rr.VCS)
repoRoot = rr.Root
repoRootDir = filepath.Join(gopath, repoRoot)
err = vcsCmd.Create(repoRootDir, rr.Repo)
if err != nil {
return fmt.Errorf("failed to create repo %q in %q %v", rr.Repo, repoRootDir, err)
}
} else {
vcsCmd = updateVcsCmd(sysVcsCmd)
err = vcsCmd.Download(repoRootDir)
if err != nil {
return fmt.Errorf("failed to download repo into %q %v", repoRootDir, err)
}
}
err = os.MkdirAll(filepath.Join(repoRootDir, "vendor"), 0777)
if err != nil {
return err
}
ctx, err := NewContext(repoRootDir, filepath.Join("vendor", vendorFilename), "vendor", false)
if err != nil {
return err
}
ctx.Insecure = insecure
ctx.Logger = logger
statusList, err := ctx.Status()
if err != nil {
return err
}
added := make(map[string]bool, len(statusList))
for _, item := range statusList {
switch item.Status.Location {
case LocationExternal, LocationNotFound:
if added[item.Pkg.Path] {
continue
}
ctx.ModifyImport(item.Pkg, Fetch)
added[item.Pkg.Path] = true
}
}
defer ctx.WriteVendorFile()
return ctx.Alter()
}

View File

@@ -0,0 +1,240 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package context
import (
"bytes"
"fmt"
"sort"
"strconv"
"strings"
"unicode"
)
type LabelSource byte
const (
LabelNone LabelSource = iota
LabelBranch
LabelTag
)
func (ls LabelSource) String() string {
switch ls {
default:
panic("unknown label source")
case LabelNone:
return "none"
case LabelBranch:
return "branch"
case LabelTag:
return "tag"
}
}
type Label struct {
Text string
Source LabelSource
}
func (l Label) String() string {
return fmt.Sprintf("[%s]%s", l.Source, l.Text)
}
type labelGroup struct {
seq string
sections []labelSection
}
type labelSection struct {
seq string
number int64
brokenBy rune
}
type labelAnalysis struct {
Label Label
Groups []labelGroup
}
func (item *labelAnalysis) fillSections(buf *bytes.Buffer) {
previousNumber := false
number := false
isBreak := func(r rune) bool {
return r == '.'
}
add := func(r rune, group *labelGroup) {
if buf.Len() > 0 {
sVal := buf.String()
buf.Reset()
value, err := strconv.ParseInt(sVal, 10, 64)
if err != nil {
value = -1
}
if isBreak(r) == false {
r = 0
}
group.sections = append(group.sections, labelSection{
seq: sVal,
number: value,
brokenBy: r,
})
}
}
for _, groupText := range strings.Split(item.Label.Text, "-") {
group := labelGroup{
seq: groupText,
}
for index, r := range groupText {
number = unicode.IsNumber(r)
different := number != previousNumber && index > 0
previousNumber = number
if isBreak(r) {
add(r, &group)
continue
}
if different {
add(r, &group)
buf.WriteRune(r)
continue
}
buf.WriteRune(r)
}
add(0, &group)
buf.Reset()
item.Groups = append(item.Groups, group)
}
}
type labelAnalysisList []*labelAnalysis
func (l labelAnalysisList) Len() int {
return len(l)
}
func (l labelAnalysisList) Swap(i, j int) {
l[i], l[j] = l[j], l[i]
}
func (l labelAnalysisList) Less(i, j int) bool {
const debug = false
df := func(f string, a ...interface{}) {
if debug {
fmt.Printf(f, a...)
}
}
a := l[i]
b := l[j]
// Want to return the *smaller* of the two group counts.
if len(a.Groups) != len(b.Groups) {
return len(a.Groups) < len(b.Groups)
}
gct := len(a.Groups)
if gct > len(b.Groups) {
gct = len(b.Groups)
}
df(":: %s vs %s ::\n", a.Label.Text, b.Label.Text)
for ig := 0; ig < gct; ig++ {
ga := a.Groups[ig]
gb := b.Groups[ig]
if ga.seq == gb.seq {
df("pt 1 %q\n", ga.seq)
continue
}
ct := len(ga.sections)
if ct > len(gb.sections) {
ct = len(gb.sections)
}
// Compare common sections.
for i := 0; i < ct; i++ {
sa := ga.sections[i]
sb := gb.sections[i]
// Sort each section by number and alpha.
if sa.number != sb.number {
df("PT A\n")
return sa.number > sb.number
}
if sa.seq != sb.seq {
df("PT B\n")
return sa.seq > sb.seq
}
}
// Sections that we can compare are equal, we want
// the longer of the two sections if lengths un-equal.
if len(ga.sections) != len(gb.sections) {
return len(ga.sections) > len(gb.sections)
}
}
// At this point we have same number of groups and same number
// of sections. We can assume the labels are the same.
// Check to see if the source of the label is different.
if a.Label.Source != b.Label.Source {
if a.Label.Source == LabelBranch {
df("PT C\n")
return true
}
}
// We ran out of things to check. Assume one is not "less" then the other.
df("PT D\n")
return false
}
// FindLabel matches a single label from a list of labels, given a version.
// If the returning label.Source is LabelNone, then no labels match.
//
// Labels are first broken into sections separated by "-". Shortest wins.
// If they have the same number of above sections, then they are compared
// further. Number sequences are treated as numbers. Numbers do not need a
// separator. The "." is a break point as well.
func FindLabel(version string, labels []Label) Label {
list := make([]*labelAnalysis, 0, 6)
exact := strings.HasPrefix(version, "=")
version = strings.TrimPrefix(version, "=")
for _, label := range labels {
if exact {
if label.Text == version {
return label
}
continue
}
if strings.HasPrefix(label.Text, version) == false {
continue
}
remain := strings.TrimPrefix(label.Text, version)
if len(remain) > 0 {
next := remain[0]
// The stated version must either be the full label,
// followed by a "." or "-".
if next != '.' && next != '-' {
continue
}
}
list = append(list, &labelAnalysis{
Label: label,
Groups: make([]labelGroup, 0, 3),
})
}
if len(list) == 0 {
return Label{Source: LabelNone}
}
buf := &bytes.Buffer{}
for _, item := range list {
item.fillSections(buf)
buf.Reset()
}
sort.Sort(labelAnalysisList(list))
return list[0].Label
}

View File

@@ -0,0 +1,217 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package context
import (
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"strings"
"github.com/kardianos/govendor/internal/pathos"
"github.com/pkg/errors"
)
type License struct {
Path string
Filename string
Text string
}
type LicenseSort []License
func (list LicenseSort) Len() int {
return len(list)
}
func (list LicenseSort) Swap(i, j int) {
list[i], list[j] = list[j], list[i]
}
func (list LicenseSort) Less(i, j int) bool {
a, b := list[i], list[j]
if a.Path == b.Path {
return a.Filename < b.Filename
}
return a.Path < b.Path
}
type licenseSearchType byte
const (
licensePrefix licenseSearchType = iota
licenseSubstring
licenseSuffix
)
type licenseSearch struct {
Text string
Search licenseSearchType
}
func (t licenseSearchType) Test(filename, test string) bool {
switch t {
case licensePrefix:
return strings.HasPrefix(filename, test)
case licenseSubstring:
return strings.Contains(filename, test)
case licenseSuffix:
return strings.HasSuffix(filename, test)
}
return false
}
type licenseTest interface {
Test(filename, test string) bool
}
// licenses lists the filenames to copy over to the vendor folder.
var licenses = []licenseSearch{
{Text: "license", Search: licensePrefix},
{Text: "unlicense", Search: licensePrefix},
{Text: "copying", Search: licensePrefix},
{Text: "copyright", Search: licensePrefix},
{Text: "copyright", Search: licensePrefix},
{Text: "legal", Search: licenseSubstring},
{Text: "notice", Search: licenseSubstring},
{Text: "disclaimer", Search: licenseSubstring},
{Text: "patent", Search: licenseSubstring},
{Text: "third-party", Search: licenseSubstring},
{Text: "thirdparty", Search: licenseSubstring},
}
var licenseNotExt = []string{
".go",
".c",
".h",
".cpp",
".hpp",
}
func isLicenseFile(name string) bool {
cname := strings.ToLower(name)
for _, X := range licenseNotExt {
if filepath.Ext(name) == X {
return false
}
}
for _, L := range licenses {
if L.Search.Test(cname, L.Text) {
return true
}
}
return false
}
// licenseWalk starts in a folder and searches up the folder tree
// for license like files. Found files are reported to the found function.
func licenseWalk(root, startIn string, found func(folder, name string) error) error {
folder := startIn
for i := 0; i <= looplimit; i++ {
dir, err := os.Open(folder)
if err != nil {
return err
}
fl, err := dir.Readdir(-1)
dir.Close()
if err != nil {
return err
}
for _, fi := range fl {
name := fi.Name()
if name[0] == '.' {
continue
}
if fi.IsDir() {
continue
}
if !isLicenseFile(name) {
continue
}
err = found(folder, name)
if err != nil {
return err
}
}
if len(folder) <= len(root) {
return nil
}
nextFolder := filepath.Clean(filepath.Join(folder, ".."))
if nextFolder == folder {
return nil
}
folder = nextFolder
}
panic("licenseFind loop limit")
}
// licenseCopy starts the search in the parent of "startIn" folder.
// Looks in all sub-folders until root is reached. The root itself is not
// searched.
func licenseCopy(root, startIn, vendorRoot, pkgPath string) error {
addTo, _ := pathos.TrimCommonSuffix(pathos.SlashToFilepath(pkgPath), startIn)
startIn = filepath.Clean(filepath.Join(startIn, ".."))
return licenseWalk(root, startIn, func(folder, name string) error {
srcPath := filepath.Join(folder, name)
trimTo := pathos.FileTrimPrefix(getLastVendorRoot(folder), root)
/*
Path: "golang.org/x/tools/go/vcs"
Root: "/tmp/govendor-cache280388238/1"
StartIn: "/tmp/govendor-cache280388238/1/go/vcs"
addTo: "golang.org/x/tools"
$PROJ/vendor + addTo + pathos.FileTrimPrefix(folder, root) + "LICENSE"
*/
destPath := filepath.Join(vendorRoot, addTo, trimTo, name)
// Only copy if file does not exist.
_, err := os.Stat(srcPath)
if err != nil {
return errors.Errorf("Source license path doesn't exist %q", srcPath)
}
destDir, _ := filepath.Split(destPath)
os.MkdirAll(destDir, 0777)
return errors.Wrapf(copyFile(destPath, srcPath, nil), "copyFile dest=%q src=%q", destPath, srcPath)
})
}
func getLastVendorRoot(s string) string {
w := strings.Replace(s, "\\", "/", -1)
ix := strings.LastIndex(w, "/vendor/")
if ix < 0 {
return s
}
return s[ix+len("/vendor"):]
}
// LicenseDiscover looks for license files in a given path.
func LicenseDiscover(root, startIn, overridePath string, list map[string]License) error {
return licenseWalk(root, startIn, func(folder, name string) error {
ipath := pathos.SlashToImportPath(strings.TrimPrefix(folder, root))
if len(overridePath) > 0 {
ipath = overridePath
}
if _, found := list[ipath]; found {
return nil
}
p := filepath.Join(folder, name)
text, err := ioutil.ReadFile(p)
if err != nil {
return fmt.Errorf("Failed to read license file %q %v", p, err)
}
key := path.Join(ipath, name)
list[key] = License{
Path: ipath,
Filename: name,
Text: string(text),
}
return nil
})
}

View File

@@ -0,0 +1,775 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package context gathers the status of packages and stores it in Context.
// A new Context needs to be pointed to the root of the project and any
// project owned vendor file.
package context
import (
"bytes"
"crypto/sha1"
"encoding/base64"
"fmt"
"math"
"path"
"path/filepath"
"strings"
"time"
"github.com/kardianos/govendor/internal/pathos"
os "github.com/kardianos/govendor/internal/vos"
"github.com/kardianos/govendor/pkgspec"
"github.com/kardianos/govendor/vcs"
"github.com/kardianos/govendor/vendorfile"
"github.com/pkg/errors"
)
// OperationState is the state of the given package move operation.
type OperationState byte
const (
OpReady OperationState = iota // Operation is ready to go.
OpIgnore // Operation should be ignored.
OpDone // Operation has been completed.
)
type OperationType byte
const (
OpCopy OperationType = iota
OpRemove
OpFetch
)
func (t OperationType) String() string {
switch t {
default:
panic("unknown operation type")
case OpCopy:
return "copy"
case OpRemove:
return "remove"
case OpFetch:
return "fetch"
}
}
// Operation defines how packages should be moved.
//
// TODO (DT): Remove Pkg field and change Src and Dest to *pkgspec.Pkg types.
type Operation struct {
Type OperationType
Pkg *Package
// Source file path to move packages from.
// Must not be empty.
Src string
// Destination file path to move package to.
// If Dest if empty the package is removed.
Dest string
// Files to ignore for operation.
IgnoreFile []string
State OperationState
// True if the operation should treat the package as uncommitted.
Uncommitted bool
}
// Conflict reports packages that are scheduled to conflict.
type Conflict struct {
Canonical string
Local string
Operation []*Operation
OpIndex int
Resolved bool
}
// Modify is the type of modifcation to do.
type Modify byte
const (
AddUpdate Modify = iota // Add or update the import.
Add // Only add, error if it already exists.
Update // Only update, error if it doesn't currently exist.
Remove // Remove from vendor path.
Fetch // Get directly from remote repository.
)
type ModifyOption byte
const (
Uncommitted ModifyOption = iota
MatchTree
IncludeTree
)
// ModifyStatus adds packages to the context by status.
func (ctx *Context) ModifyStatus(sg StatusGroup, mod Modify, mops ...ModifyOption) error {
if ctx.added == nil {
ctx.added = make(map[string]bool, 10)
}
list, err := ctx.Status()
if err != nil {
return err
}
// Add packages from status.
statusLoop:
for _, item := range list {
if !item.Status.MatchGroup(sg) {
continue
}
if ctx.added[item.Pkg.PathOrigin()] {
continue
}
// Do not add excluded packages
if item.Status.Presence == PresenceExcluded {
continue
}
// Do not attempt to add any existing status items that are
// already present in vendor folder.
if mod == Add {
if ctx.VendorFilePackagePath(item.Pkg.Path) != nil {
continue
}
for _, pkg := range ctx.Package {
if pkg.Status.Location == LocationVendor && item.Pkg.Path == pkg.Path {
continue statusLoop
}
}
}
err = ctx.modify(item.Pkg, mod, mops)
if err != nil {
// Skip these errors if from status.
if _, is := err.(ErrTreeChildren); is {
continue
}
if _, is := err.(ErrTreeParents); is {
continue
}
return err
}
}
return nil
}
// ModifyImport adds the package to the context.
func (ctx *Context) ModifyImport(imp *pkgspec.Pkg, mod Modify, mops ...ModifyOption) error {
var err error
if ctx.added == nil {
ctx.added = make(map[string]bool, 10)
}
// Grap the origin of the pkg spec from the vendor file as needed.
if len(imp.Origin) == 0 {
for _, vpkg := range ctx.VendorFile.Package {
if vpkg.Remove {
continue
}
if vpkg.Path == imp.Path {
imp.Origin = vpkg.Origin
}
}
}
if !imp.MatchTree {
if !ctx.added[imp.PathOrigin()] {
err = ctx.modify(imp, mod, mops)
if err != nil {
return err
}
}
return nil
}
list, err := ctx.Status()
if err != nil {
return err
}
// If add any matched from "...".
match := imp.Path + "/"
for _, item := range list {
if ctx.added[item.Pkg.PathOrigin()] {
continue
}
if item.Pkg.Path != imp.Path && !strings.HasPrefix(item.Pkg.Path, match) {
continue
}
if imp.HasVersion {
item.Pkg.HasVersion = true
item.Pkg.Version = imp.Version
}
item.Pkg.Origin = path.Join(imp.PathOrigin(), strings.TrimPrefix(item.Pkg.Path, imp.Path))
err = ctx.modify(item.Pkg, mod, mops)
if err != nil {
return err
}
}
return nil
}
func (ctx *Context) modify(ps *pkgspec.Pkg, mod Modify, mops []ModifyOption) error {
ctx.added[ps.PathOrigin()] = true
for _, mop := range mops {
switch mop {
default:
panic("unknown case")
case Uncommitted:
ps.Uncommitted = true
case MatchTree:
ps.MatchTree = true
case IncludeTree:
ps.IncludeTree = true
}
}
var err error
if !ctx.loaded || ctx.dirty {
err = ctx.loadPackage()
if err != nil {
return err
}
}
tree := ps.IncludeTree
switch mod {
// Determine if we can find the source path from an add or update.
case Add, Update, AddUpdate:
_, _, err = ctx.findImportDir("", ps.PathOrigin())
if err != nil {
return err
}
}
// Does the local import exist?
// If so either update or just return.
// If not find the disk path from the canonical path, copy locally and rewrite (if needed).
var pkg *Package
var foundPkg bool
if !foundPkg {
localPath := path.Join(ctx.RootImportPath, ctx.VendorFolder, ps.Path)
pkg, foundPkg = ctx.Package[localPath]
foundPkg = foundPkg && pkg.Status.Presence != PresenceMissing
}
if !foundPkg {
pkg, foundPkg = ctx.Package[ps.Path]
foundPkg = foundPkg && pkg.Status.Presence != PresenceMissing
}
if !foundPkg {
pkg, foundPkg = ctx.Package[ps.PathOrigin()]
foundPkg = foundPkg && pkg.Status.Presence != PresenceMissing
}
if !foundPkg {
pkg, err = ctx.addSingleImport(ctx.RootDir, ps.PathOrigin(), tree)
if err != nil {
return err
}
if pkg == nil {
return nil
}
pkg.Origin = ps.PathOrigin()
pkg.Path = ps.Path
}
pkg.HasOrigin = ps.HasOrigin
if ps.HasOrigin {
pkg.Origin = ps.Origin
}
// Do not support setting "tree" on Remove.
if tree && mod != Remove {
pkg.IncludeTree = true
}
// A restriction where packages cannot live inside a tree package.
if mod != Remove {
if pkg.IncludeTree {
children := ctx.findPackageChild(pkg)
if len(children) > 0 {
return ErrTreeChildren{path: pkg.Path, children: children}
}
}
treeParents := ctx.findPackageParentTree(pkg)
if len(treeParents) > 0 {
return ErrTreeParents{path: pkg.Path, parents: treeParents}
}
}
// TODO (DT): figure out how to upgrade a non-tree package to a tree package with correct checks.
localExists, err := hasGoFileInFolder(filepath.Join(ctx.RootDir, ctx.VendorFolder, pathos.SlashToFilepath(ps.Path)))
if err != nil {
return err
}
if mod == Add && localExists {
return ErrPackageExists{path.Join(ctx.RootImportPath, ctx.VendorFolder, ps.Path)}
}
dprintf("stage 2: begin!\n")
switch mod {
case Add:
return ctx.modifyAdd(pkg, ps.Uncommitted)
case AddUpdate:
return ctx.modifyAdd(pkg, ps.Uncommitted)
case Update:
return ctx.modifyAdd(pkg, ps.Uncommitted)
case Remove:
return ctx.modifyRemove(pkg)
case Fetch:
return ctx.modifyFetch(pkg, ps.Uncommitted, ps.HasVersion, ps.Version)
default:
panic("mod switch: case not handled")
}
}
func (ctx *Context) getIngoreFiles(src string) (ignoreFile, imports []string, err error) {
srcDir, err := os.Open(src)
if err != nil {
return nil, nil, err
}
fl, err := srcDir.Readdir(-1)
srcDir.Close()
if err != nil {
return nil, nil, err
}
importMap := make(map[string]struct{}, 12)
imports = make([]string, 0, 12)
for _, fi := range fl {
if fi.IsDir() {
continue
}
if fi.Name()[0] == '.' {
continue
}
tags, fileImports, err := ctx.getFileTags(filepath.Join(src, fi.Name()), nil)
if err != nil {
return nil, nil, err
}
if tags.IgnoreItem(ctx.ignoreTag...) {
ignoreFile = append(ignoreFile, fi.Name())
} else {
// Only add imports for non-ignored files.
for _, imp := range fileImports {
importMap[imp] = struct{}{}
}
}
}
for imp := range importMap {
imports = append(imports, imp)
}
return ignoreFile, imports, nil
}
func (ctx *Context) modifyAdd(pkg *Package, uncommitted bool) error {
var err error
src := pkg.OriginDir
dprintf("found import: %q\n", src)
// If the canonical package is also the local package, then the package
// isn't copied locally already and has already been checked for tags.
// If it has been vendored the source still needs to be examined.
// Examine here and add to the operations list.
var ignoreFile []string
if cpkg, found := ctx.Package[pkg.Path]; found {
ignoreFile = cpkg.ignoreFile
} else {
var err error
ignoreFile, _, err = ctx.getIngoreFiles(src)
if err != nil {
return err
}
}
dest := filepath.Join(ctx.RootDir, ctx.VendorFolder, pathos.SlashToFilepath(pkg.Path))
// TODO: This might cause other issues or might be hiding the underlying issues. Examine in depth later.
if pathos.FileStringEquals(src, dest) {
return nil
}
dprintf("add op: %q\n", src)
// Update vendor file with correct Local field.
vp := ctx.VendorFilePackagePath(pkg.Path)
if vp == nil {
vp = &vendorfile.Package{
Add: true,
Path: pkg.Path,
}
ctx.VendorFile.Package = append(ctx.VendorFile.Package, vp)
}
if pkg.IncludeTree {
vp.Tree = pkg.IncludeTree
}
if pkg.HasOrigin {
vp.Origin = pkg.Origin
}
if pkg.Path != pkg.Local && pkg.inVendor && vp.Add {
vp.Origin = pkg.Local
}
// Find the VCS information.
system, err := vcs.FindVcs(pkg.Gopath, src)
if err != nil {
return err
}
dirtyAndUncommitted := false
if system != nil {
if system.Dirty {
if !uncommitted {
return ErrDirtyPackage{pkg.Path}
}
dirtyAndUncommitted = true
if len(vp.ChecksumSHA1) == 0 {
vp.ChecksumSHA1 = "uncommitted/version="
}
} else {
vp.Revision = system.Revision
if system.RevisionTime != nil {
vp.RevisionTime = system.RevisionTime.UTC().Format(time.RFC3339)
}
}
}
ctx.Operation = append(ctx.Operation, &Operation{
Type: OpCopy,
Pkg: pkg,
Src: src,
Dest: dest,
IgnoreFile: ignoreFile,
Uncommitted: dirtyAndUncommitted,
})
if !ctx.rewriteImports {
return nil
}
mvSet := make(map[*Package]struct{}, 3)
ctx.makeSet(pkg, mvSet)
for r := range mvSet {
to := path.Join(ctx.RootImportPath, ctx.VendorFolder, r.Path)
dprintf("RULE: %s -> %s\n", r.Local, to)
ctx.RewriteRule[r.Path] = to
ctx.RewriteRule[r.Local] = to
}
return nil
}
func (ctx *Context) modifyRemove(pkg *Package) error {
// Update vendor file with correct Local field.
vp := ctx.VendorFilePackagePath(pkg.Path)
if vp != nil {
vp.Remove = true
}
if len(pkg.Dir) == 0 {
return nil
}
// Protect non-project paths from being removed.
if pathos.FileHasPrefix(pkg.Dir, ctx.RootDir) == false {
return nil
}
if pkg.Status.Location == LocationLocal {
return nil
}
ctx.Operation = append(ctx.Operation, &Operation{
Type: OpRemove,
Pkg: pkg,
Src: pkg.Dir,
Dest: "",
})
if !ctx.rewriteImports {
return nil
}
mvSet := make(map[*Package]struct{}, 3)
ctx.makeSet(pkg, mvSet)
for r := range mvSet {
dprintf("RULE: %s -> %s\n", r.Local, r.Path)
ctx.RewriteRule[r.Local] = r.Path
}
return nil
}
// modify function to fetch given package.
func (ctx *Context) modifyFetch(pkg *Package, uncommitted, hasVersion bool, version string) error {
vp := ctx.VendorFilePackagePath(pkg.Path)
if vp == nil {
vp = &vendorfile.Package{
Add: true,
Path: pkg.Path,
}
ctx.VendorFile.Package = append(ctx.VendorFile.Package, vp)
}
if hasVersion {
vp.Version = version
pkg.Version = version
pkg.HasVersion = true
}
if pkg.IncludeTree {
vp.Tree = pkg.IncludeTree
}
pkg.Origin = strings.TrimPrefix(pkg.Origin, ctx.RootImportPath+"/"+ctx.VendorFolder+"/")
vp.Origin = pkg.Origin
origin := vp.Origin
if len(vp.Origin) == 0 {
origin = vp.Path
}
ps := &pkgspec.Pkg{
Path: pkg.Path,
Origin: origin,
HasVersion: hasVersion,
Version: version,
}
dest := filepath.Join(ctx.RootDir, ctx.VendorFolder, pathos.SlashToFilepath(pkg.Path))
ctx.Operation = append(ctx.Operation, &Operation{
Type: OpFetch,
Pkg: pkg,
Src: ps.String(),
Dest: dest,
})
return nil
}
// Check returns any conflicts when more then one package can be moved into
// the same path.
func (ctx *Context) Check() []*Conflict {
// Find duplicate packages that have been marked for moving.
findDups := make(map[string][]*Operation, 3) // map[canonical][]local
for _, op := range ctx.Operation {
if op.State != OpReady {
continue
}
findDups[op.Pkg.Path] = append(findDups[op.Pkg.Path], op)
}
var ret []*Conflict
for canonical, lop := range findDups {
if len(lop) == 1 {
continue
}
destDir := path.Join(ctx.RootImportPath, ctx.VendorFolder, canonical)
ret = append(ret, &Conflict{
Canonical: canonical,
Local: destDir,
Operation: lop,
})
}
return ret
}
// ResolveApply applies the conflict resolution selected. It chooses the
// Operation listed in the OpIndex field.
func (ctx *Context) ResloveApply(cc []*Conflict) {
for _, c := range cc {
if c.Resolved == false {
continue
}
for i, op := range c.Operation {
if op.State != OpReady {
continue
}
if i == c.OpIndex {
if vp := ctx.VendorFilePackagePath(c.Canonical); vp != nil {
vp.Origin = c.Local
}
continue
}
op.State = OpIgnore
}
}
}
// ResolveAutoLongestPath finds the longest local path in each conflict
// and set it to be used.
func ResolveAutoLongestPath(cc []*Conflict) []*Conflict {
for _, c := range cc {
if c.Resolved {
continue
}
longestLen := 0
longestIndex := 0
for i, op := range c.Operation {
if op.State != OpReady {
continue
}
if len(op.Pkg.Local) > longestLen {
longestLen = len(op.Pkg.Local)
longestIndex = i
}
}
c.OpIndex = longestIndex
c.Resolved = true
}
return cc
}
// ResolveAutoShortestPath finds the shortest local path in each conflict
// and set it to be used.
func ResolveAutoShortestPath(cc []*Conflict) []*Conflict {
for _, c := range cc {
if c.Resolved {
continue
}
shortestLen := math.MaxInt32
shortestIndex := 0
for i, op := range c.Operation {
if op.State != OpReady {
continue
}
if len(op.Pkg.Local) < shortestLen {
shortestLen = len(op.Pkg.Local)
shortestIndex = i
}
}
c.OpIndex = shortestIndex
c.Resolved = true
}
return cc
}
// ResolveAutoVendorFileOrigin resolves conflicts based on the vendor file
// if possible.
func (ctx *Context) ResolveAutoVendorFileOrigin(cc []*Conflict) []*Conflict {
for _, c := range cc {
if c.Resolved {
continue
}
vp := ctx.VendorFilePackagePath(c.Canonical)
if vp == nil {
continue
}
// If this was just added, we still can't rely on it.
// We still need to ask user.
if vp.Add {
continue
}
lookFor := vp.Path
if len(vp.Origin) != 0 {
lookFor = vp.Origin
}
for i, op := range c.Operation {
if op.State != OpReady {
continue
}
if op.Pkg.Local == lookFor {
c.OpIndex = i
c.Resolved = true
break
}
}
}
return cc
}
// Alter runs any requested package alterations.
func (ctx *Context) Alter() error {
ctx.added = nil
// Ensure there are no conflicts at this time.
buf := &bytes.Buffer{}
for _, conflict := range ctx.Check() {
buf.WriteString(fmt.Sprintf("Different Canonical Packages for %s\n", conflict.Canonical))
for _, op := range conflict.Operation {
buf.WriteString(fmt.Sprintf("\t%s\n", op.Pkg.Local))
}
}
if buf.Len() != 0 {
return errors.New(buf.String())
}
var err error
fetch, err := newFetcher(ctx)
if err != nil {
return err
}
for {
var nextOps []*Operation
for _, op := range ctx.Operation {
if op.State != OpReady {
continue
}
switch op.Type {
case OpFetch:
var ops []*Operation
// Download packages, transform fetch op into a copy op.
ops, err = fetch.op(op)
if len(ops) > 0 {
nextOps = append(nextOps, ops...)
}
}
if err != nil {
return errors.Wrapf(err, "Failed to fetch package %q", op.Pkg.Path)
}
}
if len(nextOps) == 0 {
break
}
ctx.Operation = append(ctx.Operation, nextOps...)
}
// Move and possibly rewrite packages.
for _, op := range ctx.Operation {
if op.State != OpReady {
continue
}
pkg := op.Pkg
if pathos.FileStringEquals(op.Dest, op.Src) {
panic("For package " + pkg.Local + " attempt to copy to same location: " + op.Src)
}
dprintf("MV: %s (%q -> %q)\n", pkg.Local, op.Src, op.Dest)
// Copy the package or remove.
switch op.Type {
default:
panic("unknown operation type")
case OpRemove:
ctx.dirty = true
err = RemovePackage(op.Src, filepath.Join(ctx.RootDir, ctx.VendorFolder), pkg.IncludeTree)
op.State = OpDone
case OpCopy:
err = ctx.copyOperation(op, nil)
if os.IsNotExist(errors.Cause(err)) {
// Ignore packages that don't exist, like appengine.
err = nil
}
}
if err != nil {
return errors.Wrapf(err, "Failed to %v package %q -> %q", op.Type, op.Src, op.Dest)
}
}
if ctx.rewriteImports {
return ctx.rewrite()
}
return nil
}
func (ctx *Context) copyOperation(op *Operation, beforeCopy func(deps []string) error) error {
var err error
pkg := op.Pkg
ctx.dirty = true
h := sha1.New()
var checksum []byte
root, _ := pathos.TrimCommonSuffix(op.Src, pkg.Path)
err = ctx.CopyPackage(op.Dest, op.Src, root, pkg.Path, op.IgnoreFile, pkg.IncludeTree, h, beforeCopy)
if err == nil && !op.Uncommitted {
checksum = h.Sum(nil)
vpkg := ctx.VendorFilePackagePath(pkg.Path)
if vpkg != nil {
vpkg.ChecksumSHA1 = base64.StdEncoding.EncodeToString(checksum)
}
}
op.State = OpDone
if err != nil {
return errors.Wrapf(err, "copy failed. dest: %q, src: %q, pkgPath %q", op.Dest, op.Src, root)
}
return nil
}

View File

@@ -0,0 +1,235 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package context
import (
"io"
"path/filepath"
"github.com/kardianos/govendor/internal/pathos"
os "github.com/kardianos/govendor/internal/vos"
)
// Import path is in GOROOT or is a special package.
func (ctx *Context) isStdLib(importPath string) (yes bool, err error) {
if importPath == "builtin" || importPath == "unsafe" || importPath == "C" {
yes = true
return
}
dir := filepath.Join(ctx.Goroot, importPath)
fi, _ := os.Stat(dir)
if fi == nil {
return
}
if fi.IsDir() == false {
return
}
yes, err = hasGoFileInFolder(dir)
return
}
// findImportDir finds the absolute directory. If rel is empty vendor folders
// are not looked in.
func (ctx *Context) findImportDir(relative, importPath string) (dir, gopath string, err error) {
if importPath == "builtin" || importPath == "unsafe" || importPath == "C" {
return filepath.Join(ctx.Goroot, importPath), ctx.Goroot, nil
}
if len(relative) != 0 {
rel := relative
for {
look := filepath.Join(rel, ctx.VendorDiscoverFolder, importPath)
nextRel := filepath.Join(rel, "..")
if rel == nextRel {
break
}
rel = nextRel
fi, err := os.Stat(look)
if os.IsNotExist(err) {
continue
}
if err != nil {
continue
}
if fi.IsDir() == false {
continue
}
for _, gopath = range ctx.GopathList {
if pathos.FileHasPrefix(look, gopath) {
hasGo, err := hasGoFileInFolder(look)
if err != nil {
return "", "", err
}
if hasGo {
return look, gopath, nil
}
}
}
}
}
for _, gopath = range ctx.GopathList {
dir := filepath.Join(gopath, importPath)
fi, err := os.Stat(dir)
if os.IsNotExist(err) {
continue
}
if fi == nil {
continue
}
if fi.IsDir() == false {
continue
}
return dir, gopath, nil
}
return "", "", ErrNotInGOPATH{importPath}
}
// findImportPath takes a absolute directory and returns the import path and go path.
func (ctx *Context) findImportPath(dir string) (importPath, gopath string, err error) {
dirResolved, err := filepath.EvalSymlinks(dir)
if err != nil {
return "", "", err
}
dirs := make([]string, 1)
dirs = append(dirs, dir)
if dir != dirResolved {
dirs = append(dirs, dirResolved)
}
for _, gopath := range ctx.GopathList {
for _, dir := range dirs {
if pathos.FileHasPrefix(dir, gopath) {
importPath = pathos.FileTrimPrefix(dir, gopath)
importPath = pathos.SlashToImportPath(importPath)
return importPath, gopath, nil
}
}
}
return "", "", ErrNotInGOPATH{dir}
}
func findRoot(folder, vendorPath string) (root string, err error) {
for i := 0; i <= looplimit; i++ {
test := filepath.Join(folder, vendorPath)
_, err := os.Stat(test)
if os.IsNotExist(err) == false {
return folder, nil
}
nextFolder := filepath.Clean(filepath.Join(folder, ".."))
// Check for root folder.
if nextFolder == folder {
return "", ErrMissingVendorFile{vendorPath}
}
folder = nextFolder
}
panic("findRoot loop limit")
}
func hasGoFileInFolder(folder string) (bool, error) {
dir, err := os.Open(folder)
if err != nil {
if os.IsNotExist(err) {
// No folder present, no need to check for files.
return false, nil
}
return false, err
}
fl, err := dir.Readdir(-1)
dir.Close()
if err != nil {
return false, err
}
for _, fi := range fl {
if fi.IsDir() == false && filepath.Ext(fi.Name()) == ".go" {
return true, nil
}
}
return false, nil
}
// RemovePackage removes the specified folder files. If folder is empty when
// done (no nested folders, remove the folder and any empty parent folders.
func RemovePackage(path, root string, tree bool) error {
// Ensure the path is empty of files.
dir, err := os.Open(path)
if err != nil {
if os.IsNotExist(err) {
return nil
}
return err
}
// Remove package files.
fl, err := dir.Readdir(-1)
dir.Close()
if err != nil {
return err
}
for _, fi := range fl {
fullPath := filepath.Join(path, fi.Name())
if fi.IsDir() {
if tree {
// If tree == true then remove sub-directories too.
err = os.RemoveAll(fullPath)
if err != nil {
return err
}
}
continue
}
err = os.Remove(fullPath)
if err != nil {
return err
}
}
// Remove empty parent folders.
// Ignore errors here.
for i := 0; i <= looplimit; i++ {
if pathos.FileStringEquals(path, root) {
return nil
}
dir, err := os.Open(path)
if err != nil {
// fmt.Fprintf(os.Stderr, "Failedd to open directory %q: %v\n", path, err)
return nil
}
fl, err := dir.Readdir(1)
dir.Close()
if err != nil && err != io.EOF {
// fmt.Fprintf(os.Stderr, "Failedd to list directory %q: %v\n", path, err)
return nil
}
if len(fl) > 0 {
allAreLicense := true
for _, fi := range fl {
if isLicenseFile(fi.Name()) == false {
allAreLicense = false
break
}
}
if !allAreLicense {
return nil
}
}
err = os.RemoveAll(path)
if err != nil {
// fmt.Fprintf(os.Stderr, "Failedd to remove empty directory %q: %v\n", path, err)
return nil
}
nextPath := filepath.Clean(filepath.Join(path, ".."))
// Check for root.
if nextPath == path {
return nil
}
path = nextPath
}
panic("removePackage() remove parent folders")
}

View File

@@ -0,0 +1,550 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package context
import (
"go/ast"
"go/parser"
"go/token"
"path"
"sort"
"strconv"
"strings"
"github.com/kardianos/govendor/internal/pathos"
filepath "github.com/kardianos/govendor/internal/vfilepath"
os "github.com/kardianos/govendor/internal/vos"
"github.com/kardianos/govendor/pkgspec"
)
var knownOS = make(map[string]bool)
var knownArch = make(map[string]bool)
func init() {
for _, v := range strings.Fields(goosList) {
knownOS[v] = true
}
for _, v := range strings.Fields(goarchList) {
knownArch[v] = true
}
}
// loadPackage sets up the context with package information and
// is called before any initial operation is performed.
func (ctx *Context) loadPackage() error {
ctx.loaded = true
ctx.dirty = false
ctx.statusCache = nil
ctx.Package = make(map[string]*Package, len(ctx.Package))
// We following the root symlink only in case the root of the repo is symlinked into the GOPATH
// This could happen during on some CI that didn't checkout into the GOPATH
rootdir, err := filepath.EvalSymlinks(ctx.RootDir)
if err != nil {
return err
}
err = filepath.Walk(rootdir, func(path string, info os.FileInfo, err error) error {
if info == nil {
return err
}
if !info.IsDir() {
// We replace the directory path (followed by the symlink), to the real go repo package name/path
// ex : replace "<somewhere>/govendor.source.repo" to "github.com/kardianos/govendor"
path = strings.Replace(path, rootdir, ctx.RootDir, 1)
_, err = ctx.addFileImports(path, ctx.RootGopath)
return err
}
name := info.Name()
// Still go into "_workspace" to aid godep migration.
if name == "_workspace" {
return nil
}
switch name[0] {
case '.', '_':
return filepath.SkipDir
}
switch name {
case "testdata", "node_modules":
return filepath.SkipDir
}
return nil
})
if err != nil {
return err
}
// Finally, set any unset status.
return ctx.determinePackageStatus()
}
func (ctx *Context) getFileTags(pathname string, f *ast.File) (tags *TagSet, imports []string, err error) {
_, filenameExt := filepath.Split(pathname)
if strings.HasSuffix(pathname, ".go") == false {
return nil, nil, nil
}
if f == nil {
f, err = parser.ParseFile(token.NewFileSet(), pathname, nil, parser.ImportsOnly|parser.ParseComments)
if f == nil {
return nil, nil, nil
}
}
tags = &TagSet{}
if strings.HasSuffix(f.Name.Name, "_test") {
tags.AddFileTag("test")
}
pkgNameNormalized := strings.TrimSuffix(f.Name.Name, "_test")
// Files with package name "documentation" should be ignored, per go build tool.
if pkgNameNormalized == "documentation" {
return nil, nil, nil
}
filename := filenameExt[:len(filenameExt)-3]
l := strings.Split(filename, "_")
if n := len(l); n > 1 && l[n-1] == "test" {
l = l[:n-1]
tags.AddFileTag("test")
}
n := len(l)
if n >= 2 && knownOS[l[n-2]] && knownArch[l[n-1]] {
tags.AddFileTag(l[n-2])
tags.AddFileTag(l[n-1])
}
if n >= 1 && knownOS[l[n-1]] {
tags.AddFileTag(l[n-1])
}
if n >= 1 && knownArch[l[n-1]] {
tags.AddFileTag(l[n-1])
}
const buildPrefix = "// +build "
for _, cc := range f.Comments {
for _, c := range cc.List {
if strings.HasPrefix(c.Text, buildPrefix) {
text := strings.TrimPrefix(c.Text, buildPrefix)
tags.AddBuildTags(text)
}
}
}
imports = make([]string, 0, len(f.Imports))
for i := range f.Imports {
imp := f.Imports[i].Path.Value
imp, err = strconv.Unquote(imp)
if err != nil {
// Best errort
continue
}
imports = append(imports, imp)
}
return tags, imports, nil
}
// addFileImports is called from loadPackage and resolveUnknown.
func (ctx *Context) addFileImports(pathname, gopath string) (*Package, error) {
dir, filenameExt := filepath.Split(pathname)
importPath := pathos.FileTrimPrefix(dir, gopath)
importPath = pathos.SlashToImportPath(importPath)
importPath = strings.Trim(importPath, "/")
if strings.HasSuffix(pathname, ".go") == false {
return nil, nil
}
// No need to add the same file more then once.
for _, pkg := range ctx.Package {
if pathos.FileStringEquals(pkg.Dir, dir) == false {
continue
}
for _, f := range pkg.Files {
if pathos.FileStringEquals(f.Path, pathname) {
return nil, nil
}
}
for _, f := range pkg.ignoreFile {
if pathos.FileStringEquals(f, filenameExt) {
return nil, nil
}
}
}
// Ignore error here and continue on best effort.
f, _ := parser.ParseFile(token.NewFileSet(), pathname, nil, parser.ImportsOnly|parser.ParseComments)
if f == nil {
return nil, nil
}
pkgNameNormalized := strings.TrimSuffix(f.Name.Name, "_test")
// Files with package name "documentation" should be ignored, per go build tool.
if pkgNameNormalized == "documentation" {
return nil, nil
}
tags, _, err := ctx.getFileTags(pathname, f)
if err != nil {
return nil, err
}
// If file has "// +build ignore", can mix package main with normal package.
// For now, just ignore ignored packages.
if tags.IgnoreItem() {
return nil, nil
}
pkg, found := ctx.Package[importPath]
if !found {
status := Status{
Type: TypePackage,
Location: LocationUnknown,
Presence: PresenceFound,
}
if pkgNameNormalized == "main" {
status.Type = TypeProgram
}
pkg = ctx.setPackage(dir, importPath, importPath, gopath, status)
ctx.Package[importPath] = pkg
}
if pkg.Status.Location != LocationLocal {
if tags.IgnoreItem(ctx.ignoreTag...) {
pkg.ignoreFile = append(pkg.ignoreFile, filenameExt)
return pkg, nil
}
// package excluded if non-local && same name or sub-package of an excluded package
for _, exclude := range ctx.excludePackage {
if importPath == exclude || strings.HasPrefix(importPath, exclude+"/") {
pkg.Status.Presence = PresenceExcluded
}
}
}
pf := &File{
Package: pkg,
Path: pathname,
Imports: make([]string, len(f.Imports)),
}
pkg.Files = append(pkg.Files, pf)
for i := range f.Imports {
imp := f.Imports[i].Path.Value
imp, err = strconv.Unquote(imp)
if err != nil {
// Best effort only.
continue
}
if strings.HasPrefix(imp, "./") {
imp = path.Join(importPath, imp)
}
pf.Imports[i] = imp
if pkg.Status.Presence != PresenceExcluded { // do not add package imports if it was explicitly excluded
_, err = ctx.addSingleImport(pkg.Dir, imp, pkg.IncludeTree)
if err != nil {
return pkg, err
}
}
}
// Record any import comment for file.
var ic *ast.Comment
if f.Name != nil {
pos := f.Name.Pos()
big:
// Find the next comment after the package name.
for _, cblock := range f.Comments {
for _, c := range cblock.List {
if c.Pos() > pos {
ic = c
break big
}
}
}
}
if ic != nil {
// If it starts with the import text, assume it is the import comment.
if index := strings.Index(ic.Text, " import "); index > 0 && index < 5 {
q := strings.TrimSpace(ic.Text[index+len(" import "):])
pf.ImportComment, err = strconv.Unquote(q)
if err != nil {
pf.ImportComment = q
}
}
}
return pkg, nil
}
func (ctx *Context) setPackage(dir, canonical, local, gopath string, status Status) *Package {
if pkg, exists := ctx.Package[local]; exists {
return pkg
}
at := 0
vMiddle := "/" + pathos.SlashToImportPath(ctx.VendorDiscoverFolder) + "/"
vStart := pathos.SlashToImportPath(ctx.VendorDiscoverFolder) + "/"
switch {
case strings.Contains(canonical, vMiddle):
at = strings.LastIndex(canonical, vMiddle) + len(vMiddle)
case strings.HasPrefix(canonical, vStart):
at = strings.LastIndex(canonical, vStart) + len(vStart)
}
originDir := dir
inVendor := false
tree := false
origin := ""
if at > 0 {
canonical = canonical[at:]
inVendor = true
if status.Location == LocationUnknown {
p := path.Join(ctx.RootImportPath, ctx.VendorDiscoverFolder)
if strings.HasPrefix(local, p) {
status.Location = LocationVendor
od, _, err := ctx.findImportDir("", canonical)
if err == nil {
originDir = od
}
}
}
}
if vp := ctx.VendorFilePackagePath(canonical); vp != nil {
tree = vp.Tree
origin = vp.Origin
}
// Set originDir correctly if origin is set.
if len(origin) > 0 {
od, _, err := ctx.findImportDir("", origin)
if err == nil {
originDir = od
}
}
if status.Location == LocationUnknown && filepath.HasPrefixDir(canonical, ctx.RootImportPath) {
status.Location = LocationLocal
}
spec, err := pkgspec.Parse("", canonical)
if err != nil {
panic(err)
}
if len(origin) > 0 && origin != canonical {
spec.Origin = origin
}
spec.IncludeTree = tree
pkg := &Package{
OriginDir: originDir,
Dir: dir,
Pkg: spec,
Local: local,
Gopath: gopath,
Status: status,
inVendor: inVendor,
}
ctx.Package[local] = pkg
return pkg
}
var testNeedsSortOrder = false
func (ctx *Context) addSingleImport(pkgInDir, imp string, tree bool) (*Package, error) {
// Do not check for existing package right away. If a external package
// has been added and we are looking in a vendor package, this won't work.
// We need to search any relative vendor folders first.
// Also need to check for vendor paths that won't use the local path in import path.
for _, pkg := range ctx.Package {
if pkg.Path == imp && pkg.inVendor && pathos.FileHasPrefix(pkg.Dir, pkgInDir) {
return nil, nil
}
}
dir, gopath, err := ctx.findImportDir(pkgInDir, imp)
if err != nil {
if _, is := err.(ErrNotInGOPATH); is {
presence := PresenceMissing
// excluded packages, don't need to be present
for _, exclude := range ctx.excludePackage {
if imp == exclude || strings.HasPrefix(imp, exclude+"/") {
presence = PresenceExcluded
}
}
return ctx.setPackage("", imp, imp, "", Status{
Type: TypePackage,
Location: LocationNotFound,
Presence: presence,
}), nil
}
return nil, err
}
if pathos.FileStringEquals(gopath, ctx.Goroot) {
return ctx.setPackage(dir, imp, imp, ctx.Goroot, Status{
Type: TypePackage,
Location: LocationStandard,
Presence: PresenceFound,
}), nil
}
if tree {
return ctx.setPackage(dir, imp, imp, ctx.RootGopath, Status{
Type: TypePackage,
Location: LocationVendor,
Presence: PresenceFound,
}), nil
}
df, err := os.Open(dir)
if err != nil {
return nil, err
}
info, err := df.Readdir(-1)
df.Close()
if err != nil {
return nil, err
}
if testNeedsSortOrder {
sort.Sort(fileInfoSort(info))
}
var pkg *Package
for _, fi := range info {
if fi.IsDir() {
continue
}
switch fi.Name()[0] {
case '.', '_':
continue
}
if pathos.FileStringEquals(dir, pkgInDir) {
continue
}
path := filepath.Join(dir, fi.Name())
tryPkg, err := ctx.addFileImports(path, gopath)
if tryPkg != nil {
pkg = tryPkg
}
if err != nil {
return pkg, err
}
}
return pkg, nil
}
func (ctx *Context) determinePackageStatus() error {
// Add any packages in the vendor file but not in GOPATH or vendor dir.
for _, vp := range ctx.VendorFile.Package {
if vp.Remove {
continue
}
if _, found := ctx.Package[vp.Path]; found {
continue
}
pkg, err := ctx.addSingleImport(ctx.RootDir, vp.Path, vp.Tree)
if err != nil {
return err
}
if pkg != nil {
pkg.Origin = vp.Origin
pkg.inTree = vp.Tree
pkg.inVendor = true
}
}
// Determine the status of remaining imports.
for _, pkg := range ctx.Package {
if pkg.Status.Location != LocationUnknown {
continue
}
if filepath.HasPrefixDir(pkg.Path, ctx.RootImportPath) {
pkg.Status.Location = LocationLocal
continue
}
pkg.Status.Location = LocationExternal
}
ctx.updatePackageReferences()
// Mark sub-tree packages as "tree", but leave any existing bit (unused) on the
// parent most tree package.
for path, pkg := range ctx.Package {
if vp := ctx.VendorFilePackagePath(pkg.Path); vp != nil && vp.Tree {
// Remove internal tree references.
del := make([]string, 0, 6)
for opath, opkg := range pkg.referenced {
if strings.HasPrefix(opkg.Path, pkg.Path+"/") {
del = append(del, opath)
}
}
delete(pkg.referenced, pkg.Local) // remove any self reference
for _, d := range del {
delete(pkg.referenced, d)
}
continue
}
if parentTrees := ctx.findPackageParentTree(pkg); len(parentTrees) > 0 {
pkg.Status.Presence = PresenceTree
// Transfer all references from the child to the top parent.
if parentPkg := ctx.Package[parentTrees[0]]; parentPkg != nil {
for opath, opkg := range pkg.referenced {
// Do not transfer internal references.
if strings.HasPrefix(opkg.Path, parentPkg.Path+"/") {
continue
}
parentPkg.referenced[opath] = opkg
}
pkg.referenced = make(map[string]*Package, 0)
for _, opkg := range ctx.Package {
if _, has := opkg.referenced[path]; has {
opkg.referenced[parentPkg.Local] = parentPkg
delete(opkg.referenced, path)
}
}
}
}
}
ctx.updatePackageReferences()
// Determine any un-used internal vendor imports.
for i := 0; i <= looplimit; i++ {
altered := false
for path, pkg := range ctx.Package {
if pkg.Status.Presence == PresenceUnused || pkg.Status.Presence == PresenceTree || pkg.Status.Type == TypeProgram {
continue
}
if len(pkg.referenced) > 0 || pkg.Status.Location != LocationVendor {
continue
}
altered = true
pkg.Status.Presence = PresenceUnused
for _, other := range ctx.Package {
delete(other.referenced, path)
}
}
if !altered {
break
}
if i == looplimit {
panic("determinePackageStatus loop limit")
}
}
ctx.updatePackageReferences()
// Unused external references may have worked their way in through
// vendor file. Remove any external leafs.
for i := 0; i <= looplimit; i++ {
altered := false
for path, pkg := range ctx.Package {
if len(pkg.referenced) > 0 || pkg.Status.Location != LocationExternal {
continue
}
altered = true
delete(ctx.Package, path)
pkg.Status.Presence = PresenceUnused
for _, other := range ctx.Package {
delete(other.referenced, path)
}
continue
}
if !altered {
break
}
if i == looplimit {
panic("determinePackageStatus loop limit")
}
}
return nil
}

View File

@@ -0,0 +1,207 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package context
import (
"go/ast"
"go/parser"
"go/printer"
"go/token"
"strconv"
"strings"
"github.com/dchest/safefile"
"github.com/kardianos/govendor/internal/pathos"
os "github.com/kardianos/govendor/internal/vos"
)
// Rewrite rewrites files to the local path.
func (ctx *Context) rewrite() error {
if !ctx.rewriteImports {
return nil
}
if ctx.dirty {
ctx.loadPackage()
}
ctx.dirty = true
fileImports := make(map[string]map[string]*File) // map[ImportPath]map[FilePath]File
for _, pkg := range ctx.Package {
for _, f := range pkg.Files {
for _, imp := range f.Imports {
fileList := fileImports[imp]
if fileList == nil {
fileList = make(map[string]*File, 1)
fileImports[imp] = fileList
}
fileList[f.Path] = f
}
}
}
filePaths := make(map[string]*File, len(ctx.RewriteRule))
for from, to := range ctx.RewriteRule {
// Add files that contain an import path to rewrite.
for _, f := range fileImports[from] {
filePaths[f.Path] = f
}
// Add files that contain import comments to remove.
if pkg := ctx.Package[from]; pkg != nil {
for _, f := range pkg.Files {
if len(f.ImportComment) != 0 {
filePaths[f.Path] = f
}
}
}
if pkg := ctx.Package[to]; pkg != nil {
for _, f := range pkg.Files {
if len(f.ImportComment) != 0 {
filePaths[f.Path] = f
}
}
}
}
/*
RULE: co2/internal/co3/pk3 -> co1/internal/co3/pk3
i co1/internal/co2/pk2 [co2/pk2] < ["co1/pk1"]
i co1/internal/co3/pk3 [co3/pk3] < ["co1/pk1"]
e co2/internal/co3/pk3 [co3/pk3] < ["co1/internal/co2/pk2"]
l co1/pk1 < []
s strings < ["co1/internal/co3/pk3" "co2/internal/co3/pk3"]
Rewrite the package "co1/internal/co2/pk2" because it references a package with a rewrite.from package.
*/
ctx.updatePackageReferences()
for from := range ctx.RewriteRule {
pkg := ctx.Package[from]
if pkg == nil {
continue
}
for _, ref := range pkg.referenced {
for _, f := range ref.Files {
dprintf("REF RW %s\n", f.Path)
filePaths[f.Path] = f
}
}
}
defer func() {
ctx.RewriteRule = make(map[string]string, 3)
}()
if len(ctx.RewriteRule) == 0 {
return nil
}
goprint := &printer.Config{
Mode: printer.TabIndent | printer.UseSpaces,
Tabwidth: 8,
}
for _, fileInfo := range filePaths {
if pathos.FileHasPrefix(fileInfo.Path, ctx.RootDir) == false {
continue
}
// Read the file into AST, modify the AST.
fileset := token.NewFileSet()
f, err := parser.ParseFile(fileset, fileInfo.Path, nil, parser.ParseComments)
if f == nil {
return nil
}
pkgNameNormalized := strings.TrimSuffix(f.Name.Name, "_test")
// Files with package name "documentation" should be ignored, per go build tool.
if pkgNameNormalized == "documentation" {
return nil
}
dprintf("RW:: File: %s\n", fileInfo.Path)
for _, impNode := range f.Imports {
imp, err := strconv.Unquote(impNode.Path.Value)
if err != nil {
return err
}
for from, to := range ctx.RewriteRule {
if imp != from {
continue
}
impNode.Path.Value = strconv.Quote(to)
for i, metaImport := range fileInfo.Imports {
if from == metaImport {
dprintf("\tImport: %s -> %s\n", from, to)
fileInfo.Imports[i] = to
}
}
break
}
}
// Remove import comment.
st := fileInfo.Package.Status
if st.Location == LocationVendor || st.Location == LocationExternal {
var ic *ast.Comment
if f.Name != nil {
pos := f.Name.Pos()
big:
// Find the next comment after the package name.
for _, cblock := range f.Comments {
for _, c := range cblock.List {
if c.Pos() > pos {
ic = c
break big
}
}
}
}
if ic != nil {
// If it starts with the import text, assume it is the import comment and remove.
if index := strings.Index(ic.Text, " import "); index > 0 && index < 5 {
ic.Text = strings.Repeat(" ", len(ic.Text))
}
}
}
// Don't sort or modify the imports to minimize diffs.
// Write the AST back to disk.
fi, err := os.Stat(fileInfo.Path)
if err != nil {
return err
}
w, err := safefile.Create(fileInfo.Path, fi.Mode())
if err != nil {
return err
}
err = goprint.Fprint(w, fileset, f)
if err != nil {
w.Close()
return err
}
err = w.Commit()
if err != nil {
return err
}
}
return nil
}
func (ctx *Context) makeSet(pkg *Package, mvSet map[*Package]struct{}) {
mvSet[pkg] = struct{}{}
for _, f := range pkg.Files {
for _, imp := range f.Imports {
next := ctx.Package[imp]
switch {
default:
if _, has := mvSet[next]; !has {
ctx.makeSet(next, mvSet)
}
case next == nil:
case next.Path == next.Local:
case next.Status.Location != LocationExternal:
}
}
}
}

View File

@@ -0,0 +1,271 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package context
import (
"bytes"
"fmt"
"sort"
"github.com/kardianos/govendor/pkgspec"
)
type (
// Status is the package type, location, and presence indicators.
Status struct {
Type StatusType // program, package
Location StatusLocation // vendor, local, external, stdlib
Presence StatusPresence // missing, unused, tree, excluded
Not bool // Not indicates boolean operation "not" on above.
}
StatusType byte // StatusType is main or not-main.
StatusLocation byte // StatusLocation is where the package is.
StatusPresence byte // StatusPresence is if it can be found or referenced.
// StatusGroup is the logical filter for status with "and", "not", and grouping.
StatusGroup struct {
Status []Status
Group []StatusGroup
And bool
Not bool
}
)
func (s Status) String() string {
t := ' '
l := ' '
p := ' '
not := ""
if s.Not {
not = "!"
}
switch s.Type {
default:
panic("Unknown Type type")
case TypeUnknown:
t = '_'
case TypePackage:
t = ' '
case TypeProgram:
t = 'p'
}
switch s.Location {
default:
panic("Unkown Location type")
case LocationUnknown:
l = '_'
case LocationNotFound:
l = ' '
case LocationLocal:
l = 'l'
case LocationExternal:
l = 'e'
case LocationVendor:
l = 'v'
case LocationStandard:
l = 's'
}
switch s.Presence {
default:
panic("Unknown Presence type")
case PresenceUnknown:
p = '_'
case PresenceFound:
p = ' '
case PresenceMissing:
p = 'm'
case PresenceUnused:
p = 'u'
case PresenceTree:
p = 't'
case PresenceExcluded:
p = 'x'
}
return not + string(t) + string(l) + string(p)
}
func (sg StatusGroup) String() string {
buf := &bytes.Buffer{}
if sg.And {
buf.WriteString("and")
} else {
buf.WriteString("or")
}
buf.WriteRune('(')
for i, s := range sg.Status {
if i != 0 {
buf.WriteRune(',')
}
buf.WriteString(s.String())
}
if len(sg.Status) > 0 && len(sg.Group) > 0 {
buf.WriteRune(',')
}
for i, ssg := range sg.Group {
if i != 0 {
buf.WriteRune(',')
}
buf.WriteString(ssg.String())
}
buf.WriteRune(')')
return buf.String()
}
func (pkgSt Status) Match(filterSt Status) bool {
// not: true, pkg: A, filter: B
// true == (A == B) -> true == false -> false
//
// not: false, pkg: A, filter: B
// false == (A == B) -> false == false -> true
//
// not: true, pkg: A, filter: A
// true == (A == A) -> true == true) -> true
//
// not: false, pkg: A, filter: A
// false == (A == A) -> false == true -> false
if filterSt.Location != LocationUnknown && filterSt.Not == (pkgSt.Location == filterSt.Location) {
return false
}
if filterSt.Type != TypeUnknown && filterSt.Not == (pkgSt.Type == filterSt.Type) {
return false
}
if filterSt.Presence != PresenceUnknown && filterSt.Not == (pkgSt.Presence == filterSt.Presence) {
return false
}
return true
}
func (status Status) MatchGroup(filter StatusGroup) bool {
or := !filter.And
for _, fs := range filter.Status {
if status.Match(fs) == or {
return or != filter.Not
}
}
for _, fg := range filter.Group {
if status.MatchGroup(fg) == or {
return or != filter.Not
}
}
return filter.And
}
const (
TypeUnknown StatusType = iota // TypeUnknown is unset StatusType.
TypePackage // TypePackage package is a non-main package.
TypeProgram // TypeProgram package is a main package.
)
const (
LocationUnknown StatusLocation = iota // LocationUnknown is unset StatusLocation.
LocationNotFound // LocationNotFound package is not to be found (use PresenceMissing).
LocationStandard // LocationStandard package is in the standard library.
LocationLocal // LocationLocal package is in a project, not in a vendor folder.
LocationExternal // LocationExternal package is not in a project, in GOPATH.
LocationVendor // LocationVendor package is in a vendor folder.
)
const (
PresenceUnknown StatusPresence = iota // PresenceUnknown is unset StatusPresence.
PresenceFound // PresenceFound package exists.
PresenceMissing // PresenceMissing package is referenced but not found.
PresenceUnused // PresenceUnused package is found locally but not referenced.
PresenceTree // PresenceTree package is in vendor folder, in a tree, but not referenced.
PresenceExcluded // PresenceExcluded package exists, but should not be vendored.
)
// ListItem represents a package in the current project.
type StatusItem struct {
Status Status
Pkg *pkgspec.Pkg
VersionExact string
Local string
ImportedBy []*Package
}
func (li StatusItem) String() string {
if li.Local == li.Pkg.Path {
return fmt.Sprintf("%s %s < %q", li.Status, li.Pkg.Path, li.ImportedBy)
}
return fmt.Sprintf("%s %s [%s] < %q", li.Status, li.Local, li.Pkg.Path, li.ImportedBy)
}
type statusItemSort []StatusItem
func (li statusItemSort) Len() int { return len(li) }
func (li statusItemSort) Swap(i, j int) { li[i], li[j] = li[j], li[i] }
func (li statusItemSort) Less(i, j int) bool {
if li[i].Status.Location != li[j].Status.Location {
return li[i].Status.Location > li[j].Status.Location
}
return li[i].Local < li[j].Local
}
// Status obtains the current package status list.
func (ctx *Context) updateStatusCache() error {
var err error
if !ctx.loaded || ctx.dirty {
err = ctx.loadPackage()
if err != nil {
return err
}
}
ctx.updatePackageReferences()
list := make([]StatusItem, 0, len(ctx.Package))
for _, pkg := range ctx.Package {
version := ""
versionExact := ""
if vp := ctx.VendorFilePackagePath(pkg.Path); vp != nil {
version = vp.Version
versionExact = vp.VersionExact
}
origin := ""
if pkg.Origin != pkg.Path {
origin = pkg.Origin
}
if len(pkg.Origin) == 0 && pkg.Path != pkg.Local {
origin = pkg.Local
}
li := StatusItem{
Status: pkg.Status,
Pkg: &pkgspec.Pkg{Path: pkg.Path, IncludeTree: pkg.IncludeTree, Origin: origin, Version: version, FilePath: pkg.Dir},
Local: pkg.Local,
VersionExact: versionExact,
ImportedBy: make([]*Package, 0, len(pkg.referenced)),
}
for _, ref := range pkg.referenced {
li.ImportedBy = append(li.ImportedBy, ref)
}
sort.Sort(packageList(li.ImportedBy))
list = append(list, li)
}
// Sort li by Status, then Path.
sort.Sort(statusItemSort(list))
ctx.statusCache = list
return nil
}
// Status obtains the current package status list.
func (ctx *Context) Status() ([]StatusItem, error) {
var err error
if !ctx.loaded || ctx.dirty {
err = ctx.loadPackage()
if err != nil {
return nil, err
}
}
if ctx.statusCache == nil {
err = ctx.updateStatusCache()
if err != nil {
return nil, err
}
}
return ctx.statusCache, nil
}

View File

@@ -0,0 +1,415 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package context
import (
"bytes"
"crypto/sha1"
"encoding/base64"
"fmt"
"hash"
"io"
"net/url"
"os"
"os/exec"
"path/filepath"
"sort"
"strings"
"github.com/kardianos/govendor/internal/pathos"
"github.com/kardianos/govendor/vendorfile"
"golang.org/x/tools/go/vcs"
)
func skipperTree(name string, dir bool) bool {
return false
}
func skipperPackage(name string, dir bool) bool {
return dir
}
func (ctx *Context) VerifyVendor() (outOfDate []*vendorfile.Package, err error) {
vf := ctx.VendorFile
root := filepath.Join(ctx.RootDir, ctx.VendorFolder)
add := func(vp *vendorfile.Package) {
outOfDate = append(outOfDate, vp)
}
for _, vp := range vf.Package {
if vp.Remove {
continue
}
if len(vp.Path) == 0 {
continue
}
if len(vp.ChecksumSHA1) == 0 {
add(vp)
continue
}
fp := filepath.Join(root, pathos.SlashToFilepath(vp.Path))
h := sha1.New()
sk := skipperPackage
if vp.Tree {
sk = skipperTree
}
err = getHash(root, fp, h, sk)
if err != nil {
return
}
checksum := base64.StdEncoding.EncodeToString(h.Sum(nil))
if vp.ChecksumSHA1 != checksum {
add(vp)
}
}
return
}
func getHash(root, fp string, h hash.Hash, skipper func(name string, isDir bool) bool) error {
rel := pathos.FileTrimPrefix(fp, root)
rel = pathos.SlashToImportPath(rel)
rel = strings.Trim(rel, "/")
h.Write([]byte(rel))
dir, err := os.Open(fp)
if err != nil {
if os.IsNotExist(err) {
return nil
}
return fmt.Errorf("Failed to open dir %q: %v", fp, err)
}
filelist, err := dir.Readdir(-1)
dir.Close()
if err != nil {
return fmt.Errorf("Failed to read dir %q: %v", fp, err)
}
sort.Sort(fileInfoSort(filelist))
for _, fi := range filelist {
if skipper(fi.Name(), fi.IsDir()) {
continue
}
p := filepath.Join(fp, fi.Name())
if fi.IsDir() {
err = getHash(root, p, h, skipper)
if err != nil {
return err
}
continue
}
f, err := os.Open(p)
if err != nil {
return err
}
h.Write([]byte(fi.Name()))
_, err = io.Copy(h, f)
f.Close()
if err != nil {
return err
}
}
return nil
}
// similarSegments compares two paths and determines if they have
// similar prefix segments. For example github.com/kardianos/rdb and
// github.com/kardianos/govendor have 2 similar segments.
func similarSegments(p1, p2 string) (string, int) {
seg1 := strings.Split(p1, "/")
seg2 := strings.Split(p2, "/")
ct := len(seg1)
if len(seg2) < ct {
ct = len(seg2)
}
similar := &bytes.Buffer{}
for i := 0; i < ct; i++ {
if seg1[i] != seg2[i] {
return similar.String(), i
}
if i != 0 {
similar.WriteRune('/')
}
similar.WriteString(seg1[i])
}
return similar.String(), ct
}
type remoteFailure struct {
Path string
Msg string
Err error
}
func (fail remoteFailure) Error() string {
return fmt.Sprintf("Failed for %q (%s): %v", fail.Path, fail.Msg, fail.Err)
}
type remoteFailureList []remoteFailure
func (list remoteFailureList) Error() string {
if len(list) == 0 {
return "(no remote failure)"
}
buf := &bytes.Buffer{}
buf.WriteString("Remotes failed for:\n")
for _, item := range list {
buf.WriteString("\t")
buf.WriteString(item.Error())
buf.WriteString("\n")
}
return buf.String()
}
type VCSCmd struct {
*vcs.Cmd
}
func (vcsCmd *VCSCmd) RevisionSync(dir, revision string) error {
return vcsCmd.run(dir, vcsCmd.TagSyncCmd, "tag", revision)
}
func (v *VCSCmd) run(dir string, cmd string, keyval ...string) error {
_, err := v.run1(dir, cmd, keyval, true)
return err
}
// run1 is the generalized implementation of run and runOutput.
func (vcsCmd *VCSCmd) run1(dir string, cmdline string, keyval []string, verbose bool) ([]byte, error) {
v := vcsCmd.Cmd
m := make(map[string]string)
for i := 0; i < len(keyval); i += 2 {
m[keyval[i]] = keyval[i+1]
}
args := strings.Fields(cmdline)
for i, arg := range args {
args[i] = expand(m, arg)
}
_, err := exec.LookPath(v.Cmd)
if err != nil {
fmt.Fprintf(os.Stderr,
"go: missing %s command. See http://golang.org/s/gogetcmd\n",
v.Name)
return nil, err
}
cmd := exec.Command(v.Cmd, args...)
cmd.Dir = dir
cmd.Env = envForDir(cmd.Dir)
if vcs.ShowCmd {
fmt.Printf("cd %s\n", dir)
fmt.Printf("%s %s\n", v.Cmd, strings.Join(args, " "))
}
var buf bytes.Buffer
cmd.Stdout = &buf
cmd.Stderr = &buf
err = cmd.Run()
out := buf.Bytes()
if err != nil {
if verbose {
fmt.Fprintf(os.Stderr, "# cd %s; %s %s\n", dir, v.Cmd, strings.Join(args, " "))
os.Stderr.Write(out)
}
return nil, err
}
return out, nil
}
// expand rewrites s to replace {k} with match[k] for each key k in match.
func expand(match map[string]string, s string) string {
for k, v := range match {
s = strings.Replace(s, "{"+k+"}", v, -1)
}
return s
}
// envForDir returns a copy of the environment
// suitable for running in the given directory.
// The environment is the current process's environment
// but with an updated $PWD, so that an os.Getwd in the
// child will be faster.
func envForDir(dir string) []string {
env := os.Environ()
// Internally we only use rooted paths, so dir is rooted.
// Even if dir is not rooted, no harm done.
return mergeEnvLists([]string{"PWD=" + dir}, env)
}
// mergeEnvLists merges the two environment lists such that
// variables with the same name in "in" replace those in "out".
func mergeEnvLists(in, out []string) []string {
NextVar:
for _, inkv := range in {
k := strings.SplitAfterN(inkv, "=", 2)[0]
for i, outkv := range out {
if strings.HasPrefix(outkv, k) {
out[i] = inkv
continue NextVar
}
}
out = append(out, inkv)
}
return out
}
func updateVcsCmd(cmd *vcs.Cmd) *VCSCmd {
switch cmd.Name {
case "Git":
cmd.TagSyncCmd = "reset --hard {tag}"
cmd.TagSyncDefault = "reset --hard origin/master"
cmd.DownloadCmd = "fetch"
case "Mercurial":
case "Bazaar":
case "Subversion":
}
return &VCSCmd{Cmd: cmd}
}
var isSecureScheme = map[string]bool{
"https": true,
"git+ssh": true,
"bzr+ssh": true,
"svn+ssh": true,
"ssh": true,
}
func vcsIsSecure(repo string) bool {
u, err := url.Parse(repo)
if err != nil {
// If repo is not a URL, it's not secure.
return false
}
return isSecureScheme[u.Scheme]
}
// Sync checks for outdated packages in the vendor folder and fetches the
// correct revision from the remote.
func (ctx *Context) Sync(dryrun bool) (err error) {
// vcs.ShowCmd = true
outOfDate, err := ctx.VerifyVendor()
if err != nil {
return fmt.Errorf("Failed to verify checksums: %v", err)
}
// GOPATH includes the src dir, move up a level.
cacheRoot := filepath.Join(ctx.RootGopath, "..", ".cache", "govendor")
err = os.MkdirAll(cacheRoot, 0700)
if err != nil {
return err
}
// collect errors and proceed where you can.
rem := remoteFailureList{}
h := sha1.New()
updatedVendorFile := false
for _, vp := range outOfDate {
// Bundle packages together that have the same revision and share at least one root segment.
if len(vp.Revision) == 0 {
continue
}
from := vp.Path
if len(vp.Origin) > 0 {
from = vp.Origin
}
if from != vp.Path {
fmt.Fprintf(ctx, "fetch %q from %q\n", vp.Path, from)
} else {
fmt.Fprintf(ctx, "fetch %q\n", vp.Path)
}
if dryrun {
continue
}
pkgDir := filepath.Join(cacheRoot, from)
// See if repo exists.
sysVcsCmd, repoRoot, err := vcs.FromDir(pkgDir, cacheRoot)
var vcsCmd *VCSCmd
repoRootDir := filepath.Join(cacheRoot, repoRoot)
if err != nil {
rr, err := vcs.RepoRootForImportPath(from, false)
if err != nil {
rem = append(rem, remoteFailure{Msg: "failed to ping remote repo", Path: vp.Path, Err: err})
continue
}
if !ctx.Insecure && !vcsIsSecure(rr.Repo) {
rem = append(rem, remoteFailure{Msg: "repo remote not secure", Path: vp.Path, Err: nil})
continue
}
vcsCmd = updateVcsCmd(rr.VCS)
repoRoot = rr.Root
repoRootDir = filepath.Join(cacheRoot, repoRoot)
err = os.MkdirAll(repoRootDir, 0700)
if err != nil {
rem = append(rem, remoteFailure{Msg: "failed to make repo root dir", Path: vp.Path, Err: err})
continue
}
err = vcsCmd.CreateAtRev(repoRootDir, rr.Repo, vp.Revision)
if err != nil {
rem = append(rem, remoteFailure{Msg: "failed to clone repo", Path: vp.Path, Err: err})
continue
}
} else {
// Use cache.
vcsCmd = updateVcsCmd(sysVcsCmd)
err = vcsCmd.RevisionSync(repoRootDir, vp.Revision)
// If revision was not found in the cache, download and try again.
if err != nil {
err = vcsCmd.Download(repoRootDir)
if err != nil {
rem = append(rem, remoteFailure{Msg: "failed to download repo", Path: vp.Path, Err: err})
continue
}
err = vcsCmd.RevisionSync(repoRootDir, vp.Revision)
if err != nil {
rem = append(rem, remoteFailure{Msg: "failed to sync repo to " + vp.Revision, Path: vp.Path, Err: err})
continue
}
}
}
dest := filepath.Join(ctx.RootDir, ctx.VendorFolder, pathos.SlashToFilepath(vp.Path))
// Path handling with single sub-packages and differing origins need to be properly handled.
src := pkgDir
// Scan go files for files that should be ignored based on tags and filenames.
ignoreFiles, _, err := ctx.getIngoreFiles(src)
if err != nil {
rem = append(rem, remoteFailure{Msg: "failed to get ignore files", Path: vp.Path, Err: err})
continue
}
root, _ := pathos.TrimCommonSuffix(src, vp.Path)
// Need to ensure we copy files from "b.Root/<import-path>" for the following command.
err = ctx.CopyPackage(dest, src, root, vp.Path, ignoreFiles, vp.Tree, h, nil)
if err != nil {
fmt.Fprintf(ctx, "failed to copy package from %q to %q: %+v", src, dest, err)
}
checksum := h.Sum(nil)
h.Reset()
vp.ChecksumSHA1 = base64.StdEncoding.EncodeToString(checksum)
updatedVendorFile = true
}
// Only write a vendor file if something changes.
if updatedVendorFile {
err = ctx.WriteVendorFile()
if err != nil {
return err
}
}
// Return network errors here.
if len(rem) > 0 {
return rem
}
return nil
}

View File

@@ -0,0 +1,8 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package context
const goosList = "android darwin dragonfly freebsd linux nacl netbsd openbsd plan9 solaris windows "
const goarchList = "386 amd64 amd64p32 arm armbe arm64 arm64be ppc64 ppc64le mips mipsle mips64 mips64le mips64p32 mips64p32le ppc s390 s390x sparc sparc64 "

View File

@@ -0,0 +1,213 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package context
import (
"bytes"
"strings"
)
// Build tags come in the format "tagA tagB,tagC" -> "taga OR (tagB AND tagC)"
// File tags compose with this as "ftag1 AND ftag2 AND (<build-tags>)".
// However in govendor all questions are reversed. Rather then asking
// "What should be built?" we ask "What should be ingored?".
type logical struct {
and bool
tag []logicalTag
sub []logical
}
type logicalTag struct {
not bool
tag string
}
func (lt logicalTag) match(lt2 logicalTag) bool {
// A logicalTag is a match if
// "tag == itag" or "!tag == !itag" or
// "tag != !itag" or "!tag != itag"
if lt.not || lt2.not {
return false
}
return lt.tag == lt2.tag
if lt.not == lt2.not {
return lt.tag == lt2.tag
}
return lt.tag != lt2.tag
}
func (lt logicalTag) String() string {
if lt.not {
return "!" + lt.tag
}
return lt.tag
}
func newLogicalTag(tag string) logicalTag {
lt := logicalTag{}
lt.not = strings.HasPrefix(tag, "!")
lt.tag = strings.TrimPrefix(tag, "!")
return lt
}
func (l logical) empty() bool {
if len(l.tag) > 0 {
return false
}
for _, sub := range l.sub {
if !sub.empty() {
return false
}
}
return true
}
func (l logical) ignored(ignoreTags []logicalTag) bool {
// A logical is ingored if ANY AND conditions match or ALL OR conditions match.
if len(ignoreTags) == 0 {
return false
}
if l.empty() {
return !l.and
}
if l.and {
// Must have all tags in ignoreTags to be ignored.
for _, t := range l.tag {
for _, it := range ignoreTags {
if t.match(it) {
return true
}
}
}
// Must ignore all sub-logicals to be ignored.
for _, sub := range l.sub {
if sub.ignored(ignoreTags) {
return true
}
}
return false
}
hasOne := false
// OR'ing the pieces together.
// Must have at least one tag in ignoreTags to be ignored.
for _, t := range l.tag {
hasOne = true
hasIgnoreTag := false
for _, it := range ignoreTags {
if t.match(it) {
hasIgnoreTag = true
break
}
}
if !hasIgnoreTag {
return false
}
}
// Must have at least one sub section be ignored to be ignored.
for _, sub := range l.sub {
hasOne = true
if !sub.ignored(ignoreTags) {
return false
}
}
return hasOne
}
func (l logical) String() string {
buf := bytes.Buffer{}
if l.and {
buf.WriteString(" AND (")
} else {
buf.WriteString(" OR (")
}
for index, tag := range l.tag {
if index != 0 {
buf.WriteString(" ")
}
buf.WriteString(tag.String())
}
for index, sub := range l.sub {
if index != 0 {
buf.WriteString(" ")
}
buf.WriteString(sub.String())
}
buf.WriteRune(')')
return buf.String()
}
type TagSet struct {
// ignore comes from a special build tag "ignore".
ignore bool
root logical
}
func (ts *TagSet) String() string {
if ts == nil {
return "(nil)"
}
if ts.ignore {
return "ignore"
}
return ts.root.String()
}
func (ts *TagSet) IgnoreItem(ignoreList ...string) bool {
if ts == nil {
return false
}
if ts.ignore {
return true
}
ts.root.and = true
ignoreTags := make([]logicalTag, len(ignoreList))
for i := 0; i < len(ignoreList); i++ {
ignoreTags[i] = newLogicalTag(ignoreList[i])
}
return ts.root.ignored(ignoreTags)
}
func (ts *TagSet) AddFileTag(tag string) {
if ts == nil {
return
}
ts.root.and = true
ts.root.tag = append(ts.root.tag, logicalTag{tag: tag})
}
func (ts *TagSet) AddBuildTags(tags string) {
if ts == nil {
return
}
ts.root.and = true
if len(ts.root.sub) == 0 {
ts.root.sub = append(ts.root.sub, logical{})
}
buildlogical := &ts.root.sub[0]
ss := strings.Fields(tags)
for _, s := range ss {
if s == "ignore" {
ts.ignore = true
continue
}
if !strings.ContainsRune(s, ',') {
buildlogical.tag = append(buildlogical.tag, newLogicalTag(s))
continue
}
sub := logical{and: true}
for _, and := range strings.Split(s, ",") {
sub.tag = append(sub.tag, newLogicalTag(and))
}
buildlogical.sub = append(buildlogical.sub, sub)
}
}

View File

@@ -0,0 +1,79 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package context
import (
"bytes"
ros "os"
"path/filepath"
"strings"
"github.com/dchest/safefile"
"github.com/kardianos/govendor/vendorfile"
os "github.com/kardianos/govendor/internal/vos"
)
// WriteVendorFile writes the current vendor file to the context location.
func (ctx *Context) WriteVendorFile() (err error) {
perm := ros.FileMode(0666)
fi, err := os.Stat(ctx.VendorFilePath)
if err == nil {
perm = fi.Mode()
}
ctx.VendorFile.RootPath = ctx.RootImportPath
buf := &bytes.Buffer{}
err = ctx.VendorFile.Marshal(buf)
if err != nil {
return
}
err = buf.WriteByte('\n')
if err != nil {
return
}
dir, _ := filepath.Split(ctx.VendorFilePath)
err = os.MkdirAll(dir, 0777)
if err != nil {
return
}
for i := range ctx.VendorFile.Package {
vp := ctx.VendorFile.Package[i]
vp.Add = false
}
err = safefile.WriteFile(ctx.VendorFilePath, buf.Bytes(), perm)
if err == nil {
for _, vp := range ctx.VendorFile.Package {
vp.Add = false
}
}
return
}
func readVendorFile(vendorRoot, vendorFilePath string) (*vendorfile.File, error) {
vf := &vendorfile.File{}
f, err := os.Open(vendorFilePath)
if err != nil {
return nil, err
}
defer f.Close()
err = vf.Unmarshal(f)
if err != nil {
return nil, err
}
// Remove any existing origin field if the prefix matches the
// context package root. This fixes a previous bug introduced in the file,
// that is now fixed.
for _, row := range vf.Package {
row.Origin = strings.TrimPrefix(row.Origin, vendorRoot)
}
return vf, nil
}

View File

@@ -0,0 +1,47 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package context
import (
"strconv"
"unicode"
)
// IsVersion returns true if the string is a version.
func isVersion(s string) bool {
hasPunct := false
onlyNumber := true
onlyHexLetter := true
for _, r := range s {
isNumber := unicode.IsNumber(r)
isLetter := unicode.IsLetter(r)
hasPunct = hasPunct || unicode.IsPunct(r)
onlyNumber = onlyNumber && isNumber
if isLetter {
low := unicode.ToLower(r)
onlyHexLetter = onlyHexLetter && low <= 'f'
}
}
if hasPunct {
return true
}
if onlyHexLetter == false {
return true
}
num, err := strconv.ParseInt(s, 10, 64)
if err == nil {
if num > 100 {
return false // numeric revision.
}
}
if len(s) > 5 && onlyHexLetter {
return false // hex revision
}
return true
}

View File

@@ -0,0 +1,8 @@
// Machine generated; DO NOT EDIT.
package help
var msgGovendorLicenses = `{{range $index, $t := .}}{{if ne $index 0}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
{{end}}{{.Filename}} - {{.Path}}
{{.Text}}{{end}}
`

View File

@@ -0,0 +1,395 @@
// Machine generated; DO NOT EDIT.
package help
var msgGovendorLicenses = `LICENSE - go
Copyright (c) 2012 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PATENTS - go
Additional IP Rights Grant (Patents)
"This implementation" means the copyrightable works distributed by
Google as part of the Go project.
Google hereby grants to You a perpetual, worldwide, non-exclusive,
no-charge, royalty-free, irrevocable (except as stated in this section)
patent license to make, have made, use, offer to sell, sell, import,
transfer and otherwise run, modify and propagate the contents of this
implementation of Go, where such license applies only to those patent
claims, both currently owned or controlled by Google and acquired in
the future, licensable by Google that are necessarily infringed by this
implementation of Go. This grant does not include claims that would be
infringed only as a consequence of further modification of this
implementation. If you or your agent or exclusive licensee institute or
order or agree to the institution of patent litigation against any
entity (including a cross-claim or counterclaim in a lawsuit) alleging
that this implementation of Go or any code incorporated within this
implementation of Go constitutes direct or contributory patent
infringement, or inducement of patent infringement, then any patent
rights granted to you under this License for this implementation of Go
shall terminate as of the date such litigation is filed.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
LICENSE - github.com/kardianos/govendor
Copyright (c) 2015 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
LICENSE - github.com/kardianos/govendor/vendor/github.com/Bowery/prompt
The MIT License (MIT)
Copyright (c) 2013-2015 Bowery, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
LICENSE - github.com/kardianos/govendor/vendor/github.com/dchest/safefile
Copyright (c) 2013 Dmitry Chestnykh <dmitry@codingrobots.com>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials
provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
COPYING - github.com/kardianos/govendor/vendor/github.com/google/shlex
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
LICENSE - github.com/kardianos/govendor/vendor/golang.org/x/tools
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PATENTS - github.com/kardianos/govendor/vendor/golang.org/x/tools
Additional IP Rights Grant (Patents)
"This implementation" means the copyrightable works distributed by
Google as part of the Go project.
Google hereby grants to You a perpetual, worldwide, non-exclusive,
no-charge, royalty-free, irrevocable (except as stated in this section)
patent license to make, have made, use, offer to sell, sell, import,
transfer and otherwise run, modify and propagate the contents of this
implementation of Go, where such license applies only to those patent
claims, both currently owned or controlled by Google and acquired in
the future, licensable by Google that are necessarily infringed by this
implementation of Go. This grant does not include claims that would be
infringed only as a consequence of further modification of this
implementation. If you or your agent or exclusive licensee institute or
order or agree to the institution of patent litigation against any
entity (including a cross-claim or counterclaim in a lawsuit) alleging
that this implementation of Go or any code incorporated within this
implementation of Go constitutes direct or contributory patent
infringement, or inducement of patent infringement, then any patent
rights granted to you under this License for this implementation of Go
shall terminate as of the date such litigation is filed.
`

62
tools/vendor/github.com/kardianos/govendor/help/msg.go generated vendored Normal file
View File

@@ -0,0 +1,62 @@
package help
type HelpMessage byte
const (
MsgNone HelpMessage = iota
MsgFull
MsgInit
MsgList
MsgAdd
MsgUpdate
MsgRemove
MsgFetch
MsgStatus
MsgSync
MsgMigrate
MsgGet
MsgLicense
MsgShell
MsgGovendorLicense
MsgGovendorVersion
)
func (msg HelpMessage) String() string {
msgText := ""
switch msg {
default:
panic("Unknown message type")
case MsgNone:
case MsgFull:
msgText = helpFull
case MsgInit:
msgText = helpInit
case MsgList:
msgText = helpList
case MsgAdd:
msgText = helpAdd
case MsgUpdate:
msgText = helpUpdate
case MsgRemove:
msgText = helpRemove
case MsgFetch:
msgText = helpFetch
case MsgStatus:
msgText = helpStatus
case MsgSync:
msgText = helpSync
case MsgMigrate:
msgText = helpMigrate
case MsgGet:
msgText = helpGet
case MsgLicense:
msgText = helpLicense
case MsgShell:
msgText = helpShell
case MsgGovendorLicense:
msgText = msgGovendorLicenses
case MsgGovendorVersion:
msgText = msgGovendorVersion
}
return msgText
}

169
tools/vendor/github.com/kardianos/govendor/help/text.go generated vendored Normal file
View File

@@ -0,0 +1,169 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package help
import (
"strings"
"github.com/kardianos/govendor/migrate"
)
//go:generate govendor license -o licenses.go -template gen-license.template
var helpFull = `govendor (` + version + `): record dependencies and copy into vendor folder
-govendor-licenses Show govendor's licenses.
-version Show govendor version
Sub-Commands
init Create the "vendor" folder and the "vendor.json" file.
list List and filter existing dependencies and packages.
add Add packages from $GOPATH.
update Update packages from $GOPATH.
remove Remove packages from the vendor folder.
status Lists any packages missing, out-of-date, or modified locally.
fetch Add new or update vendor folder packages from remote repository.
sync Pull packages into vendor folder from remote repository with revisions
from vendor.json file.
migrate Move packages from a legacy tool to the vendor folder with metadata.
get Like "go get" but copies dependencies into a "vendor" folder.
license List discovered licenses for the given status or import paths.
shell Run a "shell" to make multiple sub-commands more efficient for large
projects.
go tool commands that are wrapped:
"+status" package selection may be used with them
fmt, build, install, clean, test, vet, generate, tool
Status Types
+local (l) packages in your project
+external (e) referenced packages in GOPATH but not in current project
+vendor (v) packages in the vendor folder
+std (s) packages in the standard library
+excluded (x) external packages explicitly excluded from vendoring
+unused (u) packages in the vendor folder, but unused
+missing (m) referenced packages but not found
+program (p) package is a main package
+outside +external +missing
+all +all packages
Status can be referenced by their initial letters.
Package specifier
<path>[::<origin>][{/...|/^}][@[<version-spec>]]
Ignoring files with build tags, or excluding packages from being vendored:
The "vendor.json" file contains a string field named "ignore".
It may contain a space separated list of build tags to ignore when
listing and copying files.
This list may also contain package prefixes (containing a "/", possibly
as last character) to exclude when copying files in the vendor folder.
If "foo/" appears in this field, then package "foo" and all its sub-packages
("foo/bar", …) will be excluded (but package "bar/foo" will not).
By default the init command adds the "test" tag to the ignore list.
If using go1.5, ensure GO15VENDOREXPERIMENT=1 is set.
`
var helpInit = `govendor init
Create a vendor folder in the working directory and a vendor/vendor.json
metadata file.
`
var helpList = `govendor list [options] ( +status or import-path-filter )
List all dependencies and packages in folder tree.
Options:
-v verbose listing, show dependencies of each package
-p show file path to package instead of import path
-no-status do not prefix status to list, package names only
Examples:
$ govendor list -no-status +local
$ govendor list -p -no-status +local
$ govendor list +vend,prog +local,program
$ govendor list +local,^prog
`
var helpAdd = `govendor add [options] ( +status or import-path-filter )
Copy one or more packages into the vendor folder from GOPATH.
Options:
-n dry run and print actions that would be taken
-tree copy package(s) and all sub-folders under each package
-uncommitted allows copying a package with uncommitted changes, doesn't
update revision or checksum so it will always be out-of-date.
The following may be replaced with something else in the future.
-short if conflict, take short path
-long if conflict, take long path
`
var helpUpdate = `govendor update [options] ( +status or import-path-filter )
Update one or more packages from GOPATH into the vendor folder from GOPATH.
Options:
-n dry run and print actions that would be taken
-tree copy package(s) and all sub-folders under each package
-uncommitted allows copying a package with uncommitted changes, doesn't
update revision or checksum so it will always be out-of-date.
The following may be replaced with something else in the future.
-short if conflict, take short path
-long if conflict, take long path
`
var helpRemove = `govendor remove [options] ( +status or import-path-filter )
Remove one or more packages from the vendor folder.
Options:
-n dry run and print actions that would be taken
`
var helpFetch = `govendor fetch [options] ( +status or package-spec )
Fetches packages directly into the vendor folder.
package-spec = <path>[::<origin>][{/...|/^}][@[<version-spec>]]
Options:
-tree copy package(s) and all sub-folders under each package
-insecure allow downloading over insecure connection
-v verbose mode
`
var helpSync = `govendor sync
Ensures the contents of the vendor folder matches the vendor file.
Options:
-n dry run, print out action only
-insecure allow downloading over insecure connection
-v verbose output
`
var helpStatus = `govendor status
Shows any packages that are missing, out-of-date, or modified locally (according to the
checksum) and should be sync'ed.
`
var helpMigrate = `govendor migrate [` + strings.Join(migrate.SystemList(), ", ") + `]
Change from a one schema to use the vendor folder. Default to auto detect.
`
var helpGet = `govendor get [options] (import-path)...
Download package into GOPATH, put all dependencies into vendor folder.
Options:
-insecure allow downloading over insecure connection
-v verbose mode
`
var helpLicense = `govendor license [options] ( +status or package-spec )
Attempt to find and list licenses for the specified packages.
Options:
-o output to file name
-template template file to use, input is "[]context.License"
`
var helpShell = `govendor shell
Open a govendor "shell". Useful for faster queries on large projects.
`
var msgGovendorVersion = version + `
`

View File

@@ -0,0 +1,7 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package help
var version = "v1.0.8"

View File

@@ -0,0 +1,133 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package pathos
import (
"path/filepath"
"runtime"
"strconv"
"strings"
)
func SlashToFilepath(path string) string {
if '/' == filepath.Separator {
return path
}
return strings.Replace(path, "/", string(filepath.Separator), -1)
}
func SlashToImportPath(path string) string {
return strings.Replace(path, `\`, "/", -1)
}
func FileHasPrefix(s, prefix string) bool {
if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
s = strings.ToLower(s)
prefix = strings.ToLower(prefix)
}
return strings.HasPrefix(s, prefix)
}
func FileTrimPrefix(s, prefix string) string {
if FileHasPrefix(s, prefix) {
return s[len(prefix):]
}
return s
}
func FileHasSuffix(s, suffix string) bool {
if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
s = strings.ToLower(s)
suffix = strings.ToLower(suffix)
}
return strings.HasSuffix(s, suffix)
}
func FileTrimSuffix(s, suffix string) string {
if FileHasSuffix(s, suffix) {
return s[:len(s)-len(suffix)]
}
return s
}
var slashSep = filepath.Separator
func TrimCommonSuffix(base, suffix string) (string, string) {
a, b := base, suffix
if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
a = strings.ToLower(a)
b = strings.ToLower(b)
}
a = strings.TrimSuffix(strings.TrimSuffix(a, "\\"), "/")
b = strings.TrimSuffix(strings.TrimSuffix(b, "\\"), "/")
base = strings.TrimSuffix(strings.TrimSuffix(base, "\\"), "/")
ff := func(r rune) bool {
return r == '/' || r == '\\'
}
aa := strings.FieldsFunc(a, ff)
bb := strings.FieldsFunc(b, ff)
min := len(aa)
if min > len(bb) {
min = len(bb)
}
i := 1
for ; i <= min; i++ {
// fmt.Printf("(%d) end aa: %q, end bb: %q\n", i, aa[len(aa)-i], bb[len(bb)-i])
if aa[len(aa)-i] == bb[len(bb)-i] {
continue
}
break
}
baseParts := strings.FieldsFunc(base, ff)
// fmt.Printf("base parts: %q\n", baseParts)
base1 := FileTrimSuffix(base, strings.Join(baseParts[len(baseParts)-i+1:], string(slashSep)))
base1 = strings.TrimSuffix(strings.TrimSuffix(base1, "\\"), "/")
base2 := strings.Trim(base[len(base1):], `\/`)
return base1, base2
}
func FileStringEquals(s1, s2 string) bool {
if len(s1) == 0 {
return len(s2) == 0
}
if len(s2) == 0 {
return len(s1) == 0
}
if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
s1 = strings.ToLower(s1)
s2 = strings.ToLower(s2)
}
r1End := s1[len(s1)-1]
r2End := s2[len(s2)-1]
if r1End == '/' || r1End == '\\' {
s1 = s1[:len(s1)-1]
}
if r2End == '/' || r2End == '\\' {
s2 = s2[:len(s2)-1]
}
return s1 == s2
}
// GoEnv parses a "go env" line and checks for a specific
// variable name.
func GoEnv(name, line string) (value string, ok bool) {
// Remove any leading "set " found on windows.
// Match the name to the env var + "=".
// Remove any quotes.
// Return result.
line = strings.TrimPrefix(line, "set ")
if len(line) < len(name)+1 {
return "", false
}
if name != line[:len(name)] || line[len(name)] != '=' {
return "", false
}
line = line[len(name)+1:]
if un, err := strconv.Unquote(line); err == nil {
line = un
}
return line, true
}

View File

@@ -0,0 +1,14 @@
package vfilepath
import "strings"
func HasPrefixDir(path string, prefix string) bool {
return strings.HasPrefix(makeDirPath(path), makeDirPath(prefix))
}
func makeDirPath(path string) string {
if path != "/" {
path += "/"
}
return path
}

View File

@@ -0,0 +1,17 @@
package vfilepath
import (
"path/filepath"
)
func Split(path string) (string, string) {
return filepath.Split(path)
}
func Join(parts ...string) string {
return filepath.Join(parts...)
}
func EvalSymlinks(path string) (string, error) {
return filepath.EvalSymlinks(path)
}

View File

@@ -0,0 +1 @@
package vfilepath

View File

@@ -0,0 +1,83 @@
package vfilepath
import (
"path/filepath"
"sort"
os "github.com/kardianos/govendor/internal/vos"
)
// SkipDir is used as a return value from WalkFuncs to indicate that
// the directory named in the call is to be skipped. It is not returned
// as an error by any function.
var SkipDir = filepath.SkipDir
type WalkFunc func(path string, info os.FileInfo, err error) error
// walk recursively descends path, calling w.
func walk(path string, info os.FileInfo, walkFn WalkFunc) error {
err := walkFn(path, info, nil)
if err != nil {
if info.IsDir() && err == SkipDir {
return nil
}
return err
}
if !info.IsDir() {
return nil
}
names, err := readDirNames(path)
if err != nil {
return walkFn(path, info, err)
}
for _, name := range names {
filename := filepath.Join(path, name)
fileInfo, err := os.Lstat(filename)
if err != nil {
if err := walkFn(filename, fileInfo, err); err != nil && err != SkipDir {
return err
}
} else {
err = walk(filename, fileInfo, walkFn)
if err != nil {
if !fileInfo.IsDir() || err != SkipDir {
return err
}
}
}
}
return nil
}
// readDirNames reads the directory named by dirname and returns
// a sorted list of directory entries.
func readDirNames(dirname string) ([]string, error) {
f, err := os.Open(dirname)
if err != nil {
return nil, err
}
names, err := f.Readdirnames(-1)
f.Close()
if err != nil {
return nil, err
}
sort.Strings(names)
return names, nil
}
// Walk walks the file tree rooted at root, calling walkFn for each file or
// directory in the tree, including root. All errors that arise visiting files
// and directories are filtered by walkFn. The files are walked in lexical
// order, which makes the output deterministic but means that for very
// large directories Walk can be inefficient.
// Walk does not follow symbolic links.
func Walk(root string, walkFn WalkFunc) error {
info, err := os.Lstat(root)
if err != nil {
return walkFn(root, nil, err)
}
return walk(root, info, walkFn)
}

View File

@@ -0,0 +1,61 @@
package vos
import (
"os"
"time"
)
type FileInfo os.FileInfo
func Stat(name string) (FileInfo, error) {
l("stat", name)
fi, err := os.Stat(name)
return FileInfo(fi), err
}
func Lstat(name string) (FileInfo, error) {
l("lstat", name)
fi, err := os.Lstat(name)
return FileInfo(fi), err
}
func IsNotExist(err error) bool {
return os.IsNotExist(err)
}
func Getwd() (string, error) {
return os.Getwd()
}
func Getenv(key string) string {
return os.Getenv(key)
}
func Open(name string) (*os.File, error) {
l("open", name)
return os.Open(name)
}
func MkdirAll(path string, perm os.FileMode) error {
l("mkdirall", path)
return os.MkdirAll(path, perm)
}
func Remove(name string) error {
l("remove", name)
return os.Remove(name)
}
func RemoveAll(name string) error {
l("removeall", name)
return os.RemoveAll(name)
}
func Create(name string) (*os.File, error) {
l("create", name)
return os.Create(name)
}
func Chmod(name string, mode os.FileMode) error {
l("chmod", name)
return os.Chmod(name, mode)
}
func Chtimes(name string, atime, mtime time.Time) error {
l("chtimes", name)
return os.Chtimes(name, atime, mtime)
}

View File

@@ -0,0 +1,13 @@
package vos
import (
"log"
)
const debugLog = false
func l(fname, path string) {
if debugLog {
log.Println(fname, path)
}
}

55
tools/vendor/github.com/kardianos/govendor/main.go generated vendored Normal file
View File

@@ -0,0 +1,55 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// vendor tool to copy external source code from GOPATH or remote location to the
// local vendor folder. See README.md for usage.
package main
import (
"bytes"
"flag"
"fmt"
"io"
"os"
"strings"
"github.com/kardianos/govendor/cliprompt"
"github.com/kardianos/govendor/help"
"github.com/kardianos/govendor/run"
)
func main() {
prompt := &cliprompt.Prompt{}
allArgs := os.Args
if allArgs[len(allArgs)-1] == "-" {
stdin := &bytes.Buffer{}
if _, err := io.Copy(stdin, os.Stdin); err == nil {
stdinArgs := strings.Fields(stdin.String())
allArgs = append(allArgs[:len(allArgs)-1], stdinArgs...)
}
}
msg, err := run.Run(os.Stdout, allArgs, prompt)
if err == flag.ErrHelp {
err = nil
}
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %+v\n", err)
}
msgText := msg.String()
if len(msgText) > 0 {
fmt.Fprint(os.Stderr, msgText)
}
if err != nil {
os.Exit(2)
}
switch msg {
case help.MsgNone, help.MsgGovendorVersion, help.MsgGovendorLicense:
os.Exit(0)
default:
os.Exit(1)
}
}

View File

@@ -0,0 +1,29 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package migrate
import (
"errors"
"path/filepath"
)
func init() {
register("gb", sysGb{})
}
type sysGb struct{}
func (sys sysGb) Check(root string) (system, error) {
if hasDirs(root, "src", filepath.Join("vendor", "src")) {
return sys, nil
}
return nil, nil
}
func (sysGb) Migrate(root string) error {
// Move files from "src" to first GOPATH.
// Move vendor files from "vendor/src" to "vendor".
// Translate "vendor/manifest" to vendor.json file.
return errors.New("Migrate gb not implemented")
}

View File

@@ -0,0 +1,78 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package migrate
import (
"fmt"
"io"
"os"
"path/filepath"
"github.com/kardianos/govendor/context"
"github.com/kardianos/govendor/vendorfile"
)
func init() {
register("gdm", sysGdm{})
}
type sysGdm struct{}
func (sys sysGdm) Check(root string) (system, error) {
if hasFiles(root, "Godeps") {
return sys, nil
}
return nil, nil
}
func (sys sysGdm) Migrate(root string) error {
gdmFilePath := filepath.Join(root, "Godeps")
ctx, err := context.NewContext(root, filepath.Join("vendor", "vendor.json"), "vendor", false)
if err != nil {
return err
}
ctx.VendorFile.Ignore = "test"
f, err := os.Open(gdmFilePath)
if err != nil {
return err
}
defer f.Close()
pkgs, err := sys.parseGdmFile(f)
if err != nil {
return err
}
ctx.VendorFile.Package = pkgs
if err := ctx.WriteVendorFile(); err != nil {
return err
}
return os.RemoveAll(gdmFilePath)
}
func (sysGdm) parseGdmFile(r io.Reader) ([]*vendorfile.Package, error) {
var pkgs []*vendorfile.Package
for {
var path, rev string
if _, err := fmt.Fscanf(r, "%s %s\n", &path, &rev); err != nil {
if err == io.EOF {
break
}
return nil, err
}
pkgs = append(pkgs, &vendorfile.Package{
Add: true,
Path: path,
Revision: rev,
Tree: true,
})
}
return pkgs, nil
}

View File

@@ -0,0 +1,97 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package migrate
import (
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/kardianos/govendor/context"
"github.com/kardianos/govendor/pkgspec"
)
func init() {
register("glock", sysGlock{})
}
type sysGlock struct{}
func (sys sysGlock) Check(root string) (system, error) {
if hasFiles(root, "GLOCKFILE") {
return sys, nil
}
return nil, nil
}
func (sysGlock) Migrate(root string) error {
err := os.MkdirAll(filepath.Join(root, "vendor"), 0777)
if err != nil {
return err
}
filebytes, err := ioutil.ReadFile(filepath.Join(root, "GLOCKFILE"))
if err != nil {
return err
}
lines := strings.Split(string(filebytes), "\n")
for i, l := range lines {
lines[i] = strings.TrimSpace(l)
}
/*
vf := &vendorfile.File{}
vf.Package = make([]*vendorfile.Package, 0, len(lines))
*/
ctx, err := context.NewContext(root, filepath.Join("vendor", "vendor.json"), "vendor", false)
if err != nil {
return err
}
const cmdPrefix = "cmd "
for _, l := range lines {
if len(l) == 0 {
continue
}
isCmd := strings.HasPrefix(l, cmdPrefix)
if isCmd {
continue
}
field := strings.Fields(l)
if len(field) < 2 {
continue
}
ps, err := pkgspec.Parse("", field[0]+"@"+field[1])
if err != nil {
return err
}
ps.IncludeTree = true
err = ctx.ModifyImport(ps, context.Fetch)
if err != nil {
return err
}
}
for _, l := range lines {
if len(l) == 0 {
continue
}
isCmd := strings.HasPrefix(l, cmdPrefix)
if !isCmd {
continue
}
path := strings.TrimPrefix(l, cmdPrefix)
ps, err := pkgspec.Parse("", path)
if err != nil {
return err
}
err = ctx.ModifyImport(ps, context.Fetch)
if err != nil {
return err
}
}
err = ctx.WriteVendorFile()
os.Remove(filepath.Join(root, "GLOCKFILE"))
return err
}

View File

@@ -0,0 +1,129 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package migrate
import (
"encoding/json"
"os"
"path"
"path/filepath"
"strings"
"github.com/kardianos/govendor/context"
"github.com/kardianos/govendor/vendorfile"
)
func init() {
register("godep", sysGodep{})
}
type sysGodep struct{}
func (sys sysGodep) Check(root string) (system, error) {
if hasDirs(root, "Godeps") {
return sys, nil
}
return nil, nil
}
func (sysGodep) Migrate(root string) error {
// Determine if import paths are rewritten.
// Un-rewrite import paths.
// Copy files from Godeps/_workspace/src to "vendor".
// Translate Godeps/Godeps.json to vendor.json.
vendorFilePath := filepath.Join("Godeps", "_workspace", "src")
vendorPath := path.Join("Godeps", "_workspace", "src")
godepFilePath := filepath.Join(root, "Godeps", "Godeps.json")
ctx, err := context.NewContext(root, "vendor.json", vendorFilePath, true)
if err != nil {
return err
}
ctx.VendorDiscoverFolder = vendorPath
list, err := ctx.Status()
if err != nil {
return err
}
remove := make([]string, 0, len(list))
for _, item := range list {
if item.Status.Location != context.LocationVendor {
continue
}
pkg := ctx.Package[item.Local]
ctx.Operation = append(ctx.Operation, &context.Operation{
Pkg: pkg,
Src: pkg.Dir,
Dest: filepath.Join(ctx.RootDir, "vendor", filepath.ToSlash(item.Pkg.Path)),
})
remove = append(remove, filepath.Join(ctx.RootGopath, filepath.ToSlash(item.Local)))
ctx.RewriteRule[item.Local] = item.Pkg.Path
}
ctx.VendorFilePath = filepath.Join(ctx.RootDir, "vendor", "vendor.json")
ctx.VendorDiscoverFolder = "vendor"
ctx.VendorFile.Ignore = "test"
// Translate then remove godeps.json file.
type Godeps struct {
ImportPath string
GoVersion string // Abridged output of 'go version'.
Packages []string // Arguments to godep save, if any.
Deps []struct {
ImportPath string
Comment string // Description of commit, if present.
Rev string // VCS-specific commit ID.
}
}
godeps := Godeps{}
f, err := os.Open(godepFilePath)
if err != nil {
return err
}
coder := json.NewDecoder(f)
err = coder.Decode(&godeps)
f.Close()
if err != nil {
return err
}
for _, d := range godeps.Deps {
for _, pkg := range ctx.Package {
if strings.HasPrefix(pkg.Path, d.ImportPath) == false {
continue
}
vf := ctx.VendorFilePackagePath(pkg.Path)
if vf == nil {
ctx.VendorFile.Package = append(ctx.VendorFile.Package, &vendorfile.Package{
Add: true,
Path: pkg.Path,
Comment: d.Comment,
Revision: d.Rev,
})
}
}
}
err = ctx.WriteVendorFile()
if err != nil {
return err
}
err = ctx.Alter()
if err != nil {
return err
}
// Remove existing.
for _, r := range remove {
err = context.RemovePackage(r, "", false)
if err != nil {
return err
}
}
return os.RemoveAll(filepath.Join(root, "Godeps"))
}

View File

@@ -0,0 +1,133 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// package migrate transforms a repository from a given vendor schema to
// the vendor folder schema.
package migrate
import (
"errors"
"fmt"
"os"
"path/filepath"
"sort"
)
type ErrNoSuchSystem struct {
NotExist string
Has []string
}
func (err ErrNoSuchSystem) Error() string {
return fmt.Sprintf("Migration system for %q doesn't exist. Current systems %q.", err.NotExist, err.Has)
}
// From is the current vendor schema.
type From string
// Migrate from the given system using the current working directory.
func MigrateWD(from From) error {
wd, err := os.Getwd()
if err != nil {
return err
}
return Migrate(from, wd)
}
// SystemList list available migration systems.
func SystemList() []string {
list := make([]string, 0, len(registered))
for key := range registered {
list = append(list, string(key))
}
sort.Strings(list)
return list
}
// Migrate from the given system using the given root.
func Migrate(from From, root string) error {
sys, found := registered[from]
if !found {
return ErrNoSuchSystem{
NotExist: string(from),
Has: SystemList(),
}
}
sys, err := sys.Check(root)
if err != nil {
return err
}
if sys == nil {
return errors.New("Root not found.")
}
return sys.Migrate(root)
}
type system interface {
Check(root string) (system, error)
Migrate(root string) error
}
func register(name From, sys system) {
_, found := registered[name]
if found {
panic("system " + name + " already registered.")
}
registered[name] = sys
}
var registered = make(map[From]system, 10)
var errAutoSystemNotFound = errors.New("Unable to determine vendor system.")
func init() {
register("auto", sysAuto{})
}
type sysAuto struct{}
func (auto sysAuto) Check(root string) (system, error) {
for _, sys := range registered {
if sys == auto {
continue
}
out, err := sys.Check(root)
if err != nil {
return nil, err
}
if out != nil {
return out, nil
}
}
return nil, errAutoSystemNotFound
}
func (sysAuto) Migrate(root string) error {
return errors.New("Auto.Migrate shouldn't be called")
}
func hasDirs(root string, dd ...string) bool {
for _, d := range dd {
fi, err := os.Stat(filepath.Join(root, d))
if err != nil {
return false
}
if fi.IsDir() == false {
return false
}
}
return true
}
func hasFiles(root string, dd ...string) bool {
for _, d := range dd {
fi, err := os.Stat(filepath.Join(root, d))
if err != nil {
return false
}
if fi.IsDir() == true {
return false
}
}
return true
}

View File

@@ -0,0 +1,97 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package migrate
import (
"os"
"path/filepath"
"github.com/kardianos/govendor/context"
)
func init() {
register("internal", sysInternal{})
register("old-vendor", sysOldVendor{})
}
type sysInternal struct{}
func (sys sysInternal) Check(root string) (system, error) {
vendorFolder := "internal"
override := os.Getenv("GOVENDORFOLDER")
if len(override) != 0 {
vendorFolder = override
}
if hasDirs(root, vendorFolder) && hasFiles(root, filepath.Join(vendorFolder, "vendor.json")) {
return sys, nil
}
return nil, nil
}
func (sysInternal) Migrate(root string) error {
// Un-rewrite import paths.
// Copy files from internal to vendor.
// Update and move vendor file from "internal/vendor.json" to "vendor.json".
ctx, err := context.NewContext(root, filepath.Join("internal", "vendor.json"), "internal", true)
if err != nil {
return err
}
list, err := ctx.Status()
if err != nil {
return err
}
remove := make([]string, 0, len(list))
for _, item := range list {
if item.Status.Location != context.LocationVendor {
continue
}
pkg := ctx.Package[item.Local]
ctx.Operation = append(ctx.Operation, &context.Operation{
Pkg: pkg,
Src: pkg.Dir,
Dest: filepath.Join(ctx.RootDir, "vendor", filepath.ToSlash(item.Pkg.Path)),
})
remove = append(remove, filepath.Join(ctx.RootGopath, filepath.ToSlash(item.Local)))
ctx.RewriteRule[item.Local] = item.Pkg.Path
}
ctx.VendorFilePath = filepath.Join(ctx.RootDir, "vendor", "vendor.json")
err = ctx.WriteVendorFile()
if err != nil {
return err
}
err = ctx.Alter()
if err != nil {
return err
}
// Remove existing.
for _, r := range remove {
err = context.RemovePackage(r, "", false)
if err != nil {
return err
}
}
return os.Remove(filepath.Join(ctx.RootDir, "internal", "vendor.json"))
}
type sysOldVendor struct{}
func (sys sysOldVendor) Check(root string) (system, error) {
if hasDirs(root, "vendor") && hasFiles(root, "vendor.json") {
return sys, nil
}
return nil, nil
}
func (sysOldVendor) Migrate(root string) error {
ctx, err := context.NewContext(root, "vendor.json", "vendor", false)
if err != nil {
return err
}
ctx.VendorFilePath = filepath.Join(ctx.RootDir, "vendor", "vendor.json")
err = ctx.WriteVendorFile()
if err != nil {
return err
}
return os.Remove(filepath.Join(ctx.RootDir, "vendor.json"))
}

View File

@@ -0,0 +1,50 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package pkgspec defines a schema that contains the path, origin, version
// and other properties.
package pkgspec
import "bytes"
type Pkg struct {
Path string
FilePath string
Origin string
IncludeTree bool
MatchTree bool
HasVersion bool
HasOrigin bool
Version string
Uncommitted bool
}
func (pkg *Pkg) String() string {
buf := &bytes.Buffer{}
buf.WriteString(pkg.Path)
if pkg.IncludeTree {
buf.WriteString(TreeIncludeSuffix)
} else if pkg.MatchTree {
buf.WriteString(TreeMatchSuffix)
}
if len(pkg.Origin) > 0 {
buf.WriteString(originMatch)
buf.WriteString(pkg.Origin)
}
if pkg.HasVersion {
buf.WriteString(versionMatch)
if len(pkg.Version) > 0 {
buf.WriteString(pkg.Version)
}
}
return buf.String()
}
func (pkg *Pkg) PathOrigin() string {
if len(pkg.Origin) > 0 {
return pkg.Origin
}
return pkg.Path
}

View File

@@ -0,0 +1,99 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// package pkgspec parses the package specification string
package pkgspec
import (
"errors"
"path"
"strings"
)
const (
TreeIncludeSuffix = "/^"
TreeMatchSuffix = "/..."
)
const (
originMatch = "::"
versionMatch = "@"
vendorSegment = "/vendor/"
)
var (
ErrEmptyPath = errors.New("Empty package path")
ErrEmptyOrigin = errors.New("Empty origin specified")
ErrInvalidPath = errors.New("Path contains a vendor folder and a origin")
)
// Parse a package spec according to:
// package-spec = <path>[{/...|/^}][::<origin>][@[<version-spec>]]
func Parse(currentGoPath, s string) (*Pkg, error) {
// Clean up the import path before
s = strings.Trim(s, "/\\ \t")
if len(s) == 0 {
return nil, ErrEmptyPath
}
s = strings.Replace(s, `\`, `/`, -1)
originIndex := strings.Index(s, originMatch)
versionIndex := strings.LastIndex(s, versionMatch)
if originIndex == 0 {
return nil, ErrEmptyPath
}
// Don't count the origin if it is after the "@" symbol.
if originIndex > versionIndex && versionIndex > 0 {
originIndex = -1
}
pkg := &Pkg{
Path: s,
HasOrigin: (originIndex >= 0),
}
if versionIndex > 0 {
pkg.Path = s[:versionIndex]
pkg.Version = s[versionIndex+len(versionMatch):]
pkg.HasVersion = true
}
if originIndex > 0 {
pkg.Path = s[:originIndex]
endOrigin := len(s)
if versionIndex > 0 {
endOrigin = versionIndex
}
pkg.Origin = s[originIndex+len(originMatch) : endOrigin]
if len(pkg.Origin) == 0 {
return nil, ErrEmptyOrigin
}
}
// Look for vendor folder in package path.
// This is allowed in origin, but not path.
vendorIndex := strings.LastIndex(pkg.Path, vendorSegment)
if vendorIndex >= 0 {
if len(pkg.Origin) > 0 {
return nil, ErrInvalidPath
}
pkg.Origin = pkg.Path
pkg.Path = pkg.Path[vendorIndex+len(vendorSegment):]
}
if strings.HasSuffix(pkg.Path, TreeMatchSuffix) {
pkg.MatchTree = true
pkg.Path = strings.TrimSuffix(pkg.Path, TreeMatchSuffix)
} else if strings.HasSuffix(pkg.Path, TreeIncludeSuffix) {
pkg.IncludeTree = true
pkg.Path = strings.TrimSuffix(pkg.Path, TreeIncludeSuffix)
}
if strings.HasPrefix(pkg.Path, ".") && len(currentGoPath) != 0 {
currentGoPath = strings.Replace(currentGoPath, `\`, `/`, -1)
currentGoPath = strings.TrimPrefix(currentGoPath, "/")
pkg.Path = path.Join(currentGoPath, pkg.Path)
}
return pkg, nil
}

View File

@@ -0,0 +1,117 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package prompt prompts user for feedback.
package prompt
import (
"fmt"
)
type Option struct {
key interface{}
prompt string
validation string
other bool
Choosen bool // Set to true if choosen.
Value string // Value used if choosen and option is "other".
}
type OptionType byte
const (
TypeSelectOne OptionType = iota // Allow user to choose single option.
TypeSelectMultiple // Allow user to choose multiple options.
)
type Response byte
const (
RespAnswer Response = iota
RespCancel
)
func NewOption(key interface{}, prompt string, other bool) Option {
return Option{key: key, prompt: prompt, other: other}
}
func (opt Option) Key() interface{} {
return opt.key
}
func (opt Option) Prompt() string {
return opt.prompt
}
func (opt Option) Other() bool {
return opt.other
}
func (opt Option) Validation() string {
return opt.validation
}
func (opt Option) String() string {
if opt.other {
return opt.Value
}
return fmt.Sprintf("%v", opt.key)
}
func ValidateOption(opt Option, validation string) Option {
return Option{
key: opt.key,
prompt: opt.prompt,
other: opt.other,
validation: validation,
Choosen: opt.Choosen,
Value: opt.Value,
}
}
type Question struct {
Error string
Prompt string
Type OptionType
Options []Option
}
func (q *Question) AnswerMultiple(must bool) []*Option {
ans := []*Option{}
for i := range q.Options {
o := &q.Options[i]
if o.Choosen {
ans = append(ans, o)
}
}
if must && len(ans) == 0 {
panic("If no option is choosen, response must be cancelled")
}
return ans
}
func (q *Question) AnswerSingle(must bool) *Option {
var ans *Option
if q.Type != TypeSelectOne {
panic("Question Type should match answer type")
}
found := false
for i := range q.Options {
o := &q.Options[i]
if found && o.Choosen {
panic("Must only respond with single option")
}
if o.Choosen {
found = true
ans = o
}
}
if must && !found {
panic("If no option is choosen, response must be cancelled")
}
return ans
}
type Prompt interface {
Ask(q *Question) (Response, error)
}

View File

@@ -0,0 +1,156 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package run
import (
"flag"
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"github.com/kardianos/govendor/context"
"github.com/kardianos/govendor/help"
"github.com/kardianos/govendor/migrate"
)
func (r *runner) Init(w io.Writer, subCmdArgs []string) (help.HelpMessage, error) {
flags := flag.NewFlagSet("init", flag.ContinueOnError)
flags.SetOutput(nullWriter{})
err := flags.Parse(subCmdArgs)
if err != nil {
return help.MsgInit, err
}
ctx, err := r.NewContextWD(context.RootWD)
if err != nil {
return help.MsgNone, err
}
ctx.VendorFile.Ignore = "test" // Add default ignore rule.
err = ctx.WriteVendorFile()
if err != nil {
return help.MsgNone, err
}
err = os.MkdirAll(filepath.Join(ctx.RootDir, ctx.VendorFolder), 0777)
return help.MsgNone, err
}
func (r *runner) Migrate(w io.Writer, subCmdArgs []string) (help.HelpMessage, error) {
flags := flag.NewFlagSet("migrate", flag.ContinueOnError)
flags.SetOutput(nullWriter{})
err := flags.Parse(subCmdArgs)
if err != nil {
return help.MsgMigrate, err
}
from := migrate.From("auto")
if len(flags.Args()) > 0 {
from = migrate.From(flags.Arg(0))
}
err = migrate.MigrateWD(from)
if err != nil {
return help.MsgNone, err
}
fmt.Fprintf(w, `You may wish to run "govendor sync" now.%s`, "\n")
return help.MsgNone, nil
}
func (r *runner) Get(w io.Writer, subCmdArgs []string) (help.HelpMessage, error) {
flags := flag.NewFlagSet("get", flag.ContinueOnError)
flags.SetOutput(nullWriter{})
insecure := flags.Bool("insecure", false, "allows insecure connection")
verbose := flags.Bool("v", false, "verbose")
flags.Bool("u", false, "update") // For compatibility with "go get".
err := flags.Parse(subCmdArgs)
if err != nil {
return help.MsgGet, err
}
logger := w
if !*verbose {
logger = nil
}
for _, a := range flags.Args() {
pkg, err := context.Get(logger, a, *insecure)
if err != nil {
return help.MsgNone, err
}
r.GoCmd("install", []string{pkg.Path})
}
return help.MsgNone, nil
}
func (r *runner) GoCmd(subcmd string, args []string) (help.HelpMessage, error) {
ctx, err := r.NewContextWD(context.RootVendorOrWD)
if err != nil {
return help.MsgNone, err
}
list, err := ctx.Status()
if err != nil {
return help.MsgNone, err
}
cgp, err := currentGoPath(ctx)
if err != nil {
return help.MsgNone, err
}
otherArgs := make([]string, 1, len(args)+1)
otherArgs[0] = subcmd
// Expand any status flags in-place. Some wrapped commands the order is
// important to the operation of the command.
for _, a := range args {
if a[0] == '+' {
f, err := parseFilter(cgp, []string{a})
if err != nil {
return help.MsgNone, err
}
for _, item := range list {
if f.HasStatus(item) {
add := item.Local
// "go tool vet" takes dirs, not pkgs, so special case it.
if subcmd == "tool" && len(args) > 0 && args[0] == "vet" {
add = filepath.Join(ctx.RootGopath, add)
}
otherArgs = append(otherArgs, add)
}
}
} else {
otherArgs = append(otherArgs, a)
}
}
cmd := exec.Command("go", otherArgs...)
cmd.Stderr = os.Stderr
cmd.Stdout = os.Stdout
return help.MsgNone, cmd.Run()
}
func (r *runner) Status(w io.Writer, subCmdArgs []string) (help.HelpMessage, error) {
flags := flag.NewFlagSet("status", flag.ContinueOnError)
flags.SetOutput(nullWriter{})
err := flags.Parse(subCmdArgs)
if err != nil {
return help.MsgStatus, err
}
ctx, err := r.NewContextWD(context.RootVendor)
if err != nil {
return help.MsgStatus, err
}
outOfDate, err := ctx.VerifyVendor()
if err != nil {
return help.MsgStatus, err
}
if len(outOfDate) == 0 {
return help.MsgNone, nil
}
fmt.Fprintf(w, "The following packages are missing or modified locally:\n")
for _, pkg := range outOfDate {
fmt.Fprintf(w, "\t%s\n", pkg.Path)
}
return help.MsgNone, fmt.Errorf("status failed for %d package(s)", len(outOfDate))
}

View File

@@ -0,0 +1,179 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package run
import (
"fmt"
"os"
"strings"
"github.com/kardianos/govendor/context"
"github.com/kardianos/govendor/internal/pathos"
"github.com/kardianos/govendor/pkgspec"
)
var (
outside = []context.Status{
{Location: context.LocationExternal},
{Presence: context.PresenceMissing},
}
normal = []context.Status{
{Location: context.LocationExternal},
{Location: context.LocationVendor},
{Location: context.LocationLocal},
{Location: context.LocationNotFound},
}
all = []context.Status{
{Location: context.LocationStandard},
{Location: context.LocationExternal},
{Location: context.LocationVendor},
{Location: context.LocationLocal},
{Location: context.LocationNotFound},
}
)
func statusGroupFromList(list []context.Status, and, not bool) context.StatusGroup {
sg := context.StatusGroup{
Not: not,
And: and,
}
for _, s := range list {
sg.Status = append(sg.Status, s)
}
return sg
}
const notOp = "^"
func parseStatusGroup(statusString string) (sg context.StatusGroup, err error) {
ss := strings.Split(statusString, ",")
sg.And = true
for _, s := range ss {
st := context.Status{}
if strings.HasPrefix(s, notOp) {
st.Not = true
s = strings.TrimPrefix(s, notOp)
}
var list []context.Status
switch {
case strings.HasPrefix("external", s):
st.Location = context.LocationExternal
case strings.HasPrefix("vendor", s):
st.Location = context.LocationVendor
case strings.HasPrefix("unused", s):
st.Presence = context.PresenceUnused
case strings.HasPrefix("missing", s):
st.Presence = context.PresenceMissing
case strings.HasPrefix("xcluded", s):
st.Presence = context.PresenceExcluded
case len(s) >= 3 && strings.HasPrefix("excluded", s): // len >= 3 to distinguish from "external"
st.Presence = context.PresenceExcluded
case strings.HasPrefix("local", s):
st.Location = context.LocationLocal
case strings.HasPrefix("program", s):
st.Type = context.TypeProgram
case strings.HasPrefix("std", s):
st.Location = context.LocationStandard
case strings.HasPrefix("standard", s):
st.Location = context.LocationStandard
case strings.HasPrefix("all", s):
list = all
case strings.HasPrefix("normal", s):
list = normal
case strings.HasPrefix("outside", s):
list = outside
default:
err = fmt.Errorf("unknown status %q", s)
return
}
if len(list) == 0 {
sg.Status = append(sg.Status, st)
} else {
sg.Group = append(sg.Group, statusGroupFromList(list, false, st.Not))
}
}
return
}
type filter struct {
Status context.StatusGroup
Import []*pkgspec.Pkg
}
func (f filter) String() string {
return fmt.Sprintf("status %q, import: %q", f.Status, f.Import)
}
func (f filter) HasStatus(item context.StatusItem) bool {
return item.Status.MatchGroup(f.Status)
}
func (f filter) FindImport(item context.StatusItem) *pkgspec.Pkg {
for _, imp := range f.Import {
if imp.Path == item.Local || imp.Path == item.Pkg.Path {
return imp
}
if imp.MatchTree {
if strings.HasPrefix(item.Local, imp.Path) || strings.HasPrefix(item.Pkg.Path, imp.Path) {
return imp
}
}
}
return nil
}
func currentGoPath(ctx *context.Context) (string, error) {
wd, err := os.Getwd()
if err != nil {
return "", err
}
wdpath := pathos.FileTrimPrefix(wd, ctx.RootGopath)
wdpath = pathos.SlashToFilepath(wdpath)
wdpath = strings.Trim(wdpath, "/")
return wdpath, nil
}
func parseFilter(currentGoPath string, args []string) (filter, error) {
f := filter{
Import: make([]*pkgspec.Pkg, 0, len(args)),
}
for _, a := range args {
if len(a) == 0 {
continue
}
// Check if item is a status.
if a[0] == '+' {
sg, err := parseStatusGroup(a[1:])
if err != nil {
return f, err
}
f.Status.Group = append(f.Status.Group, sg)
} else {
pkg, err := pkgspec.Parse(currentGoPath, a)
if err != nil {
return f, err
}
f.Import = append(f.Import, pkg)
}
}
return f, nil
}
func insertListToAllNot(sg *context.StatusGroup, list []context.Status) {
if len(sg.Group) == 0 {
allStatusNot := true
for _, s := range sg.Status {
if s.Not == false {
allStatusNot = false
break
}
}
if allStatusNot {
sg.Group = append(sg.Group, statusGroupFromList(list, false, false))
}
}
for i := range sg.Group {
insertListToAllNot(&sg.Group[i], list)
}
}

View File

@@ -0,0 +1,107 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package run
import (
"flag"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"sort"
"text/template"
"github.com/kardianos/govendor/context"
"github.com/kardianos/govendor/help"
)
var defaultLicenseTemplate = `{{range $index, $t := .}}{{if ne $index 0}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
{{end}}{{.Filename}} - {{.Path}}
{{.Text}}{{end}}
`
func (r *runner) License(w io.Writer, subCmdArgs []string) (help.HelpMessage, error) {
flags := flag.NewFlagSet("license", flag.ContinueOnError)
flags.SetOutput(nullWriter{})
outputFilename := flags.String("o", "", "output")
templateFilename := flags.String("template", "", "custom template file")
err := flags.Parse(subCmdArgs)
if err != nil {
return help.MsgLicense, err
}
args := flags.Args()
templateText := defaultLicenseTemplate
if len(*templateFilename) > 0 {
text, err := ioutil.ReadFile(*templateFilename)
if err != nil {
return help.MsgNone, err
}
templateText = string(text)
}
t, err := template.New("").Parse(templateText)
if err != nil {
return help.MsgNone, err
}
output := w
if len(*outputFilename) > 0 {
f, err := os.Create(*outputFilename)
if err != nil {
return help.MsgNone, err
}
defer f.Close()
output = f
}
ctx, err := r.NewContextWD(context.RootVendorOrWD)
if err != nil {
return checkNewContextError(err)
}
cgp, err := currentGoPath(ctx)
if err != nil {
return help.MsgNone, err
}
f, err := parseFilter(cgp, args)
if err != nil {
return help.MsgLicense, err
}
if len(f.Import) == 0 {
insertListToAllNot(&f.Status, normal)
} else {
insertListToAllNot(&f.Status, all)
}
list, err := ctx.Status()
if err != nil {
return help.MsgNone, err
}
var licenseList context.LicenseSort
var lmap = make(map[string]context.License, 9)
err = context.LicenseDiscover(filepath.Clean(filepath.Join(ctx.Goroot, "..")), ctx.Goroot, " go", lmap)
if err != nil {
return help.MsgNone, fmt.Errorf("Failed to discover license for Go %q %v", ctx.Goroot, err)
}
for _, item := range list {
if f.HasStatus(item) == false {
continue
}
if len(f.Import) != 0 && f.FindImport(item) == nil {
continue
}
err = context.LicenseDiscover(ctx.RootGopath, filepath.Join(ctx.RootGopath, item.Local), "", lmap)
if err != nil {
return help.MsgNone, fmt.Errorf("Failed to discover license for %q %v", item.Local, err)
}
}
for _, l := range lmap {
licenseList = append(licenseList, l)
}
sort.Sort(licenseList)
return help.MsgNone, t.Execute(output, licenseList)
}

132
tools/vendor/github.com/kardianos/govendor/run/list.go generated vendored Normal file
View File

@@ -0,0 +1,132 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package run
import (
"flag"
"fmt"
"io"
"strings"
"text/tabwriter"
"github.com/kardianos/govendor/context"
"github.com/kardianos/govendor/help"
)
func (r *runner) List(w io.Writer, subCmdArgs []string) (help.HelpMessage, error) {
listFlags := flag.NewFlagSet("list", flag.ContinueOnError)
listFlags.SetOutput(nullWriter{})
verbose := listFlags.Bool("v", false, "verbose")
asFilePath := listFlags.Bool("p", false, "show file path to package instead of import path")
noStatus := listFlags.Bool("no-status", false, "do not show the status")
err := listFlags.Parse(subCmdArgs)
if err != nil {
return help.MsgList, err
}
args := listFlags.Args()
// fmt.Printf("Status: %q\n", f.Status)
// Print all listed status.
ctx, err := r.NewContextWD(context.RootVendorOrWD)
if err != nil {
return checkNewContextError(err)
}
cgp, err := currentGoPath(ctx)
if err != nil {
return help.MsgNone, err
}
f, err := parseFilter(cgp, args)
if err != nil {
return help.MsgList, err
}
if len(f.Import) == 0 {
insertListToAllNot(&f.Status, normal)
} else {
insertListToAllNot(&f.Status, all)
}
list, err := ctx.Status()
if err != nil {
return help.MsgNone, err
}
// If not verbose, remove any entries that will just confuse people.
// For example, one package may reference pkgA inside vendor, another
// package may reference pkgA outside vendor, resulting in both a
// external reference and a vendor reference.
// In the above case, remove the external reference.
if !*verbose {
next := make([]context.StatusItem, 0, len(list))
for checkIndex, check := range list {
if check.Status.Location != context.LocationExternal {
next = append(next, check)
continue
}
found := false
for lookIndex, look := range list {
if checkIndex == lookIndex {
continue
}
if check.Pkg.Path != look.Pkg.Path {
continue
}
if look.Status.Location == context.LocationVendor {
found = true
break
}
}
if !found {
next = append(next, check)
}
}
list = next
}
formatSame := "%[1]v %[2]s\t%[3]s\t%[4]s\n"
formatDifferent := "%[1]v %[2]s\t%[4]s\t%[5]s\n"
if *verbose {
formatDifferent = "%[1]v %[2]s ::%[3]s\t%[4]s\t%[5]s\n"
}
if *noStatus {
formatSame = "%[2]s\n"
formatDifferent = "%[2]s\n"
if *verbose {
formatDifferent = "%[2]s ::%[3]s\n"
}
}
tw := tabwriter.NewWriter(w, 0, 4, 2, ' ', 0)
defer tw.Flush()
for _, item := range list {
if f.HasStatus(item) == false {
continue
}
if len(f.Import) != 0 && f.FindImport(item) == nil {
continue
}
var path string
if *asFilePath {
path = item.Pkg.FilePath
} else {
path = item.Pkg.Path
}
if item.Local == item.Pkg.Path {
fmt.Fprintf(tw, formatSame, item.Status, path, item.Pkg.Version, item.VersionExact)
} else {
fmt.Fprintf(tw, formatDifferent, item.Status, path, strings.TrimPrefix(item.Local, ctx.RootImportPath), item.Pkg.Version, item.VersionExact)
}
if *verbose {
for i, imp := range item.ImportedBy {
if i != len(item.ImportedBy)-1 {
fmt.Fprintf(tw, " ├── %s %s\n", imp.Status, imp)
} else {
fmt.Fprintf(tw, " └── %s %s\n", imp.Status, imp)
}
}
}
}
return help.MsgNone, nil
}

View File

@@ -0,0 +1,161 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package run
import (
"errors"
"flag"
"fmt"
"io"
"github.com/kardianos/govendor/context"
"github.com/kardianos/govendor/help"
"github.com/kardianos/govendor/prompt"
)
func (r *runner) Modify(w io.Writer, subCmdArgs []string, mod context.Modify, ask prompt.Prompt) (help.HelpMessage, error) {
msg := help.MsgFull
switch mod {
case context.Add:
msg = help.MsgAdd
case context.Update, context.AddUpdate:
msg = help.MsgUpdate
case context.Remove:
msg = help.MsgRemove
case context.Fetch:
msg = help.MsgFetch
}
var err error
/*
// Fake example prompt.
q := &prompt.Question{
Error: "An error goes here",
Prompt: "Do you want to do this?",
Type: prompt.TypeSelectOne,
Options: []prompt.Option{
prompt.NewOption("yes", "Yes, continue", false),
prompt.NewOption("no", "No, stop", false),
prompt.NewOption("", "What?", true),
},
}
q.Options[2].Choosen = true
q.Options[2] = prompt.ValidateOption(q.Options[2], "Choose again!")
resp, err := ask.Ask(q)
if err != nil {
return msg, err
}
if resp == prompt.RespCancel {
fmt.Printf("Cancelled\n")
return help.MsgNone, nil
}
choosen := q.AnswerSingle(true)
fmt.Printf("Choosen: %s\n", choosen.String())
*/
listFlags := flag.NewFlagSet("mod", flag.ContinueOnError)
listFlags.SetOutput(nullWriter{})
dryrun := listFlags.Bool("n", false, "dry-run")
verbose := listFlags.Bool("v", false, "verbose")
short := listFlags.Bool("short", false, "choose the short path")
long := listFlags.Bool("long", false, "choose the long path")
tree := listFlags.Bool("tree", false, "copy all folders including and under selected folder")
insecure := listFlags.Bool("insecure", false, "allow insecure network updates")
uncommitted := listFlags.Bool("uncommitted", false, "allows adding uncommitted changes. Doesn't update revision or checksum")
err = listFlags.Parse(subCmdArgs)
if err != nil {
return msg, err
}
if *short && *long {
return help.MsgNone, errors.New("cannot select both long and short path")
}
args := listFlags.Args()
if len(args) == 0 {
return msg, errors.New("missing package or status")
}
ctx, err := r.NewContextWD(context.RootVendor)
if err != nil {
return checkNewContextError(err)
}
if *verbose {
ctx.Logger = w
}
ctx.Insecure = *insecure
cgp, err := currentGoPath(ctx)
if err != nil {
return msg, err
}
f, err := parseFilter(cgp, args)
if err != nil {
return msg, err
}
mops := make([]context.ModifyOption, 0, 3)
if *uncommitted {
mops = append(mops, context.Uncommitted)
}
if *tree {
mops = append(mops, context.IncludeTree)
}
// Add explicit imports.
for _, imp := range f.Import {
err = ctx.ModifyImport(imp, mod, mops...)
if err != nil {
return help.MsgNone, err
}
}
err = ctx.ModifyStatus(f.Status, mod, mops...)
if err != nil {
return help.MsgNone, err
}
// Auto-resolve package conflicts.
conflicts := ctx.Check()
conflicts = ctx.ResolveAutoVendorFileOrigin(conflicts)
if *long {
conflicts = context.ResolveAutoLongestPath(conflicts)
}
if *short {
conflicts = context.ResolveAutoShortestPath(conflicts)
}
ctx.ResloveApply(conflicts)
// TODO: loop through conflicts to see if there are any remaining conflicts.
// Print out any here.
if *dryrun {
for _, op := range ctx.Operation {
switch op.Type {
case context.OpRemove:
fmt.Fprintf(w, "Remove %q\n", op.Src)
case context.OpCopy:
fmt.Fprintf(w, "Copy %q -> %q\n", op.Src, op.Dest)
for _, ignore := range op.IgnoreFile {
fmt.Fprintf(w, "\tIgnore %q\n", ignore)
}
case context.OpFetch:
fmt.Fprintf(w, "Fetch %q\n", op.Src)
}
}
return help.MsgNone, nil
}
// Write intent, make the changes, then record any checksums or recursive info.
err = ctx.WriteVendorFile()
if err != nil {
return help.MsgNone, err
}
// Write out vendor file and do change.
err = ctx.Alter()
vferr := ctx.WriteVendorFile()
if err != nil {
return help.MsgNone, err
}
if vferr != nil {
return help.MsgNone, vferr
}
return help.MsgNone, nil
}

117
tools/vendor/github.com/kardianos/govendor/run/run.go generated vendored Normal file
View File

@@ -0,0 +1,117 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package run is a front-end to govendor.
package run
import (
"flag"
"fmt"
"io"
"github.com/kardianos/govendor/context"
"github.com/kardianos/govendor/help"
"github.com/kardianos/govendor/prompt"
)
type nullWriter struct{}
func (nw nullWriter) Write(p []byte) (n int, err error) {
return len(p), nil
}
type runner struct {
ctx *context.Context
}
func (r *runner) NewContextWD(rt context.RootType) (*context.Context, error) {
if r.ctx != nil {
return r.ctx, nil
}
var err error
r.ctx, err = context.NewContextWD(rt)
return r.ctx, err
}
// Run is isoloated from main and os.Args to help with testing.
// Shouldn't directly print to console, just write through w.
func Run(w io.Writer, appArgs []string, ask prompt.Prompt) (help.HelpMessage, error) {
r := &runner{}
return r.run(w, appArgs, ask)
}
func (r *runner) run(w io.Writer, appArgs []string, ask prompt.Prompt) (help.HelpMessage, error) {
if len(appArgs) == 1 {
return help.MsgFull, nil
}
flags := flag.NewFlagSet("govendor", flag.ContinueOnError)
licenses := flags.Bool("govendor-licenses", false, "show govendor's licenses")
version := flags.Bool("version", false, "show govendor version")
flags.SetOutput(nullWriter{})
err := flags.Parse(appArgs[1:])
if err != nil {
return help.MsgFull, err
}
if *licenses {
return help.MsgGovendorLicense, nil
}
if *version {
return help.MsgGovendorVersion, nil
}
args := flags.Args()
cmd := args[0]
switch cmd {
case "init":
return r.Init(w, args[1:])
case "list":
return r.List(w, args[1:])
case "add", "update", "remove", "fetch":
var mod context.Modify
switch cmd {
case "add":
mod = context.Add
case "update":
mod = context.Update
case "remove":
mod = context.Remove
case "fetch":
mod = context.Fetch
}
return r.Modify(w, args[1:], mod, ask)
case "sync":
return r.Sync(w, args[1:])
case "status":
return r.Status(w, args[1:])
case "migrate":
return r.Migrate(w, args[1:])
case "get":
return r.Get(w, args[1:])
case "license":
return r.License(w, args[1:])
case "shell":
return r.Shell(w, args[1:])
case "fmt", "build", "install", "clean", "test", "vet", "generate", "tool":
return r.GoCmd(cmd, args[1:])
default:
return help.MsgFull, fmt.Errorf("Unknown command %q", cmd)
}
}
func checkNewContextError(err error) (help.HelpMessage, error) {
// Diagnose error, show current value of 1.5vendor, suggest alter.
if err == nil {
return help.MsgNone, nil
}
if _, is := err.(context.ErrMissingVendorFile); is {
err = fmt.Errorf(`%v
Ensure the current folder or a parent folder contains a folder named "vendor".
If in doubt, run "govendor init" in the project root.
`, err)
return help.MsgNone, err
}
return help.MsgNone, err
}

View File

@@ -0,0 +1,62 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package run
import (
"flag"
"fmt"
"io"
"os"
"github.com/kardianos/govendor/help"
"github.com/Bowery/prompt"
"github.com/google/shlex"
)
func (r *runner) Shell(w io.Writer, subCmdArgs []string) (help.HelpMessage, error) {
flags := flag.NewFlagSet("shell", flag.ContinueOnError)
flags.SetOutput(nullWriter{})
err := flags.Parse(subCmdArgs)
if err != nil {
return help.MsgShell, err
}
out := os.Stdout
for {
line, err := prompt.Basic("> ", false)
if err != nil {
break
}
args, err := shlex.Split(line)
if err != nil {
fmt.Fprintf(out, "%v", err.Error())
}
if len(args) == 0 {
continue
}
cmd := args[0]
next := make([]string, 0, len(args)+1)
next = append(next, "govendor")
args = append(next, args...)
switch cmd {
case "exit", "q", "quit", "/q":
return help.MsgNone, nil
case "shell":
continue
}
msg, err := r.run(out, args, nil)
if err != nil {
fmt.Fprintf(out, "%v", err.Error())
}
msgText := msg.String()
if len(msgText) > 0 {
fmt.Fprintf(out, "%s\tType \"exit\" to exit.\n", msgText)
}
}
return help.MsgNone, nil
}

34
tools/vendor/github.com/kardianos/govendor/run/sync.go generated vendored Normal file
View File

@@ -0,0 +1,34 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package run
import (
"flag"
"io"
"github.com/kardianos/govendor/context"
"github.com/kardianos/govendor/help"
)
func (r *runner) Sync(w io.Writer, subCmdArgs []string) (help.HelpMessage, error) {
flags := flag.NewFlagSet("sync", flag.ContinueOnError)
insecure := flags.Bool("insecure", false, "allow insecure network updates")
dryrun := flags.Bool("n", false, "dry run, print what would be done")
verbose := flags.Bool("v", false, "verbose output")
flags.SetOutput(nullWriter{})
err := flags.Parse(subCmdArgs)
if err != nil {
return help.MsgSync, err
}
ctx, err := r.NewContextWD(context.RootVendor)
if err != nil {
return help.MsgSync, err
}
ctx.Insecure = *insecure
if *dryrun || *verbose {
ctx.Logger = w
}
return help.MsgNone, ctx.Sync(*dryrun)
}

61
tools/vendor/github.com/kardianos/govendor/vcs/bzr.go generated vendored Normal file
View File

@@ -0,0 +1,61 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package vcs
import (
"os/exec"
"path/filepath"
"strings"
"time"
os "github.com/kardianos/govendor/internal/vos"
)
type VcsBzr struct{}
func (VcsBzr) Find(dir string) (*VcsInfo, error) {
fi, err := os.Stat(filepath.Join(dir, ".bzr"))
if err != nil {
if os.IsNotExist(err) {
return nil, nil
}
return nil, err
}
if fi.IsDir() == false {
return nil, nil
}
// Get info.
info := &VcsInfo{}
cmd := exec.Command("bzr", "status")
cmd.Dir = dir
output, err := cmd.CombinedOutput()
if err != nil {
return nil, err
}
if string(output) != "" {
info.Dirty = true
}
cmd = exec.Command("bzr", "log", "-r-1")
cmd.Dir = dir
output, err = cmd.CombinedOutput()
if err != nil {
return nil, err
}
for _, line := range strings.Split(string(output), "\n") {
if strings.HasPrefix(line, "revno:") {
info.Revision = strings.Split(strings.TrimSpace(strings.TrimPrefix(line, "revno:")), " ")[0]
} else if strings.HasPrefix(line, "timestamp:") {
tm, err := time.Parse("Mon 2006-01-02 15:04:05 -0700", strings.TrimSpace(strings.TrimPrefix(line, "timestamp:")))
if err != nil {
return nil, err
}
info.RevisionTime = &tm
}
}
return info, nil
}

55
tools/vendor/github.com/kardianos/govendor/vcs/git.go generated vendored Normal file
View File

@@ -0,0 +1,55 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package vcs
import (
"os/exec"
"path/filepath"
"strings"
"time"
os "github.com/kardianos/govendor/internal/vos"
)
type VcsGit struct{}
func (VcsGit) Find(dir string) (*VcsInfo, error) {
fi, err := os.Stat(filepath.Join(dir, ".git"))
if err != nil {
if os.IsNotExist(err) {
return nil, nil
}
return nil, err
}
if fi.IsDir() == false {
return nil, nil
}
// Get info.
info := &VcsInfo{}
cmd := exec.Command("git", "status", "--short")
cmd.Dir = dir
err = cmd.Run()
if err != nil {
info.Dirty = true
}
cmd = exec.Command("git", "show", "--pretty=format:%H@%ai", "-s")
cmd.Dir = dir
output, err := cmd.CombinedOutput()
if err != nil {
return nil, err
}
line := strings.TrimSpace(string(output))
ss := strings.Split(line, "@")
info.Revision = ss[0]
tm, err := time.Parse("2006-01-02 15:04:05 -0700", ss[1])
if err != nil {
return nil, err
}
info.RevisionTime = &tm
return info, nil
}

65
tools/vendor/github.com/kardianos/govendor/vcs/hg.go generated vendored Normal file
View File

@@ -0,0 +1,65 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package vcs
import (
"os/exec"
"path/filepath"
"strings"
"time"
os "github.com/kardianos/govendor/internal/vos"
)
type VcsHg struct{}
func (VcsHg) Find(dir string) (*VcsInfo, error) {
fi, err := os.Stat(filepath.Join(dir, ".hg"))
if err != nil {
if os.IsNotExist(err) {
return nil, nil
}
return nil, err
}
if fi.IsDir() == false {
return nil, nil
}
// Get info.
info := &VcsInfo{}
cmd := exec.Command("hg", "identify", "-i")
cmd.Dir = dir
output, err := cmd.CombinedOutput()
if err != nil {
return nil, err
}
rev := strings.TrimSpace(string(output))
if strings.HasSuffix(rev, "+") {
info.Dirty = true
rev = strings.TrimSuffix(rev, "+")
}
cmd = exec.Command("hg", "log", "-r", rev)
cmd.Dir = dir
output, err = cmd.CombinedOutput()
if err != nil {
return nil, err
}
for _, line := range strings.Split(string(output), "\n") {
if strings.HasPrefix(line, "changeset:") {
ss := strings.Split(line, ":")
info.Revision = strings.TrimSpace(ss[len(ss)-1])
}
if strings.HasPrefix(line, "date:") {
line = strings.TrimPrefix(line, "date:")
tm, err := time.Parse("Mon Jan 02 15:04:05 2006 -0700", strings.TrimSpace(line))
if err == nil {
info.RevisionTime = &tm
}
}
}
return info, nil
}

60
tools/vendor/github.com/kardianos/govendor/vcs/svn.go generated vendored Normal file
View File

@@ -0,0 +1,60 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package vcs
import (
"encoding/xml"
"os/exec"
"path/filepath"
"time"
os "github.com/kardianos/govendor/internal/vos"
)
type VcsSvn struct{}
func (svn VcsSvn) Find(dir string) (*VcsInfo, error) {
fi, err := os.Stat(filepath.Join(dir, ".svn"))
if err != nil {
if os.IsNotExist(err) {
return nil, nil
}
return nil, err
}
if fi.IsDir() == false {
return nil, nil
}
// Get info.
info := &VcsInfo{}
cmd := exec.Command("svn", "info", "--xml")
cmd.Dir = dir
output, err := cmd.CombinedOutput()
if err != nil {
return nil, err
}
return info, svn.parseInfo(output, info)
}
func (svn VcsSvn) parseInfo(output []byte, info *VcsInfo) error {
var err error
XX := struct {
Commit struct {
Revision string `xml:"revision,attr"`
RevisionTime string `xml:"date"`
} `xml:"entry>commit"`
}{}
err = xml.Unmarshal(output, &XX)
if err != nil {
return err
}
info.Revision = XX.Commit.Revision
tm, err := time.Parse(time.RFC3339, XX.Commit.RevisionTime)
if err == nil {
info.RevisionTime = &tm
}
return nil
}

79
tools/vendor/github.com/kardianos/govendor/vcs/vcs.go generated vendored Normal file
View File

@@ -0,0 +1,79 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// package vcs gets version control information from the file system.
package vcs
import (
"path/filepath"
"sync"
"time"
"github.com/kardianos/govendor/internal/pathos"
)
// VcsInfo returns information about a given repo.
type VcsInfo struct {
Dirty bool
Revision string
RevisionTime *time.Time
}
// Vcs represents a version control system.
type Vcs interface {
// Return nil VcsInfo if unable to determine VCS from directory.
Find(dir string) (*VcsInfo, error)
}
var vcsRegistry = []Vcs{
VcsGit{},
VcsHg{},
VcsSvn{},
VcsBzr{},
}
var registerSync = sync.Mutex{}
// RegisterVCS adds a new VCS to use.
func RegisterVCS(vcs Vcs) {
registerSync.Lock()
defer registerSync.Unlock()
vcsRegistry = append(vcsRegistry, vcs)
}
const looplimit = 10000
// FindVcs determines the version control information given a package dir and
// lowest root dir.
func FindVcs(root, packageDir string) (info *VcsInfo, err error) {
if !filepath.IsAbs(root) {
return nil, nil
}
if !filepath.IsAbs(packageDir) {
return nil, nil
}
path := packageDir
for i := 0; i <= looplimit; i++ {
for _, vcs := range vcsRegistry {
info, err = vcs.Find(path)
if err != nil {
return nil, err
}
if info != nil {
return info, nil
}
}
nextPath := filepath.Clean(filepath.Join(path, ".."))
// Check for root.
if nextPath == path {
return nil, nil
}
if pathos.FileHasPrefix(nextPath, root) == false {
return nil, nil
}
path = nextPath
}
panic("loop limit")
}

View File

@@ -0,0 +1,348 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package vendorfile is the meta-data file for vendoring.
// Round-trips unknown fields.
// It will also allow moving the vendor file to new locations.
package vendorfile
import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"sort"
)
// Name of the vendor file.
const Name = "vendor.json"
// File is the structure of the vendor file.
type File struct {
RootPath string // Import path of vendor folder
Comment string
Ignore string
Package []*Package
// all preserves unknown values.
all map[string]interface{}
}
// Package represents each package.
type Package struct {
field map[string]interface{}
// If delete is set to true the package will not be written to the vendor file.
Remove bool
// If new is set to true the package will be treated as a new package to the file.
Add bool
// See the vendor spec for definitions.
Origin string
Path string
Tree bool
Revision string
RevisionTime string
Version string
VersionExact string
ChecksumSHA1 string
Comment string
}
func (pkg *Package) PathOrigin() string {
if len(pkg.Origin) > 0 {
return pkg.Origin
}
return pkg.Path
}
// The following stringer functions are useful for debugging.
type packageList []*Package
func (list packageList) String() string {
buf := &bytes.Buffer{}
for _, item := range list {
buf.WriteString("\t")
buf.WriteString(fmt.Sprintf("(%v) ", item.field))
if item.Remove {
buf.WriteString(" X ")
}
buf.WriteString(item.Path)
buf.WriteRune('\n')
}
buf.WriteRune('\n')
return buf.String()
}
func allString(all map[string]interface{}) string {
obj, _ := all["package"]
buf := &bytes.Buffer{}
for _, itemObj := range obj.([]interface{}) {
item := itemObj.(map[string]interface{})
buf.WriteString("\t")
buf.WriteString(item["path"].(string))
buf.WriteRune('\n')
}
buf.WriteRune('\n')
return buf.String()
}
var (
rootPathNames = []string{"rootPath"}
packageNames = []string{"package", "Package"}
ignoreNames = []string{"ignore"}
originNames = []string{"origin"}
pathNames = []string{"path", "canonical", "Canonical", "vendor", "Vendor"}
treeNames = []string{"tree"}
revisionNames = []string{"revision", "Revision", "version", "Version"}
revisionTimeNames = []string{"revisionTime", "RevisionTime", "versionTime", "VersionTime"}
versionNames = []string{"version"}
versionExactNames = []string{"versionExact"}
checksumSHA1Names = []string{"checksumSHA1"}
commentNames = []string{"comment", "Comment"}
)
type vendorPackageSort []interface{}
func (vp vendorPackageSort) Len() int { return len(vp) }
func (vp vendorPackageSort) Swap(i, j int) { vp[i], vp[j] = vp[j], vp[i] }
func (vp vendorPackageSort) Less(i, j int) bool {
a := vp[i].(map[string]interface{})
b := vp[j].(map[string]interface{})
aPath, _ := a[pathNames[0]].(string)
bPath, _ := b[pathNames[0]].(string)
if aPath == bPath {
aOrigin, _ := a[originNames[0]].(string)
bOrigin, _ := b[originNames[0]].(string)
return len(aOrigin) > len(bOrigin)
}
return aPath < bPath
}
func setField(fieldObj interface{}, object map[string]interface{}, names []string) {
loop:
for _, name := range names {
raw, found := object[name]
if !found {
continue
}
switch field := fieldObj.(type) {
default:
panic("unknown type")
case *string:
value, is := raw.(string)
if !is {
continue loop
}
*field = value
if len(value) != 0 {
break loop
}
case *bool:
value, is := raw.(bool)
if !is {
continue loop
}
*field = value
if value == true {
break loop
}
}
}
}
func setObject(fieldObj interface{}, object map[string]interface{}, names []string, hideEmpty bool) {
switch field := fieldObj.(type) {
default:
panic("unknown type")
case string:
for i, name := range names {
if i != 0 || (hideEmpty && len(field) == 0) {
delete(object, name)
continue
}
object[name] = field
}
case bool:
for i, name := range names {
if i != 0 || (hideEmpty && field == false) {
delete(object, name)
continue
}
object[name] = field
}
}
}
// getRawPackageList gets the array of items from all object.
func (vf *File) getRawPackageList() []interface{} {
var rawPackageList []interface{}
for index, name := range packageNames {
rawPackageListObject, found := vf.all[name]
if !found {
continue
}
if index != 0 {
vf.all[packageNames[0]] = rawPackageListObject
delete(vf.all, name)
}
var is bool
rawPackageList, is = rawPackageListObject.([]interface{})
if is {
break
}
}
return rawPackageList
}
// toFields moves values from "all" to the field values.
func (vf *File) toFields() {
setField(&vf.RootPath, vf.all, rootPathNames)
setField(&vf.Comment, vf.all, commentNames)
setField(&vf.Ignore, vf.all, ignoreNames)
rawPackageList := vf.getRawPackageList()
vf.Package = make([]*Package, len(rawPackageList))
for index, rawPackage := range rawPackageList {
object, is := rawPackage.(map[string]interface{})
if !is {
continue
}
pkg := &Package{}
vf.Package[index] = pkg
pkg.field = object
setField(&pkg.Origin, object, originNames)
setField(&pkg.Path, object, pathNames)
setField(&pkg.Tree, object, treeNames)
setField(&pkg.Revision, object, revisionNames)
setField(&pkg.RevisionTime, object, revisionTimeNames)
setField(&pkg.Version, object, versionNames)
setField(&pkg.VersionExact, object, versionExactNames)
setField(&pkg.ChecksumSHA1, object, checksumSHA1Names)
setField(&pkg.Comment, object, commentNames)
}
}
// toAll moves values from field values to "all".
func (vf *File) toAll() {
delete(vf.all, "Tool")
setObject(vf.RootPath, vf.all, rootPathNames, true)
setObject(vf.Comment, vf.all, commentNames, false)
setObject(vf.Ignore, vf.all, ignoreNames, false)
rawPackageList := vf.getRawPackageList()
setPkgFields := func(pkg *Package) {
if pkg.Origin == pkg.Path {
pkg.Origin = ""
}
if pkg.field == nil {
pkg.field = make(map[string]interface{}, 10)
}
setObject(pkg.Origin, pkg.field, originNames, true)
setObject(pkg.Path, pkg.field, pathNames, false)
setObject(pkg.Tree, pkg.field, treeNames, true)
setObject(pkg.Revision, pkg.field, revisionNames, false)
setObject(pkg.RevisionTime, pkg.field, revisionTimeNames, true)
setObject(pkg.Version, pkg.field, versionNames, true)
setObject(pkg.VersionExact, pkg.field, versionExactNames, true)
setObject(pkg.ChecksumSHA1, pkg.field, checksumSHA1Names, true)
setObject(pkg.Comment, pkg.field, commentNames, true)
}
for i := len(vf.Package) - 1; i >= 0; i-- {
pkg := vf.Package[i]
switch {
case pkg.Remove:
for index, rawObj := range rawPackageList {
raw, is := rawObj.(map[string]interface{})
if !is {
continue
}
same := true
for key, value := range pkg.field {
if raw[key] != value {
same = false
break
}
}
if same {
rawPackageList[index] = nil
}
}
case pkg.Add:
setPkgFields(pkg)
rawPackageList = append(rawPackageList, pkg.field)
default:
if pkg.field == nil {
pkg.field = make(map[string]interface{}, 10)
}
delete(pkg.field, "local")
delete(pkg.field, "Local")
setPkgFields(pkg)
}
}
nextRawPackageList := make([]interface{}, 0, len(rawPackageList))
for _, raw := range rawPackageList {
if raw == nil {
continue
}
nextRawPackageList = append(nextRawPackageList, raw)
}
vf.all[packageNames[0]] = nextRawPackageList
}
// Marshal the vendor file to the specified writer.
// Retains read fields.
func (vf *File) Marshal(w io.Writer) error {
if vf.all == nil {
vf.all = map[string]interface{}{}
}
vf.toAll()
rawList := vf.getRawPackageList()
sort.Sort(vendorPackageSort(rawList))
jb, err := json.Marshal(vf.all)
if err != nil {
return err
}
buf := &bytes.Buffer{}
err = json.Indent(buf, jb, "", "\t")
if err != nil {
return err
}
_, err = io.Copy(w, buf)
return err
}
// Unmarshal the vendor file from the specified reader.
// Stores internally all fields.
func (vf *File) Unmarshal(r io.Reader) error {
bb, err := ioutil.ReadAll(r)
if err != nil {
return err
}
if vf.all == nil {
vf.all = make(map[string]interface{}, 3)
}
err = json.Unmarshal(bb, &vf.all)
if err != nil {
return err
}
vf.toFields()
return nil
}