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

Commit 4916449

Browse files
committed
Initial gb project importer
1 parent 911cd22 commit 4916449

File tree

5 files changed

+367
-0
lines changed

5 files changed

+367
-0
lines changed

cmd/dep/gb_importer.go

+169
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
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+
"encoding/json"
9+
"io/ioutil"
10+
"log"
11+
"os"
12+
"path/filepath"
13+
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+
// gbImporter imports gb configuration into the dep configuration format.
21+
type gbImporter struct {
22+
manifest gbManifest
23+
logger *log.Logger
24+
verbose bool
25+
sm gps.SourceManager
26+
}
27+
28+
func newGbImporter(logger *log.Logger, verbose bool, sm gps.SourceManager) *gbImporter {
29+
return &gbImporter{
30+
logger: logger,
31+
verbose: verbose,
32+
sm: sm,
33+
}
34+
}
35+
36+
// gbManifest represents the manifest file for GB projects
37+
type gbManifest struct {
38+
Version int `json:"version"`
39+
Dependencies []gbDependency `json:"dependencies"`
40+
}
41+
42+
type gbDependency struct {
43+
Importpath string `json:"importpath"`
44+
Repository string `json:"repository"`
45+
46+
// All gb vendored dependencies have a specific revision
47+
Revision string `json:"revision"`
48+
49+
// Branch may be HEAD or an actual branch. In the case of HEAD, that means
50+
// the user vendored a dependency by specifying a tag or a specific revision
51+
// which results in a detached HEAD
52+
Branch string `json:"branch"`
53+
54+
// Path is used when the actual used package exists in a subpath
55+
// For importing purposes, this isn't necessary as dep will figure
56+
// out which packages are actually being used during resolving
57+
Path string `json:"path,omitempty"`
58+
}
59+
60+
func (i *gbImporter) Name() string {
61+
return "gb"
62+
}
63+
64+
func (i *gbImporter) HasDepMetadata(dir string) bool {
65+
// gb stores the manifest in the vendor tree
66+
var m = filepath.Join(dir, "vendor", "manifest")
67+
if _, err := os.Stat(m); err != nil {
68+
return false
69+
}
70+
71+
return true
72+
}
73+
74+
func (i *gbImporter) Import(dir string, pr gps.ProjectRoot) (*dep.Manifest, *dep.Lock, error) {
75+
err := i.load(dir)
76+
if err != nil {
77+
return nil, nil, err
78+
}
79+
80+
return i.convert(pr)
81+
}
82+
83+
// load the gb manifest
84+
func (i *gbImporter) load(projectDir string) error {
85+
i.logger.Println("Detected gb manifest file...")
86+
var err error
87+
var mf = filepath.Join(projectDir, "vendor", "manifest")
88+
if i.verbose {
89+
i.logger.Printf(" Loading %s", mf)
90+
}
91+
92+
var buf []byte
93+
if buf, err = ioutil.ReadFile(mf); err != nil {
94+
return errors.Wrapf(err, "Unable to read %s", mf)
95+
}
96+
if err = json.Unmarshal(buf, &i.manifest); err != nil {
97+
return errors.Wrapf(err, "Unable to parse %s", mf)
98+
}
99+
100+
return nil
101+
}
102+
103+
// convert the gb manifest into dep configuration files.
104+
func (i *gbImporter) convert(pr gps.ProjectRoot) (*dep.Manifest, *dep.Lock, error) {
105+
i.logger.Println("Converting from gb manifest...")
106+
107+
manifest := &dep.Manifest{
108+
Constraints: make(gps.ProjectConstraints),
109+
}
110+
111+
lock := &dep.Lock{}
112+
113+
for _, pkg := range i.manifest.Dependencies {
114+
pc, lp, err := i.convertOne(pkg)
115+
if err != nil {
116+
return nil, nil, err
117+
}
118+
manifest.Constraints[pc.Ident.ProjectRoot] = gps.ProjectProperties{Source: pc.Ident.Source, Constraint: pc.Constraint}
119+
lock.P = append(lock.P, lp)
120+
}
121+
122+
return manifest, lock, nil
123+
}
124+
125+
func (i *gbImporter) convertOne(pkg gbDependency) (pc gps.ProjectConstraint, lp gps.LockedProject, err error) {
126+
if pkg.Importpath == "" {
127+
err = errors.New("Invalid gb configuration, package import path is required")
128+
return
129+
}
130+
131+
/*
132+
gb's vendor plugin (gb vendor), which manages the vendor tree and manifest
133+
file, supports fetching by a specific tag or revision, but if you specify
134+
either of those it's a detached checkout and the "branch" field is HEAD.
135+
The only time the "branch" field is not "HEAD" is if you do not specify a
136+
tag or revision, otherwise it's either "master" or the value of the -branch
137+
flag
138+
139+
This means that, generally, the only possible "constraint" we can really specify is
140+
the branch name if the branch name is not HEAD. Otherwise, it's a specific revision.
141+
142+
However, if we can infer a tag that points to the revision or the branch, we may be able
143+
to use that as the constraint
144+
*/
145+
146+
pc.Ident = gps.ProjectIdentifier{ProjectRoot: gps.ProjectRoot(pkg.Importpath), Source: pkg.Repository}
147+
148+
// The default constraint is just a revision
149+
var revision = gps.Revision(pkg.Revision)
150+
151+
// See if we can get a version from that constraint
152+
version, err := lookupVersionForLockedProject(pc.Ident, revision, revision, i.sm)
153+
if err != nil {
154+
return
155+
}
156+
157+
// And now try to infer a constraint from the returned version
158+
pc.Constraint, err = i.sm.InferConstraint(version.String(), pc.Ident)
159+
if err != nil {
160+
return
161+
}
162+
163+
lp = gps.NewLockedProject(pc.Ident, version, nil)
164+
165+
fb.NewLockedProjectFeedback(lp, fb.DepTypeImported).LogFeedback(i.logger)
166+
fb.NewConstraintFeedback(pc, fb.DepTypeImported).LogFeedback(i.logger)
167+
168+
return
169+
}

