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

Commit

Permalink
Merge pull request #715 from carolynvs/detect-branch-vs-tag
Browse files Browse the repository at this point in the history
Interpret strings as branches > semver constraints
  • Loading branch information
darkowlzz authored Jul 7, 2017
2 parents 0822bae + db62987 commit ddf5680
Show file tree
Hide file tree
Showing 11 changed files with 195 additions and 81 deletions.
15 changes: 7 additions & 8 deletions cmd/dep/glide_importer.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,11 +178,11 @@ func (g *glideImporter) convert(pr gps.ProjectRoot) (*dep.Manifest, *dep.Lock, e
lock = &dep.Lock{}

for _, pkg := range g.lock.Imports {
lp := g.buildLockedProject(pkg)
lp := g.buildLockedProject(pkg, manifest)
lock.P = append(lock.P, lp)
}
for _, pkg := range g.lock.TestImports {
lp := g.buildLockedProject(pkg)
lp := g.buildLockedProject(pkg, manifest)
lock.P = append(lock.P, lp)
}
}
Expand Down Expand Up @@ -217,19 +217,18 @@ func (g *glideImporter) buildProjectConstraint(pkg glidePackage) (pc gps.Project
return
}

func (g *glideImporter) buildLockedProject(pkg glideLockedPackage) gps.LockedProject {
func (g *glideImporter) buildLockedProject(pkg glideLockedPackage, manifest *dep.Manifest) gps.LockedProject {
pi := gps.ProjectIdentifier{
ProjectRoot: gps.ProjectRoot(pkg.Name),
Source: pkg.Repository,
}
revision := gps.Revision(pkg.Reference)
pp := manifest.Constraints[pi.ProjectRoot]

version, err := lookupVersionForRevision(revision, pi, g.sm)
version, err := lookupVersionForLockedProject(pi, pp.Constraint, revision, g.sm)
if err != nil {
// Warn about the problem, it is not enough to warrant failing
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)
g.logger.Printf(warn.Error())
version = revision
// Only warn about the problem, it is not enough to warrant failing
g.logger.Println(err.Error())
}

lp := gps.NewLockedProject(pi, version, nil)
Expand Down
8 changes: 4 additions & 4 deletions cmd/dep/glide_importer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func TestGlideConfig_Import(t *testing.T) {
t.Fatal("Expected the lock to be generated")
}

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

h.TempDir(filepath.Join("src", "glidetest"))
h.TempCopy(filepath.Join("glidetest", glideYamlName), "glide/glide.yaml")
projectRoot := h.Path("glidetest")
h.TempDir(filepath.Join("src", testGlideProjectRoot))
h.TempCopy(filepath.Join(testGlideProjectRoot, glideYamlName), "glide/glide.yaml")
projectRoot := h.Path(testGlideProjectRoot)

g := newGlideImporter(ctx.Err, true, sm)
if !g.HasDepMetadata(projectRoot) {
Expand Down
35 changes: 17 additions & 18 deletions cmd/dep/godep_importer.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,16 +125,16 @@ func (g *godepImporter) convert(pr gps.ProjectRoot) (*dep.Manifest, *dep.Lock, e
ProjectRoot: gps.ProjectRoot(pkg.ImportPath),
}
revision := gps.Revision(pkg.Rev)
version, err := lookupVersionForRevision(revision, pi, g.sm)
if err != nil {
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)
g.logger.Printf(warn.Error())
version = revision
}

pp := getProjectPropertiesFromVersion(version)
if pp.Constraint != nil {
pkg.Comment = pp.Constraint.String()
version, err := lookupVersionForLockedProject(pi, nil, revision, g.sm)
if err != nil {
// Only warn about the problem, it is not enough to warrant failing
g.logger.Println(err.Error())
} else {
pp := getProjectPropertiesFromVersion(version)
if pp.Constraint != nil {
pkg.Comment = pp.Constraint.String()
}
}
}

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

lp := g.buildLockedProject(pkg)
lp := g.buildLockedProject(pkg, manifest)
lock.P = append(lock.P, lp)
}

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

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

var version gps.Version

