зеркало из https://github.com/golang/dep.git
internal/importers: add govendor importer
Move govendor files into importers pkg Expose the base importer's source manager It is used by the govendor importer to detect ignored packaegs Update govendor importer to use base importer Document exported members Remove unused testdata Import ignores with wildcards Add govendor support to changelog Do not import vendored sources Don't require a revision to import govendor config There are valid govendor configs in the wild that do not have a revision set, essentially requiring the package but not locking to a revision. We should allow that and not stop the import.
This commit is contained in:
Родитель
d77cf70f5b
Коммит
56cefc4d23
|
@ -1,5 +1,9 @@
|
|||
# v0.3.3 (Unreleased)
|
||||
|
||||
NEW FEATURES:
|
||||
* Add support for importing from [govendor](https://github.com/kardianos/govendor)
|
||||
based projects (#815).
|
||||
|
||||
BUG FIXES:
|
||||
|
||||
* Releases targeting Windows now have a `.exe` suffix (#1291).
|
||||
|
|
|
@ -1,215 +0,0 @@
|
|||
// 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 main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/dep"
|
||||
fb "github.com/golang/dep/internal/feedback"
|
||||
"github.com/golang/dep/internal/gps"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const govendorDir = "vendor"
|
||||
const govendorName = "vendor.json"
|
||||
|
||||
type govendorImporter struct {
|
||||
file govendorFile
|
||||
|
||||
logger *log.Logger
|
||||
verbose bool
|
||||
sm gps.SourceManager
|
||||
}
|
||||
|
||||
func newGovendorImporter(logger *log.Logger, verbose bool, sm gps.SourceManager) *govendorImporter {
|
||||
return &govendorImporter{
|
||||
logger: logger,
|
||||
verbose: verbose,
|
||||
sm: sm,
|
||||
}
|
||||
}
|
||||
|
||||
// File is the structure of the vendor file.
|
||||
type govendorFile struct {
|
||||
RootPath string // Import path of vendor folder
|
||||
Ignore string
|
||||
Package []*govendorPackage
|
||||
}
|
||||
|
||||
// Package represents each package.
|
||||
type govendorPackage struct {
|
||||
// See the vendor spec for definitions.
|
||||
Origin string
|
||||
Path string
|
||||
Revision string
|
||||
Version string
|
||||
}
|
||||
|
||||
func (g *govendorImporter) Name() string {
|
||||
return "govendor"
|
||||
}
|
||||
|
||||
func (g *govendorImporter) HasDepMetadata(dir string) bool {
|
||||
y := filepath.Join(dir, govendorDir, govendorName)
|
||||
if _, err := os.Stat(y); err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (g *govendorImporter) Import(dir string, pr gps.ProjectRoot) (*dep.Manifest, *dep.Lock, error) {
|
||||
err := g.load(dir)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return g.convert(pr)
|
||||
}
|
||||
|
||||
func (g *govendorImporter) load(projectDir string) error {
|
||||
g.logger.Println("Detected govendor configuration file...")
|
||||
v := filepath.Join(projectDir, govendorDir, govendorName)
|
||||
if g.verbose {
|
||||
g.logger.Printf(" Loading %s", v)
|
||||
}
|
||||
vb, err := ioutil.ReadFile(v)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "Unable to read %s", v)
|
||||
}
|
||||
err = json.Unmarshal(vb, &g.file)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "Unable to parse %s", v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *govendorImporter) convert(pr gps.ProjectRoot) (*dep.Manifest, *dep.Lock, error) {
|
||||
g.logger.Println("Converting from vendor.json...")
|
||||
|
||||
manifest := dep.NewManifest()
|
||||
|
||||
if len(g.file.Ignore) > 0 {
|
||||
// Govendor has three use cases here
|
||||
// 1. 'test' - special case for ignoring test files
|
||||
// 2. build tags - any string without a slash (/) in it
|
||||
// 3. path and path prefix - any string with a slash (/) in it.
|
||||
// The path case could be a full path or just a prefix.
|
||||
// Dep doesn't support build tags right now: https://github.com/golang/dep/issues/120
|
||||
for _, i := range strings.Split(g.file.Ignore, " ") {
|
||||
if !strings.Contains(i, "/") {
|
||||
g.logger.Printf(" Govendor was configured to ignore the %s build tag, but that isn't supported by dep yet, and will be ignored. See https://github.com/golang/dep/issues/291.", i)
|
||||
continue
|
||||
}
|
||||
_, err := g.sm.DeduceProjectRoot(i)
|
||||
if err == nil {
|
||||
manifest.Ignored = append(manifest.Ignored, i)
|
||||
} else {
|
||||
g.logger.Printf(" Govendor was configured to ignore the %s package prefix, but that isn't supported by dep yet, and will be ignored.", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lock := &dep.Lock{}
|
||||
for _, pkg := range g.file.Package {
|
||||
// Path must not be empty
|
||||
if pkg.Path == "" {
|
||||
err := errors.New("Invalid govendor configuration, Path is required")
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Obtain ProjectRoot. Required for avoiding sub-package imports.
|
||||
// Use Path instead of Origin since we are trying to group by project here
|
||||
pr, err := g.sm.DeduceProjectRoot(pkg.Path)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
pkg.Path = string(pr)
|
||||
|
||||
// Check if it already existing in locked projects
|
||||
if projectExistsInLock(lock, pr) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Revision must not be empty
|
||||
if pkg.Revision == "" {
|
||||
err := errors.New("Invalid govendor configuration, Revision is required")
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if pkg.Version == "" {
|
||||
// When no version is specified try to get the corresponding version
|
||||
pi := gps.ProjectIdentifier{
|
||||
ProjectRoot: pr,
|
||||
}
|
||||
if pkg.Origin != "" {
|
||||
pi.Source = pkg.Origin
|
||||
}
|
||||
revision := gps.Revision(pkg.Revision)
|
||||
version, err := lookupVersionForLockedProject(pi, nil, revision, g.sm)
|
||||
if err != nil {
|
||||
// Only warn about the problem, it is not enough to warrant failing
|
||||
g.logger.Println(err.Error())
|
||||
} else {
|
||||
pp := getProjectPropertiesFromVersion(version)
|
||||
if pp.Constraint != nil {
|
||||
pkg.Version = pp.Constraint.String()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If there's a version, use it to create project constraint
|
||||
pc, err := g.buildProjectConstraint(pkg)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
manifest.Constraints[pc.Ident.ProjectRoot] = gps.ProjectProperties{Constraint: pc.Constraint}
|
||||
|
||||
lp := g.buildLockedProject(pkg, manifest)
|
||||
lock.P = append(lock.P, lp)
|
||||
}
|
||||
|
||||
return manifest, lock, nil
|
||||
}
|
||||
|
||||
func (g *govendorImporter) buildProjectConstraint(pkg *govendorPackage) (pc gps.ProjectConstraint, err error) {
|
||||
pc.Ident = gps.ProjectIdentifier{ProjectRoot: gps.ProjectRoot(pkg.Path), Source: pkg.Path}
|
||||
|
||||
if pkg.Version != "" {
|
||||
pc.Constraint, err = g.sm.InferConstraint(pkg.Version, pc.Ident)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
pc.Constraint = gps.Any()
|
||||
}
|
||||
|
||||
f := fb.NewConstraintFeedback(pc, fb.DepTypeImported)
|
||||
f.LogFeedback(g.logger)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (g *govendorImporter) buildLockedProject(pkg *govendorPackage, manifest *dep.Manifest) gps.LockedProject {
|
||||
pi := gps.ProjectIdentifier{ProjectRoot: gps.ProjectRoot(pkg.Path)}
|
||||
revision := gps.Revision(pkg.Revision)
|
||||
pp := manifest.Constraints[pi.ProjectRoot]
|
||||
|
||||
version, err := lookupVersionForLockedProject(pi, pp.Constraint, revision, g.sm)
|
||||
if err != nil {
|
||||
// Only warn about the problem, it is not enough to warrant failing
|
||||
g.logger.Println(err.Error())
|
||||
}
|
||||
|
||||
lp := gps.NewLockedProject(pi, version, nil)
|
||||
f := fb.NewLockedProjectFeedback(lp, fb.DepTypeImported)
|
||||
f.LogFeedback(g.logger)
|
||||
|
||||
return lp
|
||||
}
|
|
@ -1,243 +0,0 @@
|
|||
// 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 main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"log"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/golang/dep/internal/gps"
|
||||
"github.com/golang/dep/internal/test"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const testGovendorProjectRoot = "github.com/golang/notexist"
|
||||
|
||||
func TestGovendorConfig_Import(t *testing.T) {
|
||||
h := test.NewHelper(t)
|
||||
defer h.Cleanup()
|
||||
|
||||
ctx := newTestContext(h)
|
||||
sm, err := ctx.SourceManager()
|
||||
h.Must(err)
|
||||
defer sm.Release()
|
||||
|
||||
h.TempDir(filepath.Join("src", testGovendorProjectRoot))
|
||||
h.TempCopy(filepath.Join(testGovendorProjectRoot, govendorDir, govendorName), "govendor/vendor.json")
|
||||
projectRoot := h.Path(testGovendorProjectRoot)
|
||||
|
||||
// Capture stderr so we can verify output
|
||||
verboseOutput := &bytes.Buffer{}
|
||||
ctx.Err = log.New(verboseOutput, "", 0)
|
||||
|
||||
g := newGovendorImporter(ctx.Err, false, sm) // Disable verbose so that we don't print values that change each test run
|
||||
if !g.HasDepMetadata(projectRoot) {
|
||||
t.Fatal("Expected the importer to detect the govendor configuration files")
|
||||
}
|
||||
|
||||
m, l, err := g.Import(projectRoot, testGovendorProjectRoot)
|
||||
h.Must(err)
|
||||
|
||||
if m == nil {
|
||||
t.Fatal("Expected the manifest to be generated")
|
||||
}
|
||||
|
||||
if l == nil {
|
||||
t.Fatal("Expected the lock to be generated")
|
||||
}
|
||||
|
||||
goldenFile := "govendor/golden.txt"
|
||||
got := verboseOutput.String()
|
||||
want := h.GetTestFileString(goldenFile)
|
||||
if want != got {
|
||||
if *test.UpdateGolden {
|
||||
if err := h.WriteTestFile(goldenFile, got); err != nil {
|
||||
t.Fatalf("%+v", errors.Wrapf(err, "Unable to write updated golden file %s", goldenFile))
|
||||
}
|
||||
} else {
|
||||
t.Fatalf("expected %s, got %s", want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGovendorConfig_Convert_Project(t *testing.T) {
|
||||
h := test.NewHelper(t)
|
||||
defer h.Cleanup()
|
||||
|
||||
ctx := newTestContext(h)
|
||||
sm, err := ctx.SourceManager()
|
||||
h.Must(err)
|
||||
defer sm.Release()
|
||||
|
||||
pkg := "github.com/sdboyer/deptest"
|
||||
|
||||
g := newGovendorImporter(ctx.Err, true, sm)
|
||||
g.file = govendorFile{
|
||||
Package: []*govendorPackage{
|
||||
{
|
||||
Path: pkg,
|
||||
Revision: "ff2948a2ac8f538c4ecd55962e919d1e13e74baf",
|
||||
Version: "v1.0.0",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
manifest, lock, err := g.convert(testGovendorProjectRoot)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if manifest == nil {
|
||||
t.Fatal("Expected the manifest to be generated")
|
||||
}
|
||||
|
||||
if lock == nil {
|
||||
t.Fatal("Expected the lock file to be generated")
|
||||
}
|
||||
|
||||
d, ok := manifest.Constraints[gps.ProjectRoot(pkg)]
|
||||
if !ok {
|
||||
t.Fatal("Expected the manifest to have a dependency for 'github.com/sdboyer/deptest' but got none")
|
||||
}
|
||||
|
||||
wantC := "^1.0.0"
|
||||
gotC := d.Constraint.String()
|
||||
if gotC != wantC {
|
||||
t.Fatalf("Expected manifest constraint to be %s, got %s", wantC, gotC)
|
||||
}
|
||||
|
||||
wantP := 1
|
||||
gotP := len(lock.P)
|
||||
if gotP != 1 {
|
||||
t.Fatalf("Expected the lock to contain %d project but got %d", wantP, gotP)
|
||||
}
|
||||
|
||||
p := lock.P[0]
|
||||
gotPr := string(p.Ident().ProjectRoot)
|
||||
if gotPr != pkg {
|
||||
t.Fatalf("Expected the lock to have a project for %s but got '%s'", pkg, gotPr)
|
||||
}
|
||||
|
||||
lv := p.Version()
|
||||
lpv, ok := lv.(gps.PairedVersion)
|
||||
if !ok {
|
||||
t.Fatalf("Expected locked version to be a PairedVersion but got %T", lv)
|
||||
}
|
||||
|
||||
wantRev := "ff2948a2ac8f538c4ecd55962e919d1e13e74baf"
|
||||
gotRev := lpv.Revision().String()
|
||||
if gotRev != wantRev {
|
||||
t.Fatalf("Expected locked revision to be %s, got %s", wantRev, gotRev)
|
||||
}
|
||||
|
||||
wantV := "v1.0.0"
|
||||
gotV := lpv.String()
|
||||
if gotV != wantV {
|
||||
t.Fatalf("Expected locked version to be %s, got %s", wantV, gotV)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGovendorConfig_Convert_TestProject(t *testing.T) {
|
||||
h := test.NewHelper(t)
|
||||
defer h.Cleanup()
|
||||
|
||||
ctx := newTestContext(h)
|
||||
sm, err := ctx.SourceManager()
|
||||
h.Must(err)
|
||||
defer sm.Release()
|
||||
|
||||
pkg := "github.com/sdboyer/deptest"
|
||||
|
||||
g := newGovendorImporter(ctx.Err, true, sm)
|
||||
g.file = govendorFile{
|
||||
Package: []*govendorPackage{
|
||||
{
|
||||
Path: pkg,
|
||||
Revision: "ff2948a2ac8f538c4ecd55962e919d1e13e74baf",
|
||||
Version: "v1.0.0",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
manifest, lock, err := g.convert(testGovendorProjectRoot)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if manifest == nil {
|
||||
t.Fatal("Expected the manifest to be generated")
|
||||
}
|
||||
|
||||
if lock == nil {
|
||||
t.Fatal("Expected the lock file to be generated")
|
||||
}
|
||||
|
||||
_, ok := manifest.Constraints[gps.ProjectRoot(pkg)]
|
||||
if !ok {
|
||||
t.Fatalf("Expected the manifest to have a dependency for %s but got none", pkg)
|
||||
}
|
||||
|
||||
if len(lock.P) != 1 {
|
||||
t.Fatalf("Expected the lock to contain 1 project but got %d", len(lock.P))
|
||||
}
|
||||
p := lock.P[0]
|
||||
if p.Ident().ProjectRoot != gps.ProjectRoot(pkg) {
|
||||
t.Fatalf("Expected the lock to have a project for %s but got '%s'", pkg, p.Ident().ProjectRoot)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGovendorConfig_Convert_Ignore(t *testing.T) {
|
||||
h := test.NewHelper(t)
|
||||
defer h.Cleanup()
|
||||
|
||||
ctx := newTestContext(h)
|
||||
sm, err := ctx.SourceManager()
|
||||
h.Must(err)
|
||||
defer sm.Release()
|
||||
|
||||
pkg := "github.com/sdboyer/deptest"
|
||||
|
||||
g := newGovendorImporter(ctx.Err, true, sm)
|
||||
g.file = govendorFile{
|
||||
Ignore: strings.Join([]string{"test", pkg, "linux_amd64", "github.com/sdboyer/"}, " "),
|
||||
}
|
||||
|
||||
m, _, err := g.convert(testGovendorProjectRoot)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(m.Ignored) != 1 {
|
||||
t.Fatalf("Expected the ignored list to contain 1 project but got %d", len(m.Ignored))
|
||||
}
|
||||
|
||||
p := m.Ignored[0]
|
||||
if p != pkg {
|
||||
t.Fatalf("Expected the ignored list to have an element for %s but got '%s'", pkg, p)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGovendorConfig_Convert_BadInput_EmptyPackagePath(t *testing.T) {
|
||||
h := test.NewHelper(t)
|
||||
defer h.Cleanup()
|
||||
|
||||
ctx := newTestContext(h)
|
||||
sm, err := ctx.SourceManager()
|
||||
h.Must(err)
|
||||
defer sm.Release()
|
||||
|
||||
g := newGovendorImporter(ctx.Err, true, sm)
|
||||
g.file = govendorFile{
|
||||
Package: []*govendorPackage{{Path: ""}},
|
||||
}
|
||||
|
||||
_, _, err = g.convert(testGovendorProjectRoot)
|
||||
if err == nil {
|
||||
t.Fatal("Expected conversion to fail because the package name is empty")
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
{
|
||||
"comment": "",
|
||||
"ignore": "test",
|
||||
"package": [
|
||||
{
|
||||
"checksumSHA1": "c+MCTe4HOVzNBjFpjmPYWFSs06U=",
|
||||
"path": "github.com/sdboyer/deptest",
|
||||
"revision": "e1b5acf7f78e07bc0a8e51121ca0c7e174975d7e",
|
||||
"revisionTime": "2017-07-15T19:32:19Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "cp+gzxyVhoM8sZWLPrsigvpdDuc=",
|
||||
"path": "github.com/sdboyer/deptest/floats",
|
||||
"revision": "e1b5acf7f78e07bc0a8e51121ca0c7e174975d7e",
|
||||
"revisionTime": "2017-07-15T19:32:19Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "5yiF57bPBKmLr6AFDDxfTmWYZJ4=",
|
||||
"path": "github.com/sdboyer/deptest/strings",
|
||||
"revision": "e1b5acf7f78e07bc0a8e51121ca0c7e174975d7e",
|
||||
"revisionTime": "2017-07-15T19:32:19Z"
|
||||
}
|
||||
],
|
||||
"rootPath": "github.com/golang/notexist"
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
{
|
||||
"comment": "",
|
||||
"ignore": "test",
|
||||
"package": [
|
||||
{
|
||||
"checksumSHA1": "q6es/GCwizh2GbLTKWWj8bB6Lc8=",
|
||||
"origin": "github.com/sdboyer/deptest/vendor/github.com/sdboyer/coolint",
|
||||
"path": "github.com/sdboyer/coolint",
|
||||
"revision": "a920fd05db2031ad1ba94f62035fab5def190141",
|
||||
"revisionTime": "2017-07-15T19:22:11Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "c+MCTe4HOVzNBjFpjmPYWFSs06U=",
|
||||
"path": "github.com/sdboyer/deptest",
|
||||
"revision": "a920fd05db2031ad1ba94f62035fab5def190141",
|
||||
"revisionTime": "2017-07-15T19:22:11Z"
|
||||
}
|
||||
],
|
||||
"rootPath": "github.com/golang/notexist"
|
||||
}
|
||||
|
|
@ -1,12 +1,6 @@
|
|||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/carolynvs/go-dep-test"
|
||||
packages = ["."]
|
||||
revision = "6b35265ebb3003525cd3ad6498d7e81a89be106b"
|
||||
version = "0.2.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/sdboyer/deptest"
|
||||
packages = ["."]
|
||||
|
@ -22,6 +16,6 @@
|
|||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "f8e9ea65524b2cebafacf6c9b91441301c6b231bdaf408700a27ae7f9418a273"
|
||||
inputs-digest = "9cc662f2e1b80c8df205d9d667fe2c47825a06961ceae378f44a8290d01dd359"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
|
|
|
@ -1,8 +1,4 @@
|
|||
ignored = ["github.com/sdboyer/dep-test"]
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/carolynvs/go-dep-test"
|
||||
version = "0.2.0"
|
||||
ignored = ["github.com/golang/notexist/samples*","github.com/sdboyer/dep-test*"]
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/sdboyer/deptestdos"
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
"github.com/sdboyer/deptestdos": "5c607206be5decd28e6263ffffdcee067266015e"
|
||||
},
|
||||
"vendor-final": [
|
||||
"github.com/carolynvs/go-dep-test",
|
||||
"github.com/sdboyer/deptest",
|
||||
"github.com/sdboyer/deptestdos"
|
||||
]
|
||||
|
|
|
@ -6,6 +6,7 @@ package base
|
|||
|
||||
import (
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/dep"
|
||||
"github.com/golang/dep/gps"
|
||||
|
@ -16,28 +17,27 @@ import (
|
|||
// Importer provides a common implementation for importing from other
|
||||
// dependency managers.
|
||||
type Importer struct {
|
||||
sm gps.SourceManager
|
||||
|
||||
Logger *log.Logger
|
||||
Verbose bool
|
||||
Manifest *dep.Manifest
|
||||
Lock *dep.Lock
|
||||
SourceManager gps.SourceManager
|
||||
Logger *log.Logger
|
||||
Verbose bool
|
||||
Manifest *dep.Manifest
|
||||
Lock *dep.Lock
|
||||
}
|
||||
|
||||
// NewImporter creates a new Importer for embedding in an importer.
|
||||
func NewImporter(logger *log.Logger, verbose bool, sm gps.SourceManager) *Importer {
|
||||
return &Importer{
|
||||
Logger: logger,
|
||||
Verbose: verbose,
|
||||
Manifest: dep.NewManifest(),
|
||||
Lock: &dep.Lock{},
|
||||
sm: sm,
|
||||
Logger: logger,
|
||||
Verbose: verbose,
|
||||
Manifest: dep.NewManifest(),
|
||||
Lock: &dep.Lock{},
|
||||
SourceManager: sm,
|
||||
}
|
||||
}
|
||||
|
||||
// isTag determines if the specified value is a tag (plain or semver).
|
||||
func (i *Importer) isTag(pi gps.ProjectIdentifier, value string) (bool, gps.Version, error) {
|
||||
versions, err := i.sm.ListVersions(pi)
|
||||
versions, err := i.SourceManager.ListVersions(pi)
|
||||
if err != nil {
|
||||
return false, nil, errors.Wrapf(err, "unable to list versions for %s(%s)", pi.ProjectRoot, pi.Source)
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ func (i *Importer) isTag(pi gps.ProjectIdentifier, value string) (bool, gps.Vers
|
|||
// manifest, then finally the revision.
|
||||
func (i *Importer) lookupVersionForLockedProject(pi gps.ProjectIdentifier, c gps.Constraint, rev gps.Revision) (gps.Version, error) {
|
||||
// Find the version that goes with this revision, if any
|
||||
versions, err := i.sm.ListVersions(pi)
|
||||
versions, err := i.SourceManager.ListVersions(pi)
|
||||
if err != nil {
|
||||
return rev, errors.Wrapf(err, "Unable to lookup the version represented by %s in %s(%s). Falling back to locking the revision only.", rev, pi.ProjectRoot, pi.Source)
|
||||
}
|
||||
|
@ -130,7 +130,7 @@ func (i *Importer) loadPackages(packages []ImportedPackage) ([]importedProject,
|
|||
|
||||
projects := make(map[gps.ProjectRoot]*importedProject, len(packages))
|
||||
for _, pkg := range packages {
|
||||
pr, err := i.sm.DeduceProjectRoot(pkg.Name)
|
||||
pr, err := i.SourceManager.DeduceProjectRoot(pkg.Name)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Cannot determine the project root for %s", pkg.Name)
|
||||
}
|
||||
|
@ -188,6 +188,9 @@ func (i *Importer) ImportPackages(packages []ImportedPackage, defaultConstraintF
|
|||
source = ""
|
||||
} else if isDefault {
|
||||
source = ""
|
||||
} else if strings.Contains(source, "/vendor/") {
|
||||
i.Logger.Printf(" Ignoring imported source %s for %s because vendored sources aren't supported", source, prj.Root)
|
||||
source = ""
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -198,7 +201,7 @@ func (i *Importer) ImportPackages(packages []ImportedPackage, defaultConstraintF
|
|||
},
|
||||
}
|
||||
|
||||
pc.Constraint, err = i.sm.InferConstraint(prj.ConstraintHint, pc.Ident)
|
||||
pc.Constraint, err = i.SourceManager.InferConstraint(prj.ConstraintHint, pc.Ident)
|
||||
if err != nil {
|
||||
pc.Constraint = gps.Any()
|
||||
}
|
||||
|
@ -306,12 +309,12 @@ func (i *Importer) convertToConstraint(v gps.Version) gps.Constraint {
|
|||
func (i *Importer) isDefaultSource(projectRoot gps.ProjectRoot, sourceURL string) (bool, error) {
|
||||
// this condition is mainly for gopkg.in imports,
|
||||
// as some importers specify the repository url as https://gopkg.in/...,
|
||||
// but sm.SourceURLsForPath() returns https://github.com/... urls for gopkg.in
|
||||
// but SourceManager.SourceURLsForPath() returns https://github.com/... urls for gopkg.in
|
||||
if sourceURL == "https://"+string(projectRoot) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
sourceURLs, err := i.sm.SourceURLsForPath(string(projectRoot))
|
||||
sourceURLs, err := i.SourceManager.SourceURLsForPath(string(projectRoot))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
|
|
@ -382,7 +382,7 @@ func TestBaseImporter_ImportProjects(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
"ignoring default source": {
|
||||
"ignore default source": {
|
||||
importertest.TestCase{
|
||||
WantConstraint: "*",
|
||||
WantSourceRepo: "",
|
||||
|
@ -394,6 +394,19 @@ func TestBaseImporter_ImportProjects(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
"ignore vendored source": {
|
||||
importertest.TestCase{
|
||||
WantConstraint: "*",
|
||||
WantSourceRepo: "",
|
||||
WantWarning: "vendored sources aren't supported",
|
||||
},
|
||||
[]ImportedPackage{
|
||||
{
|
||||
Name: importertest.Project,
|
||||
Source: "example.com/vendor/" + importertest.Project,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testcases {
|
||||
|
|
|
@ -0,0 +1,150 @@
|
|||
// 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 govendor
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/dep"
|
||||
"github.com/golang/dep/gps"
|
||||
"github.com/golang/dep/internal/importers/base"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const govendorDir = "vendor"
|
||||
const govendorName = "vendor.json"
|
||||
|
||||
// Importer imports govendor configuration into the dep configuration format.
|
||||
type Importer struct {
|
||||
*base.Importer
|
||||
|
||||
file govendorFile
|
||||
}
|
||||
|
||||
// NewImporter for govendor.
|
||||
func NewImporter(logger *log.Logger, verbose bool, sm gps.SourceManager) *Importer {
|
||||
return &Importer{Importer: base.NewImporter(logger, verbose, sm)}
|
||||
}
|
||||
|
||||
// File is the structure of the vendor file.
|
||||
type govendorFile struct {
|
||||
RootPath string // Import path of vendor folder
|
||||
Ignore string
|
||||
Package []*govendorPackage
|
||||
}
|
||||
|
||||
// Package represents each package.
|
||||
type govendorPackage struct {
|
||||
// See the vendor spec for definitions.
|
||||
Origin string
|
||||
Path string
|
||||
Revision string
|
||||
Version string
|
||||
}
|
||||
|
||||
// Name of the importer.
|
||||
func (g *Importer) Name() string {
|
||||
return "govendor"
|
||||
}
|
||||
|
||||
// HasDepMetadata checks if a directory contains config that the importer can handle.
|
||||
func (g *Importer) HasDepMetadata(dir string) bool {
|
||||
y := filepath.Join(dir, govendorDir, govendorName)
|
||||
if _, err := os.Stat(y); err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Import the config found in the directory.
|
||||
func (g *Importer) Import(dir string, pr gps.ProjectRoot) (*dep.Manifest, *dep.Lock, error) {
|
||||
err := g.load(dir)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return g.convert(pr)
|
||||
}
|
||||
|
||||
func (g *Importer) load(projectDir string) error {
|
||||
g.Logger.Println("Detected govendor configuration file...")
|
||||
v := filepath.Join(projectDir, govendorDir, govendorName)
|
||||
if g.Verbose {
|
||||
g.Logger.Printf(" Loading %s", v)
|
||||
}
|
||||
vb, err := ioutil.ReadFile(v)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unable to read %s", v)
|
||||
}
|
||||
err = json.Unmarshal(vb, &g.file)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unable to parse %s", v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Importer) convert(pr gps.ProjectRoot) (*dep.Manifest, *dep.Lock, error) {
|
||||
g.Logger.Println("Converting from vendor.json...")
|
||||
|
||||
packages := make([]base.ImportedPackage, 0, len(g.file.Package))
|
||||
for _, pkg := range g.file.Package {
|
||||
// Path must not be empty
|
||||
if pkg.Path == "" {
|
||||
err := errors.New("invalid govendor configuration, Path is required")
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// There are valid govendor configs in the wild that don't have a revision set
|
||||
// so we are not requiring it to be set during import
|
||||
|
||||
ip := base.ImportedPackage{
|
||||
Name: pkg.Path,
|
||||
Source: pkg.Origin,
|
||||
LockHint: pkg.Revision,
|
||||
}
|
||||
packages = append(packages, ip)
|
||||
}
|
||||
|
||||
err := g.ImportPackages(packages, true)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if len(g.file.Ignore) > 0 {
|
||||
// Govendor has three use cases here
|
||||
// 1. 'test' - special case for ignoring test files
|
||||
// 2. build tags - any string without a slash (/) in it
|
||||
// 3. path and path prefix - any string with a slash (/) in it.
|
||||
// The path case could be a full path or just a prefix.
|
||||
// Dep doesn't support build tags right now: https://github.com/golang/dep/issues/120
|
||||
for _, i := range strings.Split(g.file.Ignore, " ") {
|
||||
if !strings.Contains(i, "/") {
|
||||
g.Logger.Printf(" Govendor was configured to ignore the %s build tag, but that isn't supported by dep yet, and will be ignored. See https://github.com/golang/dep/issues/291.", i)
|
||||
continue
|
||||
}
|
||||
|
||||
var ignorePattern string
|
||||
_, err := g.SourceManager.DeduceProjectRoot(i)
|
||||
if err == nil { // external package
|
||||
ignorePattern = i
|
||||
} else { // relative package path in the current project
|
||||
ignorePattern = path.Join(string(pr), i)
|
||||
}
|
||||
|
||||
// Convert to a a wildcard ignore
|
||||
ignorePattern = strings.TrimRight(ignorePattern, "/")
|
||||
ignorePattern += "*"
|
||||
|
||||
g.Manifest.Ignored = append(g.Manifest.Ignored, ignorePattern)
|
||||
}
|
||||
}
|
||||
|
||||
return g.Manifest, g.Lock, nil
|
||||
}
|
|
@ -0,0 +1,155 @@
|
|||
// 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 govendor
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"log"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/golang/dep"
|
||||
"github.com/golang/dep/gps"
|
||||
"github.com/golang/dep/internal/importers/importertest"
|
||||
"github.com/golang/dep/internal/test"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const testGovendorProjectRoot = "github.com/golang/notexist"
|
||||
|
||||
func TestGovendorConfig_Import(t *testing.T) {
|
||||
h := test.NewHelper(t)
|
||||
defer h.Cleanup()
|
||||
|
||||
ctx := importertest.NewTestContext(h)
|
||||
sm, err := ctx.SourceManager()
|
||||
h.Must(err)
|
||||
defer sm.Release()
|
||||
|
||||
h.TempDir(filepath.Join("src", testGovendorProjectRoot))
|
||||
h.TempCopy(filepath.Join(testGovendorProjectRoot, govendorDir, govendorName), "vendor.json")
|
||||
projectRoot := h.Path(testGovendorProjectRoot)
|
||||
|
||||
// Capture stderr so we can verify output
|
||||
verboseOutput := &bytes.Buffer{}
|
||||
ctx.Err = log.New(verboseOutput, "", 0)
|
||||
|
||||
g := NewImporter(ctx.Err, false, sm) // Disable verbose so that we don't print values that change each test run
|
||||
if !g.HasDepMetadata(projectRoot) {
|
||||
t.Fatal("Expected the importer to detect the govendor configuration files")
|
||||
}
|
||||
|
||||
m, l, err := g.Import(projectRoot, testGovendorProjectRoot)
|
||||
h.Must(err)
|
||||
|
||||
if m == nil {
|
||||
t.Fatal("Expected the manifest to be generated")
|
||||
}
|
||||
|
||||
if l == nil {
|
||||
t.Fatal("Expected the lock to be generated")
|
||||
}
|
||||
|
||||
goldenFile := "golden.txt"
|
||||
got := verboseOutput.String()
|
||||
want := h.GetTestFileString(goldenFile)
|
||||
if want != got {
|
||||
if *test.UpdateGolden {
|
||||
if err := h.WriteTestFile(goldenFile, got); err != nil {
|
||||
t.Fatalf("%+v", errors.Wrapf(err, "Unable to write updated golden file %s", goldenFile))
|
||||
}
|
||||
} else {
|
||||
t.Fatalf("expected %s, got %s", want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGovendorConfig_Convert(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
file govendorFile
|
||||
importertest.TestCase
|
||||
}{
|
||||
"project": {
|
||||
govendorFile{
|
||||
Package: []*govendorPackage{
|
||||
{
|
||||
Path: importertest.Project,
|
||||
Origin: importertest.ProjectSrc,
|
||||
Revision: importertest.V1Rev,
|
||||
},
|
||||
},
|
||||
},
|
||||
importertest.TestCase{
|
||||
WantSourceRepo: importertest.ProjectSrc,
|
||||
WantConstraint: importertest.V1Constraint,
|
||||
WantRevision: importertest.V1Rev,
|
||||
WantVersion: importertest.V1Tag,
|
||||
},
|
||||
},
|
||||
"skipped build tags": {
|
||||
govendorFile{
|
||||
Ignore: "test linux_amd64",
|
||||
},
|
||||
importertest.TestCase{
|
||||
WantIgnored: nil,
|
||||
},
|
||||
},
|
||||
"ignored external package": {
|
||||
govendorFile{
|
||||
Ignore: "github.com/sdboyer/deptest k8s.io/apimachinery",
|
||||
},
|
||||
importertest.TestCase{
|
||||
WantIgnored: []string{"github.com/sdboyer/deptest*", "k8s.io/apimachinery*"},
|
||||
},
|
||||
},
|
||||
"ignored internal package": {
|
||||
govendorFile{
|
||||
Ignore: "samples/ foo/bar",
|
||||
},
|
||||
importertest.TestCase{
|
||||
WantIgnored: []string{importertest.RootProject + "/samples*", importertest.RootProject + "/foo/bar*"},
|
||||
},
|
||||
},
|
||||
"missing package path": {
|
||||
govendorFile{
|
||||
Package: []*govendorPackage{
|
||||
{
|
||||
Revision: importertest.V2PatchRev,
|
||||
},
|
||||
},
|
||||
},
|
||||
importertest.TestCase{
|
||||
WantConvertErr: true,
|
||||
},
|
||||
},
|
||||
"missing package revision doesn't cause an error": {
|
||||
govendorFile{
|
||||
Package: []*govendorPackage{
|
||||
{
|
||||
Path: importertest.Project,
|
||||
},
|
||||
},
|
||||
},
|
||||
importertest.TestCase{
|
||||
WantConstraint: "*",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for name, testCase := range testCases {
|
||||
name := name
|
||||
testCase := testCase
|
||||
t.Run(name, func(t *testing.T) {
|
||||
err := testCase.Execute(t, func(logger *log.Logger, sm gps.SourceManager) (*dep.Manifest, *dep.Lock, error) {
|
||||
g := NewImporter(logger, true, sm)
|
||||
g.file = testCase.file
|
||||
return g.convert(importertest.RootProject)
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("%#v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
Detected govendor configuration file...
|
||||
Converting from vendor.json...
|
||||
Govendor was configured to ignore the test build tag, but that isn't supported by dep yet, and will be ignored. See https://github.com/golang/dep/issues/291.
|
||||
Using ^0.8.1 as initial constraint for imported dep github.com/sdboyer/deptest
|
||||
Trying v0.8.1 (3f4c3be) as initial lock for imported dep github.com/sdboyer/deptest
|
||||
Using ^2.0.0 as initial constraint for imported dep github.com/sdboyer/deptestdos
|
||||
Trying v2.0.0 (5c60720) as initial lock for imported dep github.com/sdboyer/deptestdos
|
||||
Govendor was configured to ignore the test build tag, but that isn't supported by dep yet, and will be ignored. See https://github.com/golang/dep/issues/291.
|
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/golang/dep/internal/importers/glide"
|
||||
"github.com/golang/dep/internal/importers/godep"
|
||||
"github.com/golang/dep/internal/importers/govend"
|
||||
"github.com/golang/dep/internal/importers/govendor"
|
||||
"github.com/golang/dep/internal/importers/gvt"
|
||||
"github.com/golang/dep/internal/importers/vndr"
|
||||
)
|
||||
|
@ -37,5 +38,6 @@ func BuildAll(logger *log.Logger, verbose bool, sm gps.SourceManager) []Importer
|
|||
vndr.NewImporter(logger, verbose, sm),
|
||||
govend.NewImporter(logger, verbose, sm),
|
||||
gvt.NewImporter(logger, verbose, sm),
|
||||
govendor.NewImporter(logger, verbose, sm),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -79,7 +79,7 @@ func (tc TestCase) validate(manifest *dep.Manifest, lock *dep.Lock, convertErr e
|
|||
}
|
||||
|
||||
if !equalSlice(manifest.Ignored, tc.WantIgnored) {
|
||||
return errors.Errorf("unexpected set of ignored projects: \n\t(GOT) %v \n\t(WNT) %v",
|
||||
return errors.Errorf("unexpected set of ignored projects: \n\t(GOT) %#v \n\t(WNT) %#v",
|
||||
manifest.Ignored, tc.WantIgnored)
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче