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

Commit c300e1c

Browse files
committed
Interpret strings as branches > semver constraints
When a user supplied string in an imported config file, or specified to dep ensure, can be interpreted multiple ways, prefer the branch over a semver constraint. In #710, glide.yaml specified v2 for https://github.com/go-mgo/mgo. When we assume that is a semver constraint, solve fails because the hinted revision in the lock (a commit on the v2 branch) doesn't satisfy the assumed constraint of ^2.0.0. The new preferred match order for the user string is: * revision * branch * semver constraint * tag I am giving preference of a semver constraint over a tag so that a bare version, 1.0.0, is interpreted more loosely with an implied caret, ^1.0.0, instead of the stricter exact match.
1 parent 0822bae commit c300e1c

11 files changed

+164
-81
lines changed

cmd/dep/glide_importer.go

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -178,11 +178,11 @@ func (g *glideImporter) convert(pr gps.ProjectRoot) (*dep.Manifest, *dep.Lock, e
178178
lock = &dep.Lock{}
179179

180180
for _, pkg := range g.lock.Imports {
181-
lp := g.buildLockedProject(pkg)
181+
lp := g.buildLockedProject(pkg, manifest)
182182
lock.P = append(lock.P, lp)
183183
}
184184
for _, pkg := range g.lock.TestImports {
185-
lp := g.buildLockedProject(pkg)
185+
lp := g.buildLockedProject(pkg, manifest)
186186
lock.P = append(lock.P, lp)
187187
}
188188
}
@@ -217,19 +217,18 @@ func (g *glideImporter) buildProjectConstraint(pkg glidePackage) (pc gps.Project
217217
return
218218
}
219219

220-
func (g *glideImporter) buildLockedProject(pkg glideLockedPackage) gps.LockedProject {
220+
func (g *glideImporter) buildLockedProject(pkg glideLockedPackage, manifest *dep.Manifest) gps.LockedProject {
221221
pi := gps.ProjectIdentifier{
222222
ProjectRoot: gps.ProjectRoot(pkg.Name),
223223
Source: pkg.Repository,
224224
}
225225
revision := gps.Revision(pkg.Reference)
226+
pp := manifest.Constraints[pi.ProjectRoot]
226227

227-
version, err := lookupVersionForRevision(revision, pi, g.sm)
228+
version, err := lookupVersionForLockedProject(pi, pp.Constraint, revision, g.sm)
228229
if err != nil {
229-
// Warn about the problem, it is not enough to warrant failing
230-
warn := errors.Wrapf(err, "Unable to lookup the version represented by %s in %s(%s). Falling back to locking the revision only.", revision, pi.ProjectRoot, pi.Source)
231-
g.logger.Printf(warn.Error())
232-
version = revision
230+
// Only warn about the problem, it is not enough to warrant failing
231+
g.logger.Println(err.Error())
233232
}
234233

235234
lp := gps.NewLockedProject(pi, version, nil)

cmd/dep/glide_importer_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ func TestGlideConfig_Import(t *testing.T) {
6868
t.Fatal("Expected the lock to be generated")
6969
}
7070

71-
goldenFile := "glide/expected_import_output.txt"
71+
goldenFile := "glide/golden.txt"
7272
got := verboseOutput.String()
7373
want := h.GetTestFileString(goldenFile)
7474
if want != got {
@@ -91,9 +91,9 @@ func TestGlideConfig_Import_MissingLockFile(t *testing.T) {
9191
h.Must(err)
9292
defer sm.Release()
9393

94-
h.TempDir(filepath.Join("src", "glidetest"))
95-
h.TempCopy(filepath.Join("glidetest", glideYamlName), "glide/glide.yaml")
96-
projectRoot := h.Path("glidetest")
94+
h.TempDir(filepath.Join("src", testGlideProjectRoot))
95+
h.TempCopy(filepath.Join(testGlideProjectRoot, glideYamlName), "glide/glide.yaml")
96+
projectRoot := h.Path(testGlideProjectRoot)
9797

9898
g := newGlideImporter(ctx.Err, true, sm)
9999
if !g.HasDepMetadata(projectRoot) {

cmd/dep/godep_importer.go

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -125,16 +125,16 @@ func (g *godepImporter) convert(pr gps.ProjectRoot) (*dep.Manifest, *dep.Lock, e
125125
ProjectRoot: gps.ProjectRoot(pkg.ImportPath),
126126
}
127127
revision := gps.Revision(pkg.Rev)
128-
version, err := lookupVersionForRevision(revision, pi, g.sm)
129-
if err != nil {
130-
warn := errors.Wrapf(err, "Unable to lookup the version represented by %s in %s. Falling back to locking the revision only.", pkg.Rev, pi.ProjectRoot)
131-
g.logger.Printf(warn.Error())
132-
version = revision
133-
}
134128

135-
pp := getProjectPropertiesFromVersion(version)
136-
if pp.Constraint != nil {
137-
pkg.Comment = pp.Constraint.String()
129+
version, err := lookupVersionForLockedProject(pi, nil, revision, g.sm)
130+
if err != nil {
131+
// Only warn about the problem, it is not enough to warrant failing
132+
g.logger.Println(err.Error())
133+
} else {
134+
pp := getProjectPropertiesFromVersion(version)
135+
if pp.Constraint != nil {
136+
pkg.Comment = pp.Constraint.String()
137+
}
138138
}
139139
}
140140

@@ -147,7 +147,7 @@ func (g *godepImporter) convert(pr gps.ProjectRoot) (*dep.Manifest, *dep.Lock, e
147147
manifest.Constraints[pc.Ident.ProjectRoot] = gps.ProjectProperties{Constraint: pc.Constraint}
148148
}
149149

150-
lp := g.buildLockedProject(pkg)
150+
lp := g.buildLockedProject(pkg, manifest)
151151
lock.P = append(lock.P, lp)
152152
}
153153

@@ -170,16 +170,15 @@ func (g *godepImporter) buildProjectConstraint(pkg godepPackage) (pc gps.Project
170170
}
171171

172172
// buildLockedProject uses the package Rev and Comment to create lock project
173-
func (g *godepImporter) buildLockedProject(pkg godepPackage) gps.LockedProject {
173+
func (g *godepImporter) buildLockedProject(pkg godepPackage, manifest *dep.Manifest) gps.LockedProject {
174174
pi := gps.ProjectIdentifier{ProjectRoot: gps.ProjectRoot(pkg.ImportPath)}
175+
revision := gps.Revision(pkg.Rev)
176+
pp := manifest.Constraints[pi.ProjectRoot]
175177

176-
var version gps.Version
177-
178-
if pkg.Comment != "" {
179-
ver := gps.NewVersion(pkg.Comment)
180-
version = ver.Pair(gps.Revision(pkg.Rev))
181-
} else {
182-
version = gps.Revision(pkg.Rev)
178+
version, err := lookupVersionForLockedProject(pi, pp.Constraint, revision, g.sm)
179+
if err != nil {
180+
// Only warn about the problem, it is not enough to warrant failing
181+
g.logger.Println(err.Error())
183182
}
184183

185184
lp := gps.NewLockedProject(pi, version, nil)

cmd/dep/godep_importer_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,8 +214,8 @@ func TestGodepConfig_ConvertProject_EmptyComment(t *testing.T) {
214214
}
215215

216216
ver := lpv.String()
217-
if ver != "^1.0.0" {
218-
t.Fatalf("Expected locked version to be '^1.0.0', got %s", ver)
217+
if ver != "v1.0.0" {
218+
t.Fatalf("Expected locked version to be 'v1.0.0', got %s", ver)
219219
}
220220
}
221221

cmd/dep/root_analyzer.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,11 +173,15 @@ func (a *rootAnalyzer) Info() gps.ProjectAnalyzerInfo {
173173
}
174174
}
175175

176-
func lookupVersionForRevision(rev gps.Revision, pi gps.ProjectIdentifier, sm gps.SourceManager) (gps.Version, error) {
176+
// lookupVersionForLockedProject figures out the appropriate version for a locked
177+
// project based on the locked revision and the constraint from the manifest.
178+
// First try matching the revision to a version, then try the constraint from the
179+
// manifest, then finally the revision.
180+
func lookupVersionForLockedProject(pi gps.ProjectIdentifier, c gps.Constraint, rev gps.Revision, sm gps.SourceManager) (gps.Version, error) {
177181
// Find the version that goes with this revision, if any
178182
versions, err := sm.ListVersions(pi)
179183
if err != nil {
180-
return nil, errors.Wrapf(err, "Unable to list versions for %s(%s)", pi.ProjectRoot, pi.Source)
184+
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)
181185
}
182186

183187
gps.SortPairedForUpgrade(versions) // Sort versions in asc order
@@ -187,5 +191,14 @@ func lookupVersionForRevision(rev gps.Revision, pi gps.ProjectIdentifier, sm gps
187191
}
188192
}
189193

194+
// Use the version from the manifest as long as it wasn't a range
195+
switch tv := c.(type) {
196+
case gps.PairedVersion:
197+
return tv.Unpair().Pair(rev), nil
198+
case gps.UnpairedVersion:
199+
return tv.Pair(rev), nil
200+
}
201+
202+
// Give up and lock only to a revision
190203
return rev, nil
191204
}

cmd/dep/root_analyzer_test.go

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,12 @@
44

55
package main
66

7-
import "testing"
7+
import (
8+
"testing"
9+
10+
"github.com/golang/dep/internal/gps"
11+
"github.com/golang/dep/internal/test"
12+
)
813

914
func TestRootAnalyzer_Info(t *testing.T) {
1015
testCases := map[bool]string{
@@ -19,3 +24,68 @@ func TestRootAnalyzer_Info(t *testing.T) {
1924
}
2025
}
2126
}
27+
28+
func TestLookupVersionForLockedProject_MatchRevisionToTag(t *testing.T) {
29+
h := test.NewHelper(t)
30+
defer h.Cleanup()
31+
32+
ctx := newTestContext(h)
33+
sm, err := ctx.SourceManager()
34+
h.Must(err)
35+
defer sm.Release()
36+
37+
pi := gps.ProjectIdentifier{ProjectRoot: gps.ProjectRoot("github.com/sdboyer/deptest")}
38+
c, _ := gps.NewSemverConstraint("^0.8.1")
39+
rev := gps.Revision("ff2948a2ac8f538c4ecd55962e919d1e13e74baf")
40+
v, err := lookupVersionForLockedProject(pi, c, rev, sm)
41+
h.Must(err)
42+
43+
wantV := "v1.0.0"
44+
gotV := v.String()
45+
if gotV != wantV {
46+
t.Fatalf("Expected the locked version to be the tag paired with the manifest's pinned revision: wanted '%s', got '%s'", wantV, gotV)
47+
}
48+
}
49+
50+
func TestLookupVersionForLockedProject_FallbackToConstraint(t *testing.T) {
51+
h := test.NewHelper(t)
52+
defer h.Cleanup()
53+
54+
ctx := newTestContext(h)
55+
sm, err := ctx.SourceManager()
56+
h.Must(err)
57+
defer sm.Release()
58+
59+
pi := gps.ProjectIdentifier{ProjectRoot: gps.ProjectRoot("github.com/sdboyer/deptest")}
60+
c := gps.NewBranch("master")
61+
rev := gps.Revision("c575196502940c07bf89fd6d95e83b999162e051")
62+
v, err := lookupVersionForLockedProject(pi, c, rev, sm)
63+
h.Must(err)
64+
65+
wantV := c.String()
66+
gotV := v.String()
67+
if gotV != wantV {
68+
t.Fatalf("Expected the locked version to be defaulted from the manifest's branch constraint: wanted '%s', got '%s'", wantV, gotV)
69+
}
70+
}
71+
72+
func TestLookupVersionForLockedProject_FallbackToRevision(t *testing.T) {
73+
h := test.NewHelper(t)
74+
defer h.Cleanup()
75+
76+
ctx := newTestContext(h)
77+
sm, err := ctx.SourceManager()
78+
h.Must(err)
79+
defer sm.Release()
80+
81+
pi := gps.ProjectIdentifier{ProjectRoot: gps.ProjectRoot("github.com/sdboyer/deptest")}
82+
rev := gps.Revision("c575196502940c07bf89fd6d95e83b999162e051")
83+
v, err := lookupVersionForLockedProject(pi, nil, rev, sm)
84+
h.Must(err)
85+
86+
wantV := rev.String()
87+
gotV := v.String()
88+
if gotV != wantV {
89+
t.Fatalf("Expected the locked version to be the manifest's pinned revision: wanted '%s', got '%s'", wantV, gotV)
90+
}
91+
}

cmd/dep/testdata/glide/expected_import_output.txt renamed to cmd/dep/testdata/glide/golden.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ Converting from glide.yaml and glide.lock...
55
Using master as initial constraint for imported dep github.com/golang/lint
66
Trying v0.8.1 (3f4c3be) as initial lock for imported dep github.com/sdboyer/deptest
77
Trying v2.0.0 (5c60720) as initial lock for imported dep github.com/sdboyer/deptestdos
8+
Trying master (cb00e56) as initial lock for imported dep github.com/golang/lint
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Detected godep configuration files...
22
Converting from Godeps.json ...
33
Using ^0.8.1 as initial constraint for imported dep github.com/sdboyer/deptest
4-
Trying ^0.8.1 (3f4c3be) as initial lock for imported dep github.com/sdboyer/deptest
4+
Trying v0.8.1 (3f4c3be) as initial lock for imported dep github.com/sdboyer/deptest
55
Using ^2.0.0 as initial constraint for imported dep github.com/sdboyer/deptestdos
66
Trying v2.0.0 (5c60720) as initial lock for imported dep github.com/sdboyer/deptestdos

cmd/dep/testdata/harness_tests/init/godep/case1/final/Gopkg.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/gps/source_manager.go

Lines changed: 31 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -462,39 +462,20 @@ func (sm *SourceMgr) DeduceProjectRoot(ip string) (ProjectRoot, error) {
462462
return ProjectRoot(pd.root), err
463463
}
464464

465-
// InferConstraint tries to puzzle out what kind of version is given in a string -
466-
// semver, a revision, or as a fallback, a plain tag
465+
// InferConstraint tries to puzzle out what kind of version is given in a string.
466+
// Preference is given first for revisions, then branches, then semver constraints,
467+
// and then plain tags.
467468
func (sm *SourceMgr) InferConstraint(s string, pi ProjectIdentifier) (Constraint, error) {
468-
if s == "" {
469-
// Find the default branch
470-
versions, err := sm.ListVersions(pi)
471-
if err != nil {
472-
return nil, errors.Wrapf(err, "list versions for %s(%s)", pi.ProjectRoot, pi.Source) // means repo does not exist
473-
}
474-
475-
SortPairedForUpgrade(versions)
476-
for _, v := range versions {
477-
if v.Type() == IsBranch {
478-
return v.Unpair(), nil
479-
}
480-
}
481-
}
482-
483-
// always semver if we can
484-
c, err := NewSemverConstraintIC(s)
485-
if err == nil {
486-
return c, nil
487-
}
488-
489469
slen := len(s)
490470
if slen == 40 {
491-
if _, err = hex.DecodeString(s); err == nil {
471+
if _, err := hex.DecodeString(s); err == nil {
492472
// Whether or not it's intended to be a SHA1 digest, this is a
493473
// valid byte sequence for that, so go with Revision. This
494474
// covers git and hg
495475
return Revision(s), nil
496476
}
497477
}
478+
498479
// Next, try for bzr, which has a three-component GUID separated by
499480
// dashes. There should be two, but the email part could contain
500481
// internal dashes
@@ -507,24 +488,44 @@ func (sm *SourceMgr) InferConstraint(s string, pi ProjectIdentifier) (Constraint
507488
}
508489

509490
i2 := strings.LastIndex(s[:i3], "-")
510-
if _, err = strconv.ParseUint(s[i2+1:i3], 10, 64); err == nil {
491+
if _, err := strconv.ParseUint(s[i2+1:i3], 10, 64); err == nil {
511492
// Getting this far means it'd pretty much be nuts if it's not a
512493
// bzr rev, so don't bother parsing the email.
513494
return Revision(s), nil
514495
}
515496
}
516497

517-
// call out to network and get the package's versions
498+
// Lookup the string in the repository
499+
var version PairedVersion
518500
versions, err := sm.ListVersions(pi)
519501
if err != nil {
520502
return nil, errors.Wrapf(err, "list versions for %s(%s)", pi.ProjectRoot, pi.Source) // means repo does not exist
521503
}
522-
523-
for _, version := range versions {
524-
if s == version.String() {
525-
return version.Unpair(), nil
504+
SortPairedForUpgrade(versions)
505+
for _, v := range versions {
506+
// Pick the default branch if no constraint is given
507+
if s == "" || s == v.String() {
508+
version = v
509+
break
526510
}
527511
}
512+
513+
// Branch
514+
if version != nil && version.Type() == IsBranch {
515+
return version.Unpair(), nil
516+
}
517+
518+
// Semver Constraint
519+
c, err := NewSemverConstraintIC(s)
520+
if c != nil && err == nil {
521+
return c, nil
522+
}
523+
524+
// Tag
525+
if version != nil {
526+
return version.Unpair(), nil
527+
}
528+
528529
return nil, errors.Errorf("%s is not a valid version for the package %s(%s)", s, pi.ProjectRoot, pi.Source)
529530
}
530531

0 commit comments

Comments
 (0)