cmd/dep/gb_importer_test.go

+165
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
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+
const testGbProjectRoot = "github.com/golang/notexist"
19+
20+
func TestGbConfig_Import(t *testing.T) {
21+
h := test.NewHelper(t)
22+
defer h.Cleanup()
23+
24+
ctx := newTestContext(h)
25+
sm, err := ctx.SourceManager()
26+
h.Must(err)
27+
defer sm.Release()
28+
29+
h.TempDir(filepath.Join("src", testGbProjectRoot, "vendor"))
30+
h.TempCopy(filepath.Join(testGbProjectRoot, "vendor", "manifest"), "gb/manifest")
31+
projectRoot := h.Path(testGbProjectRoot)
32+
33+
// Capture stderr so we can verify output
34+
verboseOutput := &bytes.Buffer{}
35+
ctx.Err = log.New(verboseOutput, "", 0)
36+
37+
g := newGbImporter(ctx.Err, false, sm) // Disable verbose so that we don't print values that change each test run
38+
if !g.HasDepMetadata(projectRoot) {
39+
t.Fatal("Expected the importer to detect the gb manifest file")
40+
}
41+
42+
m, l, err := g.Import(projectRoot, testGbProjectRoot)
43+
h.Must(err)
44+
45+
if m == nil {
46+
t.Fatal("Expected the manifest to be generated")
47+
}
48+
49+
if l == nil {
50+
t.Fatal("Expected the lock to be generated")
51+
}
52+
53+
goldenFile := "gb/golden.txt"
54+
got := verboseOutput.String()
55+
want := h.GetTestFileString(goldenFile)
56+
if want != got {
57+
if *test.UpdateGolden {
58+
if err := h.WriteTestFile(goldenFile, got); err != nil {
59+
t.Fatalf("%+v", errors.Wrapf(err, "Unable to write updated golden file %s", goldenFile))
60+
}
61+
} else {
62+
t.Fatalf("expected %s, got %s", want, got)
63+
}
64+
}
65+
}
66+
67+
func TestGbConfig_Convert_Project(t *testing.T) {
68+
h := test.NewHelper(t)
69+
defer h.Cleanup()
70+
71+
ctx := newTestContext(h)
72+
sm, err := ctx.SourceManager()
73+
h.Must(err)
74+
defer sm.Release()
75+
76+
pkg := "github.com/sdboyer/deptest"
77+
repo := "https://github.com/sdboyer/deptest.git"
78+
79+
g := newGbImporter(ctx.Err, true, sm)
80+
g.manifest = gbManifest{
81+
Dependencies: []gbDependency{
82+
{
83+
Importpath: pkg,
84+
Repository: repo,
85+
Revision: "ff2948a2ac8f538c4ecd55962e919d1e13e74baf",
86+
},
87+
},
88+
}
89+
90+
manifest, lock, err := g.convert(testGbProjectRoot)
91+
if err != nil {
92+
t.Fatal(err)
93+
}
94+
95+
d, ok := manifest.Constraints[gps.ProjectRoot(pkg)]
96+
if !ok {
97+
t.Fatal("Expected the manifest to have a dependency for 'github.com/sdboyer/deptest' but got none")
98+
}
99+
100+
wantC := "^1.0.0"
101+
gotC := d.Constraint.String()
102+
if gotC != wantC {
103+
t.Fatalf("Expected manifest constraint to be %s, got %s", wantC, gotC)
104+
}
105+
106+
gotS := d.Source
107+
if gotS != repo {
108+
t.Fatalf("Expected manifest source to be %s, got %s", repo, gotS)
109+
}
110+
111+
wantP := 1
112+
gotP := len(lock.P)
113+
if gotP != 1 {
114+
t.Fatalf("Expected the lock to contain %d project but got %d", wantP, gotP)
115+
}
116+
117+
p := lock.P[0]
118+
gotPr := string(p.Ident().ProjectRoot)
119+
if gotPr != pkg {
120+
t.Fatalf("Expected the lock to have a project for %s but got '%s'", pkg, gotPr)
121+
}
122+
123+
gotS = p.Ident().Source
124+
if gotS != repo {
125+
t.Fatalf("Expected locked source to be %s, got '%s'", repo, gotS)
126+
}
127+
128+
lv := p.Version()
129+
lpv, ok := lv.(gps.PairedVersion)
130+
if !ok {
131+
t.Fatalf("Expected locked version to be a PairedVersion but got %T", lv)
132+
}
133+
134+
wantRev := "ff2948a2ac8f538c4ecd55962e919d1e13e74baf"
135+
gotRev := lpv.Revision().String()
136+
if gotRev != wantRev {
137+
t.Fatalf("Expected locked revision to be %s, got %s", wantRev, gotRev)
138+
}
139+
140+
wantV := "v1.0.0"
141+
gotV := lpv.String()
142+
if gotV != wantV {
143+
t.Fatalf("Expected locked version to be %s, got %s", wantV, gotV)
144+
}
145+
}
146+
147+
func TestGbConfig_Convert_BadInput_EmptyPackageName(t *testing.T) {
148+
h := test.NewHelper(t)
149+
defer h.Cleanup()
150+
151+
ctx := newTestContext(h)
152+
sm, err := ctx.SourceManager()
153+
h.Must(err)
154+
defer sm.Release()
155+
156+
g := newGbImporter(ctx.Err, true, sm)
157+
g.manifest = gbManifest{
158+
Dependencies: []gbDependency{{Importpath: ""}},
159+
}
160+
161+
_, _, err = g.convert(testGbProjectRoot)
162+
if err == nil {
163+
t.Fatal("Expected conversion to fail because the package name is empty")
164+
}
165+
}