if pkg.Comment != "" {
ver := gps.NewVersion(pkg.Comment)
version = ver.Pair(gps.Revision(pkg.Rev))
} else {
version = gps.Revision(pkg.Rev)
version, err := lookupVersionForLockedProject(pi, pp.Constraint, revision, g.sm)
if err != nil {
// Only warn about the problem, it is not enough to warrant failing
g.logger.Println(err.Error())
}

lp := gps.NewLockedProject(pi, version, nil)
Expand Down
4 changes: 2 additions & 2 deletions cmd/dep/godep_importer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,8 +214,8 @@ func TestGodepConfig_ConvertProject_EmptyComment(t *testing.T) {
}

ver := lpv.String()
if ver != "^1.0.0" {
t.Fatalf("Expected locked version to be '^1.0.0', got %s", ver)
if ver != "v1.0.0" {
t.Fatalf("Expected locked version to be 'v1.0.0', got %s", ver)
}
}

Expand Down
26 changes: 24 additions & 2 deletions cmd/dep/root_analyzer.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,19 +173,41 @@ func (a *rootAnalyzer) Info() gps.ProjectAnalyzerInfo {
}
}

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

gps.SortPairedForUpgrade(versions) // Sort versions in asc order
for _, v := range versions {
if v.Revision() == rev {
// If the constraint is semver, make sure the version is acceptable.
// This prevents us from suggesting an incompatible version, which
// helps narrow the field when there are multiple matching versions.
if c != nil {
_, err := gps.NewSemverConstraint(c.String())
if err == nil && !c.Matches(v) {
continue
}
}
return v, nil
}
}

// Use the version from the manifest as long as it wasn't a range
switch tv := c.(type) {
case gps.PairedVersion:
return tv.Unpair().Pair(rev), nil
case gps.UnpairedVersion:
return tv.Pair(rev), nil
}

// Give up and lock only to a revision
return rev, nil
}
94 changes: 93 additions & 1 deletion cmd/dep/root_analyzer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@

package main

import "testing"
import (
"testing"

"github.com/golang/dep/internal/gps"
"github.com/golang/dep/internal/test"
)

func TestRootAnalyzer_Info(t *testing.T) {
testCases := map[bool]string{
Expand All @@ -19,3 +24,90 @@ func TestRootAnalyzer_Info(t *testing.T) {
}
}
}

func TestLookupVersionForLockedProject_MatchRevisionToTag(t *testing.T) {
h := test.NewHelper(t)
defer h.Cleanup()

ctx := newTestContext(h)
sm, err := ctx.SourceManager()
h.Must(err)
defer sm.Release()

pi := gps.ProjectIdentifier{ProjectRoot: gps.ProjectRoot("github.com/sdboyer/deptest")}
rev := gps.Revision("ff2948a2ac8f538c4ecd55962e919d1e13e74baf")
v, err := lookupVersionForLockedProject(pi, nil, rev, sm)
h.Must(err)

wantV := "v1.0.0"
gotV := v.String()
if gotV != wantV {
t.Fatalf("Expected the locked version to be the tag paired with the manifest's pinned revision: wanted '%s', got '%s'", wantV, gotV)
}
}

func TestLookupVersionForLockedProject_MatchRevisionToMultipleTags(t *testing.T) {
h := test.NewHelper(t)
defer h.Cleanup()

ctx := newTestContext(h)
sm, err := ctx.SourceManager()
h.Must(err)
defer sm.Release()

pi := gps.ProjectIdentifier{ProjectRoot: gps.ProjectRoot("github.com/sdboyer/deptest")}
// Both 0.8.0 and 1.0.0 use the same rev, force dep to pick the lower version
c, _ := gps.NewSemverConstraint("<1.0.0")
rev := gps.Revision("ff2948a2ac8f538c4ecd55962e919d1e13e74baf")
v, err := lookupVersionForLockedProject(pi, c, rev, sm)
h.Must(err)

wantV := "v0.8.0"
gotV := v.String()
if gotV != wantV {
t.Fatalf("Expected the locked version to satisfy the manifest's semver constraint: wanted '%s', got '%s'", wantV, gotV)
}
}

func TestLookupVersionForLockedProject_FallbackToConstraint(t *testing.T) {
h := test.NewHelper(t)
defer h.Cleanup()

ctx := newTestContext(h)
sm, err := ctx.SourceManager()
h.Must(err)
defer sm.Release()

pi := gps.ProjectIdentifier{ProjectRoot: gps.ProjectRoot("github.com/sdboyer/deptest")}
c := gps.NewBranch("master")
rev := gps.Revision("c575196502940c07bf89fd6d95e83b999162e051")
v, err := lookupVersionForLockedProject(pi, c, rev, sm)
h.Must(err)

wantV := c.String()
gotV := v.String()
if gotV != wantV {
t.Fatalf("Expected the locked version to be defaulted from the manifest's branch constraint: wanted '%s', got '%s'", wantV, gotV)
}
}

