Skip to content

Commit

Permalink
Sort versions from releases JSON response (#37)
Browse files Browse the repository at this point in the history
* Sort versions from releases JSON response

* bump Go requirement to 1.16 (for go-cmp)

* add E2E test for Versions.List
  • Loading branch information
radeksimko committed Nov 11, 2021
1 parent 489c6f6 commit af09eee
Show file tree
Hide file tree
Showing 10 changed files with 124 additions and 35 deletions.
2 changes: 1 addition & 1 deletion .go-version
@@ -1 +1 @@
1.15.0
1.16.0
1 change: 1 addition & 0 deletions fs/fs_unix.go
@@ -1,4 +1,5 @@
//go:build !windows
// +build !windows

package fs

Expand Down
1 change: 1 addition & 0 deletions fs/fs_unix_test.go
@@ -1,4 +1,5 @@
//go:build !windows
// +build !windows

package fs

Expand Down
3 changes: 2 additions & 1 deletion go.mod
@@ -1,9 +1,10 @@
module github.com/hashicorp/hc-install

go 1.15
go 1.16

require (
github.com/go-git/go-git/v5 v5.4.2
github.com/google/go-cmp v0.3.0
github.com/hashicorp/go-checkpoint v0.5.0
github.com/hashicorp/go-cleanhttp v0.5.2
github.com/hashicorp/go-multierror v1.1.1
Expand Down
4 changes: 2 additions & 2 deletions internal/releasesjson/checksum_downloader.go
Expand Up @@ -51,7 +51,7 @@ func (cd *ChecksumDownloader) DownloadAndVerifyChecksums() (ChecksumFileMap, err
client := httpclient.NewHTTPClient()
sigURL := fmt.Sprintf("%s/%s/%s/%s", cd.BaseURL,
url.PathEscape(cd.ProductVersion.Name),
url.PathEscape(cd.ProductVersion.Version),
url.PathEscape(cd.ProductVersion.RawVersion),
url.PathEscape(sigFilename))
cd.Logger.Printf("downloading signature from %s", sigURL)
sigResp, err := client.Get(sigURL)
Expand All @@ -67,7 +67,7 @@ func (cd *ChecksumDownloader) DownloadAndVerifyChecksums() (ChecksumFileMap, err

shasumsURL := fmt.Sprintf("%s/%s/%s/%s", cd.BaseURL,
url.PathEscape(cd.ProductVersion.Name),
url.PathEscape(cd.ProductVersion.Version),
url.PathEscape(cd.ProductVersion.RawVersion),
url.PathEscape(cd.ProductVersion.SHASUMS))
cd.Logger.Printf("downloading checksums from %s", shasumsURL)
sumsResp, err := client.Get(shasumsURL)
Expand Down
41 changes: 41 additions & 0 deletions internal/releasesjson/product_version.go
@@ -0,0 +1,41 @@
package releasesjson

import "github.com/hashicorp/go-version"

// ProductVersion is a wrapper around a particular product version like
// "consul 0.5.1". A ProductVersion may have one or more builds.
type ProductVersion struct {
Name string `json:"name"`
RawVersion string `json:"version"`
Version *version.Version `json:"-"`
SHASUMS string `json:"shasums,omitempty"`
SHASUMSSig string `json:"shasums_signature,omitempty"`
SHASUMSSigs []string `json:"shasums_signatures,omitempty"`
Builds ProductBuilds `json:"builds"`
}

type ProductVersionsMap map[string]*ProductVersion

type ProductVersions []*ProductVersion

func (pv ProductVersions) Len() int {
return len(pv)
}

func (pv ProductVersions) Less(i, j int) bool {
return pv[i].Version.LessThan(pv[j].Version)
}

func (pv ProductVersions) Swap(i, j int) {
pv[i], pv[j] = pv[j], pv[i]
}

func (pvm ProductVersionsMap) AsSlice() ProductVersions {
versions := make(ProductVersions, 0)

for _, pVersion := range pvm {
versions = append(versions, pVersion)
}

return versions
}
20 changes: 6 additions & 14 deletions internal/releasesjson/releases.go
Expand Up @@ -18,19 +18,8 @@ const defaultBaseURL = "https://releases.hashicorp.com"
// Product is a top-level product like "Consul" or "Nomad". A Product may have
// one or more versions.
type Product struct {
Name string `json:"name"`
Versions map[string]*ProductVersion `json:"versions"`
}

// ProductVersion is a wrapper around a particular product version like
// "consul 0.5.1". A ProductVersion may have one or more builds.
type ProductVersion struct {
Name string `json:"name"`
Version string `json:"version"`
SHASUMS string `json:"shasums,omitempty"`
SHASUMSSig string `json:"shasums_signature,omitempty"`
SHASUMSSigs []string `json:"shasums_signatures,omitempty"`
Builds ProductBuilds `json:"builds"`
Name string `json:"name"`
Versions ProductVersionsMap `json:"versions"`
}

type ProductBuilds []*ProductBuild
Expand Down Expand Up @@ -71,7 +60,7 @@ func (r *Releases) SetLogger(logger *log.Logger) {
r.logger = logger
}

func (r *Releases) ListProductVersions(ctx context.Context, productName string) (map[string]*ProductVersion, error) {
func (r *Releases) ListProductVersions(ctx context.Context, productName string) (ProductVersionsMap, error) {
client := httpclient.NewHTTPClient()

productIndexURL := fmt.Sprintf("%s/%s/index.json",
Expand Down Expand Up @@ -122,7 +111,10 @@ func (r *Releases) ListProductVersions(ctx context.Context, productName string)
// Remove (currently unsupported) enterprise
// version and any other "custom" build
delete(p.Versions, rawVersion)
continue
}

p.Versions[rawVersion].Version = v
}

return p.Versions, nil
Expand Down
13 changes: 4 additions & 9 deletions releases/latest_version.go
Expand Up @@ -149,20 +149,15 @@ func (lv *LatestVersion) Remove(ctx context.Context) error {
return nil
}

func (lv *LatestVersion) findLatestMatchingVersion(pvs map[string]*rjson.ProductVersion, vc version.Constraints) (*rjson.ProductVersion, bool) {
func (lv *LatestVersion) findLatestMatchingVersion(pvs rjson.ProductVersionsMap, vc version.Constraints) (*rjson.ProductVersion, bool) {
versions := make(version.Collection, 0)
for _, pv := range pvs {
v, err := version.NewVersion(pv.Version)
if err != nil {
continue
}

if !lv.IncludePrereleases && v.Prerelease() != "" {
for _, pv := range pvs.AsSlice() {
if !lv.IncludePrereleases && pv.Version.Prerelease() != "" {
// skip prereleases if desired
continue
}

versions = append(versions, v)
versions = append(versions, pv.Version)
}

if len(versions) == 0 {
Expand Down
15 changes: 7 additions & 8 deletions releases/versions.go
Expand Up @@ -3,6 +3,7 @@ package releases
import (
"context"
"fmt"
"sort"
"time"

"github.com/hashicorp/go-version"
Expand Down Expand Up @@ -54,21 +55,19 @@ func (v *Versions) List(ctx context.Context) ([]src.Source, error) {
return nil, err
}

installables := make([]src.Source, 0)
for _, pv := range pvs {
installableVersion, err := version.NewVersion(pv.Version)
if err != nil {
continue
}
versions := pvs.AsSlice()
sort.Stable(versions)

if !v.Constraints.Check(installableVersion) {
installables := make([]src.Source, 0)
for _, pv := range versions {
if !v.Constraints.Check(pv.Version) {
// skip version which doesn't match constraint
continue
}

ev := &ExactVersion{
Product: v.Product,
Version: installableVersion,
Version: pv.Version,
InstallDir: v.Install.Dir,
Timeout: v.Install.Timeout,

Expand Down
59 changes: 59 additions & 0 deletions releases/versions_test.go
@@ -0,0 +1,59 @@
package releases

import (
"context"
"testing"

"github.com/google/go-cmp/cmp"
"github.com/hashicorp/go-version"
"github.com/hashicorp/hc-install/internal/testutil"
"github.com/hashicorp/hc-install/product"
"github.com/hashicorp/hc-install/src"
)

func TestVersions_List(t *testing.T) {
testutil.EndToEndTest(t)

cons, err := version.NewConstraint(">= 1.0.0, < 1.0.10")
if err != nil {
t.Fatal(err)
}

versions := &Versions{
Product: product.Terraform,
Constraints: cons,
}

ctx := context.Background()
sources, err := versions.List(ctx)
if err != nil {
t.Fatal(err)
}

expectedVersions := []string{
"1.0.0",
"1.0.1",
"1.0.2",
"1.0.3",
"1.0.4",
"1.0.5",
"1.0.6",
"1.0.7",
"1.0.8",
"1.0.9",
}
if diff := cmp.Diff(expectedVersions, sourcesToRawVersions(sources)); diff != "" {
t.Fatalf("unexpected versions: %s", diff)
}
}

func sourcesToRawVersions(srcs []src.Source) []string {
rawVersions := make([]string, len(srcs))

for idx, src := range srcs {
source := src.(*ExactVersion)
rawVersions[idx] = source.Version.String()
}

return rawVersions
}

0 comments on commit af09eee

Please sign in to comment.