Skip to content
This repository was archived by the owner on Sep 9, 2020. It is now read-only.

Commit 7c04369

Browse files
RaviTezucarolynvs
authored andcommitted
Add support importing from govend (#1040)
1 parent 1f6d6bb commit 7c04369

File tree

12 files changed

+436
-2
lines changed

12 files changed

+436
-2
lines changed

cmd/dep/govend_importer.go

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
// Copyright 2017 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package main
6+
7+
import (
8+
"io/ioutil"
9+
"log"
10+
"os"
11+
"path/filepath"
12+
13+
"github.com/go-yaml/yaml"
14+
"github.com/golang/dep"
15+
fb "github.com/golang/dep/internal/feedback"
16+
"github.com/golang/dep/internal/gps"
17+
"github.com/pkg/errors"
18+
)
19+
20+
// ToDo: govend supports json and xml formats as well and we will add support for other formats in next PR - @RaviTezu
21+
// govend don't have a separate lock file.
22+
const govendYAMLName = "vendor.yml"
23+
24+
// govendImporter imports govend configuration in to the dep configuration format.
25+
type govendImporter struct {
26+
yaml govendYAML
27+
28+
logger *log.Logger
29+
verbose bool
30+
sm gps.SourceManager
31+
}
32+
33+
func newGovendImporter(logger *log.Logger, verbose bool, sm gps.SourceManager) *govendImporter {
34+
return &govendImporter{
35+
logger: logger,
36+
verbose: verbose,
37+
sm: sm,
38+
}
39+
}
40+
41+
type govendYAML struct {
42+
Imports []govendPackage `yaml:"vendors"`
43+
}
44+
45+
type govendPackage struct {
46+
Path string `yaml:"path"`
47+
Revision string `yaml:"rev"`
48+
}
49+
50+
func (g *govendImporter) Name() string {
51+
return "govend"
52+
}
53+
54+
func (g *govendImporter) HasDepMetadata(dir string) bool {
55+
y := filepath.Join(dir, govendYAMLName)
56+
if _, err := os.Stat(y); err != nil {
57+
return false
58+
}
59+
60+
return true
61+
}
62+
63+
func (g *govendImporter) Import(dir string, pr gps.ProjectRoot) (*dep.Manifest, *dep.Lock, error) {
64+
err := g.load(dir)
65+
if err != nil {
66+
return nil, nil, err
67+
}
68+
69+
return g.convert(pr)
70+
}
71+
72+
// load the govend configuration files.
73+
func (g *govendImporter) load(projectDir string) error {
74+
g.logger.Println("Detected govend configuration files...")
75+
y := filepath.Join(projectDir, govendYAMLName)
76+
if g.verbose {
77+
g.logger.Printf(" Loading %s", y)
78+
}
79+
yb, err := ioutil.ReadFile(y)
80+
if err != nil {
81+
return errors.Wrapf(err, "Unable to read %s", y)
82+
}
83+
err = yaml.Unmarshal(yb, &g.yaml)
84+
if err != nil {
85+
return errors.Wrapf(err, "Unable to parse %s", y)
86+
}
87+
return nil
88+
}
89+
90+
// convert the govend configuration files into dep configuration files.
91+
func (g *govendImporter) convert(pr gps.ProjectRoot) (*dep.Manifest, *dep.Lock, error) {
92+
g.logger.Println("Converting from vendor.yaml...")
93+
94+
manifest := dep.NewManifest()
95+
lock := &dep.Lock{}
96+
97+
for _, pkg := range g.yaml.Imports {
98+
// Path must not be empty
99+
if pkg.Path == "" || pkg.Revision == "" {
100+
return nil, nil, errors.New("Invalid govend configuration, Path and Rev are required")
101+
}
102+
103+
p, err := g.sm.DeduceProjectRoot(pkg.Path)
104+
if err != nil {
105+
return nil, nil, err
106+
}
107+
pkg.Path = string(p)
108+
109+
// Check if the current project is already existing in locked projects.
110+
if projectExistsInLock(lock, p) {
111+
continue
112+
}
113+
114+
pi := gps.ProjectIdentifier{
115+
ProjectRoot: gps.ProjectRoot(pkg.Path),
116+
}
117+
revision := gps.Revision(pkg.Revision)
118+
119+
version, err := lookupVersionForLockedProject(pi, nil, revision, g.sm)
120+
if err != nil {
121+
g.logger.Println(err.Error())
122+
} else {
123+
pp := getProjectPropertiesFromVersion(version)
124+
if pp.Constraint != nil {
125+
pc, err := g.buildProjectConstraint(pkg, pp.Constraint.String())
126+
if err != nil {
127+
g.logger.Printf("Unable to infer a constraint for revision %s for package %s: %s", pkg.Revision, pkg.Path, err.Error())
128+
continue
129+
}
130+
manifest.Constraints[pc.Ident.ProjectRoot] = gps.ProjectProperties{Constraint: pc.Constraint}
131+
}
132+
}
133+
134+
lp := g.buildLockedProject(pkg, manifest)
135+
lock.P = append(lock.P, lp)
136+
}
137+
138+
return manifest, lock, nil
139+
}
140+
141+
func (g *govendImporter) buildProjectConstraint(pkg govendPackage, constraint string) (pc gps.ProjectConstraint, err error) {
142+
pc.Ident = gps.ProjectIdentifier{ProjectRoot: gps.ProjectRoot(pkg.Path)}
143+
pc.Constraint, err = g.sm.InferConstraint(constraint, pc.Ident)
144+
if err != nil {
145+
return
146+
}
147+
148+
f := fb.NewConstraintFeedback(pc, fb.DepTypeImported)
149+
f.LogFeedback(g.logger)
150+
151+
return
152+
153+
}
154+
155+
func (g *govendImporter) buildLockedProject(pkg govendPackage, manifest *dep.Manifest) gps.LockedProject {
156+
p := gps.ProjectIdentifier{ProjectRoot: gps.ProjectRoot(pkg.Path)}
157+
revision := gps.Revision(pkg.Revision)
158+
pp := manifest.Constraints[p.ProjectRoot]
159+
160+
version, err := lookupVersionForLockedProject(p, pp.Constraint, revision, g.sm)
161+
if err != nil {
162+
g.logger.Println(err.Error())
163+
}
164+
165+
lp := gps.NewLockedProject(p, version, nil)
166+
f := fb.NewLockedProjectFeedback(lp, fb.DepTypeImported)
167+
f.LogFeedback(g.logger)
168+
169+
return lp
170+
}

cmd/dep/govend_importer_test.go

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
// Copyright 2017 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package main
6+
7+
import (
8+
"bytes"
9+
"log"
10+
"path/filepath"
11+
"testing"
12+
13+
"github.com/golang/dep/internal/gps"
14+
"github.com/golang/dep/internal/test"
15+
"github.com/pkg/errors"
16+
)
17+
18+
func TestGovendConfig_Convert(t *testing.T) {
19+
testCases := map[string]struct {
20+
*convertTestCase
21+
yaml govendYAML
22+
}{
23+
"project": {
24+
yaml: govendYAML{
25+
Imports: []govendPackage{
26+
{
27+
Path: "github.com/sdboyer/deptest",
28+
Revision: "ff2948a2ac8f538c4ecd55962e919d1e13e74baf",
29+
},
30+
},
31+
},
32+
convertTestCase: &convertTestCase{
33+
projectRoot: gps.ProjectRoot("github.com/sdboyer/deptest"),
34+
wantConstraint: "^1.0.0",
35+
wantLockCount: 1,
36+
wantRevision: gps.Revision("ff2948a2ac8f538c4ecd55962e919d1e13e74baf"),
37+
wantVersion: "v1.0.0",
38+
},
39+
},
40+
"bad input - empty package name": {
41+
yaml: govendYAML{
42+
Imports: []govendPackage{
43+
{
44+
Path: "",
45+
},
46+
},
47+
},
48+
convertTestCase: &convertTestCase{
49+
wantConvertErr: true,
50+
},
51+
},
52+
53+
"bad input - empty revision": {
54+
yaml: govendYAML{
55+
Imports: []govendPackage{
56+
{
57+
Path: "github.com/sdboyer/deptest",
58+
},
59+
},
60+
},
61+
convertTestCase: &convertTestCase{
62+
wantConvertErr: true,
63+
},
64+
},
65+
}
66+
67+
h := test.NewHelper(t)
68+
defer h.Cleanup()
69+
70+
ctx := newTestContext(h)
71+
sm, err := ctx.SourceManager()
72+
h.Must(err)
73+
defer sm.Release()
74+
75+
for name, testCase := range testCases {
76+
t.Run(name, func(t *testing.T) {
77+
g := newGovendImporter(discardLogger, true, sm)
78+
g.yaml = testCase.yaml
79+
80+
manifest, lock, convertErr := g.convert(testCase.projectRoot)
81+
err = validateConvertTestCase(testCase.convertTestCase, manifest, lock, convertErr)
82+
if err != nil {
83+
t.Fatalf("%#v", err)
84+
}
85+
})
86+
}
87+
}
88+
89+
func TestGovendConfig_Import(t *testing.T) {
90+
h := test.NewHelper(t)
91+
defer h.Cleanup()
92+
93+
cacheDir := "gps-repocache"
94+
h.TempDir(cacheDir)
95+
h.TempDir("src")
96+
h.TempDir(filepath.Join("src", testProjectRoot))
97+
h.TempCopy(filepath.Join(testProjectRoot, govendYAMLName), "govend/vendor.yml")
98+
99+
projectRoot := h.Path(testProjectRoot)
100+
sm, err := gps.NewSourceManager(h.Path(cacheDir))
101+
h.Must(err)
102+
defer sm.Release()
103+
104+
// Capture stderr so we can verify the import output
105+
verboseOutput := &bytes.Buffer{}
106+
logger := log.New(verboseOutput, "", 0)
107+
108+
// Disable verbose so that we don't print values that change each test run
109+
g := newGovendImporter(logger, false, sm)
110+
if !g.HasDepMetadata(projectRoot) {
111+
t.Fatal("Expected the importer to detect govend configuration file")
112+
}
113+
114+
m, l, err := g.Import(projectRoot, testProjectRoot)
115+
h.Must(err)
116+
117+
if m == nil {
118+
t.Fatal("Expected the manifest to be generated")
119+
}
120+
121+
if l == nil {
122+
t.Fatal("Expected the lock to be generated")
123+
}
124+
125+
govendImportOutputFile := "govend/expected_govend_import_output.txt"
126+
got := verboseOutput.String()
127+
want := h.GetTestFileString(govendImportOutputFile)
128+
if want != got {
129+
if *test.UpdateGolden {
130+
if err := h.WriteTestFile(govendImportOutputFile, got); err != nil {
131+
t.Fatalf("%+v", errors.Wrapf(err, "Unable to write updated golden file %s", govendImportOutputFile))
132+
}
133+
} else {
134+
t.Fatalf("want %s, got %s", want, got)
135+
}
136+
}
137+
138+
}
139+
140+
func TestGovendConfig_YAMLLoad(t *testing.T) {
141+
// This is same as cmd/testdata/govend/vendor.yml
142+
wantYAML := govendYAML{
143+
Imports: []govendPackage{
144+
{
145+
Path: "github.com/sdboyer/deptest",
146+
Revision: "3f4c3bea144e112a69bbe5d8d01c1b09a544253f",
147+
},
148+
{
149+
Path: "github.com/sdboyer/deptestdos",
150+
Revision: "5c607206be5decd28e6263ffffdcee067266015e",
151+
},
152+
},
153+
}
154+
h := test.NewHelper(t)
155+
defer h.Cleanup()
156+
157+
ctx := newTestContext(h)
158+
h.TempCopy(filepath.Join(testProjectRoot, govendYAMLName), "govend/vendor.yml")
159+
160+
projectRoot := h.Path(testProjectRoot)
161+
162+
g := newGovendImporter(ctx.Err, true, nil)
163+
err := g.load(projectRoot)
164+
if err != nil {
165+
t.Fatalf("Error while loading %v", err)
166+
}
167+
168+
if !equalGovendImports(g.yaml.Imports, wantYAML.Imports) {
169+
t.Fatalf("Expected import to be equal. \n\t(GOT): %v\n\t(WNT): %v", g.yaml.Imports, wantYAML.Imports)
170+
}
171+
}
172+
173+
func equalGovendImports(a, b []govendPackage) bool {
174+
if a == nil && b == nil {
175+
return true
176+
}
177+
178+
if a == nil || b == nil {
179+
return false
180+
}
181+
182+
if len(a) != len(b) {
183+
return false
184+
}
185+
186+
for i := range a {
187+
if a[i] != b[i] {
188+
return false
189+
}
190+
}
191+
return true
192+
}

cmd/dep/init.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ specified, use the current directory.
2929
When configuration for another dependency management tool is detected, it is
3030
imported into the initial manifest and lock. Use the -skip-tools flag to
3131
disable this behavior. The following external tools are supported:
32-
glide, godep, vndr.
32+
glide, godep, vndr, govend.
3333
3434
Any dependencies that are not constrained by external configuration use the
3535
GOPATH analysis below.

cmd/dep/root_analyzer.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ func (a *rootAnalyzer) importManifestAndLock(dir string, pr gps.ProjectRoot, sup
7171
newGlideImporter(logger, a.ctx.Verbose, a.sm),
7272
newGodepImporter(logger, a.ctx.Verbose, a.sm),
7373
newVndrImporter(logger, a.ctx.Verbose, a.sm),
74+
newGovendImporter(logger, a.ctx.Verbose, a.sm),
7475
}
7576

7677
for _, i := range importers {
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Detected govend configuration files...
2+
Converting from vendor.yaml...
3+
Using ^0.8.1 as initial constraint for imported dep github.com/sdboyer/deptest
4+
Trying v0.8.1 (3f4c3be) as initial lock for imported dep github.com/sdboyer/deptest
5+
Using ^2.0.0 as initial constraint for imported dep github.com/sdboyer/deptestdos
6+
Trying v2.0.0 (5c60720) as initial lock for imported dep github.com/sdboyer/deptestdos

cmd/dep/testdata/govend/vendor.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
vendors:
2+
- path: github.com/sdboyer/deptest
3+
rev: 3f4c3bea144e112a69bbe5d8d01c1b09a544253f
4+
- path: github.com/sdboyer/deptestdos
5+
rev: 5c607206be5decd28e6263ffffdcee067266015e
6+

0 commit comments

Comments
 (0)