Skip to content
This repository has been archived by the owner on May 3, 2022. It is now read-only.

feat(install): omitting the tag now fetches the latest semver tag #559

Merged
merged 1 commit into from
Dec 3, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 8 additions & 12 deletions cmd/duffle/bundle_remove.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,16 +52,12 @@ func newBundleRemoveCmd(w io.Writer) *cobra.Command {
if err != nil {
return err
}
deletions := map[string]string{}
for ver, sha := range vers {
sv, err := semver.NewVersion(ver)
if err != nil {
fmt.Fprintf(w, "WARNING: %q is not a semantic version", ver)
}
if ok, _ := matcher.Validate(sv); ok {
deletions := []repo.BundleVersion{}
for _, ver := range vers {
if ok, _ := matcher.Validate(ver.Version); ok {
fmt.Fprintf(w, "Version %s matches constraint %q\n", ver, versions)
deletions[ver] = sha
index.DeleteVersion(bname, ver)
deletions = append(deletions, ver)
index.DeleteVersion(bname, ver.Version.String())
// If there are no more versions, remove the entire entry.
if vers, ok := index.GetVersions(bname); ok && len(vers) == 0 {
index.Delete(bname)
Expand Down Expand Up @@ -101,9 +97,9 @@ func newBundleRemoveCmd(w io.Writer) *cobra.Command {
// deleteBundleVersions removes the given SHAs from bundle storage
//
// It warns, but does not fail, if a given SHA is not found.
func deleteBundleVersions(vers map[string]string, index repo.Index, h home.Home, w io.Writer) {
for _, sha := range vers {
fpath := filepath.Join(h.Bundles(), sha)
func deleteBundleVersions(vers []repo.BundleVersion, index repo.Index, h home.Home, w io.Writer) {
for _, ver := range vers {
fpath := filepath.Join(h.Bundles(), ver.Digest)
if err := os.Remove(fpath); err != nil {
fmt.Fprintf(w, "WARNING: could not delete stake record %q", fpath)
}
Expand Down
7 changes: 6 additions & 1 deletion cmd/duffle/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,12 @@ func getBundleFilepath(bun string, insecure bool) (string, error) {
return "", fmt.Errorf("cannot open %s: %v", home.Repositories(), err)
}

digest, err := index.Get(ref.Name(), ref.Tag())
tag := ref.Tag()
if ref.Tag() == "latest" {
tag = ""
}

digest, err := index.Get(ref.Name(), tag)
if err != nil {
return "", fmt.Errorf("could not find %s:%s in %s: %v", ref.Name(), ref.Tag(), home.Repositories(), err)
}
Expand Down
52 changes: 38 additions & 14 deletions pkg/repo/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"sort"

"github.com/Masterminds/semver"
)
Expand All @@ -20,6 +22,19 @@ var (
ErrNoBundleName = errors.New("no bundle name found")
)

type BundleVersion struct {
Version *semver.Version
Digest string
}

// ByVersion implements sort.Interface for []BundleVersion based on
// the version field.
type ByVersion []BundleVersion

func (a ByVersion) Len() int { return len(a) }
func (a ByVersion) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByVersion) Less(i, j int) bool { return a[i].Version.LessThan(a[j].Version) }

// Index defines a list of bundle repositories, each repository's respective tags and the digest reference.
type Index map[string]map[string]string

Expand Down Expand Up @@ -90,14 +105,17 @@ func (i Index) Has(name, version string) bool {
//
// If version is empty, this will return the digest for the bundle with the highest version.
func (i Index) Get(name, version string) (string, error) {
vs, ok := i[name]
var versions ByVersion
versions, ok := i.GetVersions(name)
if !ok {
return "", ErrNoBundleName
}
if len(vs) == 0 {
if len(versions) == 0 {
return "", ErrNoBundleVersion
}

sort.Sort(sort.Reverse(versions))

var constraint *semver.Constraints
if len(version) == 0 {
constraint, _ = semver.NewConstraint("*")
Expand All @@ -109,27 +127,33 @@ func (i Index) Get(name, version string) (string, error) {
}
}

for ver, digest := range vs {
test, err := semver.NewVersion(ver)
if err != nil {
continue
}

if constraint.Check(test) {
return digest, nil
for _, ver := range versions {
if constraint.Check(ver.Version) {
return ver.Digest, nil
}
}
return "", ErrNoBundleVersion
}

// GetVersions gets all of the versions for the given name.
//
// The versions are returned as hash keys, where the values are the SHAs
//
// If the name is not found, this will return false.
func (i Index) GetVersions(name string) (map[string]string, bool) {
func (i Index) GetVersions(name string) ([]BundleVersion, bool) {
ret, ok := i[name]
return ret, ok
rawversions := []string{}
for ver := range ret {
rawversions = append(rawversions, ver)
}

bv := make([]BundleVersion, len(ret))
for i, r := range rawversions {
v, err := semver.NewVersion(r)
if err != nil {
panic(fmt.Sprintf("found a version in the index that is not semver compatible: %s\n", r))
}
bv[i] = BundleVersion{Version: v, Digest: ret[r]}
}
return bv, ok
}

// WriteFile writes an index file to the given destination path.
Expand Down
26 changes: 25 additions & 1 deletion pkg/repo/index_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@ package repo
import (
"bytes"
"reflect"
"sort"
"testing"

"github.com/Masterminds/semver"

"github.com/stretchr/testify/assert"
)

Expand Down Expand Up @@ -44,7 +47,7 @@ func TestLoadIndexReader(t *testing.T) {
revs, ok := l.GetVersions("hub.cnlabs.io/goodbyeworld")
is.True(ok)
is.Len(revs, 2)
is.Equal("abcdefghijklmnop", revs["1.0.0"])
is.Equal("abcdefghijklmnop", revs[0].Digest)

is.True(l.Delete("hub.cnlabs.io/goodbyeworld"))
is.False(l.Has("hub.cnlabs.io/goodbyeworld", "1.0.0"))
Expand All @@ -54,3 +57,24 @@ func TestLoadIndexReader(t *testing.T) {
is.True(l.Has("hub.cnlabs.io/helloworld", "1.0.0"))
is.False(l.Has("hub.cnlabs.io/helloworld", "2.0.0"))
}

func TestBundleVersionSortByVersion(t *testing.T) {
byVersion := ByVersion{
BundleVersion{
Version: semver.MustParse("0.1.0"),
},
BundleVersion{
Version: semver.MustParse("0.2.0"),
},
}

sort.Sort(byVersion)
if byVersion[0].Version.String() != "0.1.0" {
t.Errorf("expected 0.1.0, got %s", byVersion[0].Version.String())
}

sort.Sort(sort.Reverse(byVersion))
if byVersion[0].Version.String() != "0.2.0" {
t.Errorf("expected 0.2.0, got %s", byVersion[0].Version.String())
}
}