This repository has been archived by the owner on Sep 9, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support importing from govend (#1040)
- Loading branch information
Showing
12 changed files
with
436 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
// 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 main | ||
|
||
import ( | ||
"io/ioutil" | ||
"log" | ||
"os" | ||
"path/filepath" | ||
|
||
"github.com/go-yaml/yaml" | ||
"github.com/golang/dep" | ||
fb "github.com/golang/dep/internal/feedback" | ||
"github.com/golang/dep/internal/gps" | ||
"github.com/pkg/errors" | ||
) | ||
|
||
// ToDo: govend supports json and xml formats as well and we will add support for other formats in next PR - @RaviTezu | ||
// govend don't have a separate lock file. | ||
const govendYAMLName = "vendor.yml" | ||
|
||
// govendImporter imports govend configuration in to the dep configuration format. | ||
type govendImporter struct { | ||
yaml govendYAML | ||
|
||
logger *log.Logger | ||
verbose bool | ||
sm gps.SourceManager | ||
} | ||
|
||
func newGovendImporter(logger *log.Logger, verbose bool, sm gps.SourceManager) *govendImporter { | ||
return &govendImporter{ | ||
logger: logger, | ||
verbose: verbose, | ||
sm: sm, | ||
} | ||
} | ||
|
||
type govendYAML struct { | ||
Imports []govendPackage `yaml:"vendors"` | ||
} | ||
|
||
type govendPackage struct { | ||
Path string `yaml:"path"` | ||
Revision string `yaml:"rev"` | ||
} | ||
|
||
func (g *govendImporter) Name() string { | ||
return "govend" | ||
} | ||
|
||
func (g *govendImporter) HasDepMetadata(dir string) bool { | ||
y := filepath.Join(dir, govendYAMLName) | ||
if _, err := os.Stat(y); err != nil { | ||
return false | ||
} | ||
|
||
return true | ||
} | ||
|
||
func (g *govendImporter) 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) | ||
} | ||
|
||
// load the govend configuration files. | ||
func (g *govendImporter) load(projectDir string) error { | ||
g.logger.Println("Detected govend configuration files...") | ||
y := filepath.Join(projectDir, govendYAMLName) | ||
if g.verbose { | ||
g.logger.Printf(" Loading %s", y) | ||
} | ||
yb, err := ioutil.ReadFile(y) | ||
if err != nil { | ||
return errors.Wrapf(err, "Unable to read %s", y) | ||
} | ||
err = yaml.Unmarshal(yb, &g.yaml) | ||
if err != nil { | ||
return errors.Wrapf(err, "Unable to parse %s", y) | ||
} | ||
return nil | ||
} | ||
|
||
// convert the govend configuration files into dep configuration files. | ||
func (g *govendImporter) convert(pr gps.ProjectRoot) (*dep.Manifest, *dep.Lock, error) { | ||
g.logger.Println("Converting from vendor.yaml...") | ||
|
||
manifest := dep.NewManifest() | ||
lock := &dep.Lock{} | ||
|
||
for _, pkg := range g.yaml.Imports { | ||
// Path must not be empty | ||
if pkg.Path == "" || pkg.Revision == "" { | ||
return nil, nil, errors.New("Invalid govend configuration, Path and Rev are required") | ||
} | ||
|
||
p, err := g.sm.DeduceProjectRoot(pkg.Path) | ||
if err != nil { | ||
return nil, nil, err | ||
} | ||
pkg.Path = string(p) | ||
|
||
// Check if the current project is already existing in locked projects. | ||
if projectExistsInLock(lock, p) { | ||
continue | ||
} | ||
|
||
pi := gps.ProjectIdentifier{ | ||
ProjectRoot: gps.ProjectRoot(pkg.Path), | ||
} | ||
revision := gps.Revision(pkg.Revision) | ||
|
||
version, err := lookupVersionForLockedProject(pi, nil, revision, g.sm) | ||
if err != nil { | ||
g.logger.Println(err.Error()) | ||
} else { | ||
pp := getProjectPropertiesFromVersion(version) | ||
if pp.Constraint != nil { | ||
pc, err := g.buildProjectConstraint(pkg, pp.Constraint.String()) | ||
if err != nil { | ||
g.logger.Printf("Unable to infer a constraint for revision %s for package %s: %s", pkg.Revision, pkg.Path, err.Error()) | ||
continue | ||
} | ||
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 *govendImporter) buildProjectConstraint(pkg govendPackage, constraint string) (pc gps.ProjectConstraint, err error) { | ||
pc.Ident = gps.ProjectIdentifier{ProjectRoot: gps.ProjectRoot(pkg.Path)} | ||
pc.Constraint, err = g.sm.InferConstraint(constraint, pc.Ident) | ||
if err != nil { | ||
return | ||
} | ||
|
||
f := fb.NewConstraintFeedback(pc, fb.DepTypeImported) | ||
f.LogFeedback(g.logger) | ||
|
||
return | ||
|
||
} | ||
|
||
func (g *govendImporter) buildLockedProject(pkg govendPackage, manifest *dep.Manifest) gps.LockedProject { | ||
p := gps.ProjectIdentifier{ProjectRoot: gps.ProjectRoot(pkg.Path)} | ||
revision := gps.Revision(pkg.Revision) | ||
pp := manifest.Constraints[p.ProjectRoot] | ||
|
||
version, err := lookupVersionForLockedProject(p, pp.Constraint, revision, g.sm) | ||
if err != nil { | ||
g.logger.Println(err.Error()) | ||
} | ||
|
||
lp := gps.NewLockedProject(p, version, nil) | ||
f := fb.NewLockedProjectFeedback(lp, fb.DepTypeImported) | ||
f.LogFeedback(g.logger) | ||
|
||
return lp | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,192 @@ | ||
// 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 main | ||
|
||
import ( | ||
"bytes" | ||
"log" | ||
"path/filepath" | ||
"testing" | ||
|
||
"github.com/golang/dep/internal/gps" | ||
"github.com/golang/dep/internal/test" | ||
"github.com/pkg/errors" | ||
) | ||
|
||
func TestGovendConfig_Convert(t *testing.T) { | ||
testCases := map[string]struct { | ||
*convertTestCase | ||
yaml govendYAML | ||
}{ | ||
"project": { | ||
yaml: govendYAML{ | ||
Imports: []govendPackage{ | ||
{ | ||
Path: "github.com/sdboyer/deptest", | ||
Revision: "ff2948a2ac8f538c4ecd55962e919d1e13e74baf", | ||
}, | ||
}, | ||
}, | ||
convertTestCase: &convertTestCase{ | ||
projectRoot: gps.ProjectRoot("github.com/sdboyer/deptest"), | ||
wantConstraint: "^1.0.0", | ||
wantLockCount: 1, | ||
wantRevision: gps.Revision("ff2948a2ac8f538c4ecd55962e919d1e13e74baf"), | ||
wantVersion: "v1.0.0", | ||
}, | ||
}, | ||
"bad input - empty package name": { | ||
yaml: govendYAML{ | ||
Imports: []govendPackage{ | ||
{ | ||
Path: "", | ||
}, | ||
}, | ||
}, | ||
convertTestCase: &convertTestCase{ | ||
wantConvertErr: true, | ||
}, | ||
}, | ||
|
||
"bad input - empty revision": { | ||
yaml: govendYAML{ | ||
Imports: []govendPackage{ | ||
{ | ||
Path: "github.com/sdboyer/deptest", | ||
}, | ||
}, | ||
}, | ||
convertTestCase: &convertTestCase{ | ||
wantConvertErr: true, | ||
}, | ||
}, | ||
} | ||
|
||
h := test.NewHelper(t) | ||
defer h.Cleanup() | ||
|
||
ctx := newTestContext(h) | ||
sm, err := ctx.SourceManager() | ||
h.Must(err) | ||
defer sm.Release() | ||
|
||
for name, testCase := range testCases { | ||
t.Run(name, func(t *testing.T) { | ||
g := newGovendImporter(discardLogger, true, sm) | ||
g.yaml = testCase.yaml | ||
|
||
manifest, lock, convertErr := g.convert(testCase.projectRoot) | ||
err = validateConvertTestCase(testCase.convertTestCase, manifest, lock, convertErr) | ||
if err != nil { | ||
t.Fatalf("%#v", err) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestGovendConfig_Import(t *testing.T) { | ||
h := test.NewHelper(t) | ||
defer h.Cleanup() | ||
|
||
cacheDir := "gps-repocache" | ||
h.TempDir(cacheDir) | ||
h.TempDir("src") | ||
h.TempDir(filepath.Join("src", testProjectRoot)) | ||
h.TempCopy(filepath.Join(testProjectRoot, govendYAMLName), "govend/vendor.yml") | ||
|
||
projectRoot := h.Path(testProjectRoot) | ||
sm, err := gps.NewSourceManager(h.Path(cacheDir)) | ||
h.Must(err) | ||
defer sm.Release() | ||
|
||
// Capture stderr so we can verify the import output | ||
verboseOutput := &bytes.Buffer{} | ||
logger := log.New(verboseOutput, "", 0) | ||
|
||
// Disable verbose so that we don't print values that change each test run | ||
g := newGovendImporter(logger, false, sm) | ||
if !g.HasDepMetadata(projectRoot) { | ||
t.Fatal("Expected the importer to detect govend configuration file") | ||
} | ||
|
||
m, l, err := g.Import(projectRoot, testProjectRoot) | ||
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") | ||
} | ||
|
||
govendImportOutputFile := "govend/expected_govend_import_output.txt" | ||
got := verboseOutput.String() | ||
want := h.GetTestFileString(govendImportOutputFile) | ||
if want != got { | ||
if *test.UpdateGolden { | ||
if err := h.WriteTestFile(govendImportOutputFile, got); err != nil { | ||
t.Fatalf("%+v", errors.Wrapf(err, "Unable to write updated golden file %s", govendImportOutputFile)) | ||
} | ||
} else { | ||
t.Fatalf("want %s, got %s", want, got) | ||
} | ||
} | ||
|
||
} | ||
|
||
func TestGovendConfig_YAMLLoad(t *testing.T) { | ||
// This is same as cmd/testdata/govend/vendor.yml | ||
wantYAML := govendYAML{ | ||
Imports: []govendPackage{ | ||
{ | ||
Path: "github.com/sdboyer/deptest", | ||
Revision: "3f4c3bea144e112a69bbe5d8d01c1b09a544253f", | ||
}, | ||
{ | ||
Path: "github.com/sdboyer/deptestdos", | ||
Revision: "5c607206be5decd28e6263ffffdcee067266015e", | ||
}, | ||
}, | ||
} | ||
h := test.NewHelper(t) | ||
defer h.Cleanup() | ||
|
||
ctx := newTestContext(h) | ||
h.TempCopy(filepath.Join(testProjectRoot, govendYAMLName), "govend/vendor.yml") | ||
|
||
projectRoot := h.Path(testProjectRoot) | ||
|
||
g := newGovendImporter(ctx.Err, true, nil) | ||
err := g.load(projectRoot) | ||
if err != nil { | ||
t.Fatalf("Error while loading %v", err) | ||
} | ||
|
||
if !equalGovendImports(g.yaml.Imports, wantYAML.Imports) { | ||
t.Fatalf("Expected import to be equal. \n\t(GOT): %v\n\t(WNT): %v", g.yaml.Imports, wantYAML.Imports) | ||
} | ||
} | ||
|
||
func equalGovendImports(a, b []govendPackage) bool { | ||
if a == nil && b == nil { | ||
return true | ||
} | ||
|
||
if a == nil || b == nil { | ||
return false | ||
} | ||
|
||
if len(a) != len(b) { | ||
return false | ||
} | ||
|
||
for i := range a { | ||
if a[i] != b[i] { | ||
return false | ||
} | ||
} | ||
return true | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
Detected govend configuration files... | ||
Converting from vendor.yaml... | ||
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
vendors: | ||
- path: github.com/sdboyer/deptest | ||
rev: 3f4c3bea144e112a69bbe5d8d01c1b09a544253f | ||
- path: github.com/sdboyer/deptestdos | ||
rev: 5c607206be5decd28e6263ffffdcee067266015e | ||
|
Oops, something went wrong.