func TestLookupVersionForLockedProject_FallbackToRevision(t *testing.T) {
h := test.NewHelper(t)
defer h.Cleanup()

ctx := newTestContext(h)
sm, err := ctx.SourceManager()
h.Must(err)
defer sm.Release()

pi := gps.ProjectIdentifier{ProjectRoot: gps.ProjectRoot("github.com/sdboyer/deptest")}
rev := gps.Revision("c575196502940c07bf89fd6d95e83b999162e051")
v, err := lookupVersionForLockedProject(pi, nil, rev, sm)
h.Must(err)

wantV := rev.String()
gotV := v.String()
if gotV != wantV {
t.Fatalf("Expected the locked version to be the manifest's pinned revision: wanted '%s', got '%s'", wantV, gotV)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ Converting from glide.yaml and glide.lock...
Using master as initial constraint for imported dep github.com/golang/lint
Trying v0.8.1 (3f4c3be) as initial lock for imported dep github.com/sdboyer/deptest
Trying v2.0.0 (5c60720) as initial lock for imported dep github.com/sdboyer/deptestdos
Trying master (cb00e56) as initial lock for imported dep github.com/golang/lint
2 changes: 1 addition & 1 deletion cmd/dep/testdata/godep/expected_import_output.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Detected godep configuration files...
Converting from Godeps.json ...
Using ^0.8.1 as initial constraint for imported dep github.com/sdboyer/deptest
Trying ^0.8.1 (3f4c3be) as initial lock 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

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

61 changes: 31 additions & 30 deletions internal/gps/source_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -462,39 +462,20 @@ func (sm *SourceMgr) DeduceProjectRoot(ip string) (ProjectRoot, error) {
return ProjectRoot(pd.root), err
}

// InferConstraint tries to puzzle out what kind of version is given in a string -
// semver, a revision, or as a fallback, a plain tag
// InferConstraint tries to puzzle out what kind of version is given in a string.
// Preference is given first for revisions, then branches, then semver constraints,
// and then plain tags.
func (sm *SourceMgr) InferConstraint(s string, pi ProjectIdentifier) (Constraint, error) {
if s == "" {
// Find the default branch
versions, err := sm.ListVersions(pi)
if err != nil {
return nil, errors.Wrapf(err, "list versions for %s(%s)", pi.ProjectRoot, pi.Source) // means repo does not exist
}

SortPairedForUpgrade(versions)
for _, v := range versions {
if v.Type() == IsBranch {
return v.Unpair(), nil
}
}
}

// always semver if we can
c, err := NewSemverConstraintIC(s)
if err == nil {
return c, nil
}

slen := len(s)
if slen == 40 {
if _, err = hex.DecodeString(s); err == nil {
if _, err := hex.DecodeString(s); err == nil {
// Whether or not it's intended to be a SHA1 digest, this is a
// valid byte sequence for that, so go with Revision. This
// covers git and hg
return Revision(s), nil
}
}

// Next, try for bzr, which has a three-component GUID separated by
// dashes. There should be two, but the email part could contain
// internal dashes
Expand All @@ -507,24 +488,44 @@ func (sm *SourceMgr) InferConstraint(s string, pi ProjectIdentifier) (Constraint
}

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

// call out to network and get the package's versions
// Lookup the string in the repository
var version PairedVersion
versions, err := sm.ListVersions(pi)
if err != nil {
return nil, errors.Wrapf(err, "list versions for %s(%s)", pi.ProjectRoot, pi.Source) // means repo does not exist
}

for _, version := range versions {
if s == version.String() {
return version.Unpair(), nil
SortPairedForUpgrade(versions)
for _, v := range versions {
// Pick the default branch if no constraint is given
if s == "" || s == v.String() {
version = v
break
}
}

// Branch
if version != nil && version.Type() == IsBranch {
return version.Unpair(), nil
}

// Semver Constraint
c, err := NewSemverConstraintIC(s)
if c != nil && err == nil {
return c, nil
}

// Tag
if version != nil {
return version.Unpair(), nil
}

return nil, errors.Errorf("%s is not a valid version for the package %s(%s)", s, pi.ProjectRoot, pi.Source)
}

Expand Down
Loading

0 comments on commit ddf5680

Please sign in to comment.