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
Add support importing from govend #1040
Merged
carolynvs
merged 4 commits into
golang:master
from
RaviTezu:add-support-for-importing-from-govend
Aug 24, 2017
+436
−2
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
5ddabee
Support importing from govend
RaviTezu c21b520
Adding a newline at the end of the file, to fix the failing tests
RaviTezu 25e1d69
use new manifest constructor and making requested changes
RaviTezu b28420b
Log a warning, if we cannont infer a constrain; includes minor fixes
RaviTezu File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I realize this isn't in the other importers yet, but we'd like to have import keep going even when we can't guess a constraint from the revision. See #907.
So when
buildProjectConstraint
returns an error, let's log a warning like "Unable to infer a constraint for REVISION", and then skip adding the constraint to the manifest.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@carolynvs Done. Please take a look.