cmd/dep/root_analyzer.go

+1
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ func (a *rootAnalyzer) importManifestAndLock(dir string, pr gps.ProjectRoot, sup
7373
importers := []importer{
7474
newGlideImporter(logger, a.ctx.Verbose, a.sm),
7575
newGodepImporter(logger, a.ctx.Verbose, a.sm),
76+
newGbImporter(logger, a.ctx.Verbose, a.sm),
7677
}
7778

7879
for _, i := range importers {

cmd/dep/testdata/gb/golden.txt

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Detected gb manifest file...
2+
Converting from gb manifest...
3+
Trying master (2788f0d) as initial lock for imported dep github.com/kr/fs
4+
Using master as initial constraint for imported dep github.com/kr/fs
5+
Using f234c3c6540c0358b1802f7fd90c0879af9232eb as initial hint for imported dep github.com/pkg/sftp
6+
Trying v1.0.0 (ff2948a) as initial lock for imported dep github.com/sdboyer/deptest
7+
Using ^1.0.0 as initial constraint for imported dep github.com/sdboyer/deptest

cmd/dep/testdata/gb/manifest

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"version": 0,
3+
"dependencies": [
4+
{
5+
"importpath": "github.com/kr/fs",
6+
"repository": "https://github.com/kr/fs",
7+
"revision": "2788f0dbd16903de03cb8186e5c7d97b69ad387b",
8+
"branch": "master",
9+
"path": ""
10+
},
11+
{
12+
"importpath": "github.com/pkg/sftp",
13+
"repository": "https://github.com/pkg/sftp",
14+
"revision": "f234c3c6540c0358b1802f7fd90c0879af9232eb",
15+
"branch": "master",
16+
"path": ""
17+
},
18+
{
19+
"importpath": "github.com/sdboyer/deptest",
20+
"repository": "https://github.com/sdboyer/deptest",
21+
"revision": "ff2948a2ac8f538c4ecd55962e919d1e13e74baf",
22+
"branch": "HEAD"
23+
}
24+
]
25+
}

0 commit comments

Comments
 (0)