Unverified Commit 97410698 authored by Jeremy Morrell's avatar Jeremy Morrell Committed by GitHub

Resolve node versions (#655)

parent 58078fcb
......@@ -9,6 +9,7 @@ import (
"net/url"
"os"
"regexp"
"runtime"
"sort"
"time"
......@@ -56,7 +57,24 @@ func main() {
binary := os.Args[1]
versionRequirement := os.Args[2]
if binary == "yarn" {
if binary == "node" {
objects, err := listS3Objects("heroku-nodebin", "node")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
result, err := resolveNode(objects, getPlatform(), versionRequirement)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
if result.matched {
fmt.Printf("%s %s\n", result.release.version.String(), result.release.url)
} else {
fmt.Println("No result")
os.Exit(1)
}
} else if binary == "yarn" {
objects, err := listS3Objects("heroku-nodebin", "yarn")
if err != nil {
fmt.Println(err)
......@@ -80,6 +98,57 @@ func printUsage() {
fmt.Println("resolve-version binary version-requirement")
}
func getPlatform() string {
if runtime.GOOS == "darwin" {
return "darwin-x64"
}
return "linux-x64"
}
func resolveNode(objects []s3Object, platform string, versionRequirement string) (matchResult, error) {
releases := []release{}
staging := []release{}
for _, obj := range objects {
release, err := parseObject(obj.Key)
if err != nil {
continue
}
// ignore any releases that are not for the given platform
if release.platform != platform {
continue
}
if release.stage == "release" {
releases = append(releases, release)
} else {
staging = append(staging, release)
}
}
result, err := matchReleaseSemver(releases, versionRequirement)
if err != nil {
return matchResult{}, err
}
// In order to accomodate integrated testing of staged Node binaries before they are
// released broadly, there is a special case where:
//
// - if there is no match to a Node binary AND
// - an exact version of a binary in `node/staging` is present
//
// the staging binary is used
if result.matched == false {
stagingResult := matchReleaseExact(staging, versionRequirement)
if stagingResult.matched {
return stagingResult, nil
}
}
return result, nil
}
func resolveYarn(objects []s3Object, versionRequirement string) (matchResult, error) {
releases := []release{}
......
......@@ -179,3 +179,129 @@ func TestResolveYarn(t *testing.T) {
}
}
}
func genNodeS3ObjectList(releaseVersions []string, stagingVersions []string, platform string) []s3Object {
out := []s3Object{}
for _, version := range releaseVersions {
out = append(out, s3Object{
Key: fmt.Sprintf("node/release/%s/node-v%s-%s.tar.gz", platform, version, platform),
LastModified: time.Time{},
ETag: "abcdef",
Size: 0,
StorageClass: "normal",
})
}
for _, version := range stagingVersions {
out = append(out, s3Object{
Key: fmt.Sprintf("node/staging/%s/node-v%s-%s.tar.gz", platform, version, platform),
LastModified: time.Time{},
ETag: "abcdef",
Size: 0,
StorageClass: "normal",
})
}
return out
}
func TestResolveNode(t *testing.T) {
releasedVersions := []string{
"10.0.0", "10.1.0", "10.10.0", "10.11.0", "10.12.0", "10.13.0", "10.14.0", "10.14.1", "10.14.2", "10.15.0",
"10.15.1", "10.15.2", "10.15.3", "10.2.0", "10.2.1", "10.3.0", "10.4.0", "10.4.1", "10.5.0", "10.6.0",
"10.7.0", "10.8.0", "10.9.0", "11.0.0", "11.1.0", "11.10.0", "11.10.1", "11.11.0", "11.12.0", "11.13.0",
"11.14.0", "11.2.0", "11.3.0", "11.4.0", "11.5.0", "11.6.0", "11.7.0", "11.8.0", "11.9.0", "6.0.0",
"6.1.0", "6.10.0", "6.10.1", "6.10.2", "6.10.3", "6.11.0", "6.11.1", "6.11.2", "6.11.3", "6.11.4",
"6.11.5", "6.12.0", "6.12.1", "6.12.2", "6.12.3", "6.13.0", "6.13.1", "6.14.0", "6.14.1", "6.14.2",
"6.14.3", "6.14.4", "6.15.0", "6.15.1", "6.16.0", "6.17.0", "6.17.1", "6.2.0", "6.2.1", "6.2.2",
"6.3.0", "6.3.1", "6.4.0", "6.5.0", "6.6.0", "6.7.0", "6.8.0", "6.8.1", "6.9.0", "6.9.1", "6.9.2",
"6.9.3", "6.9.4", "6.9.5", "8.0.0", "8.1.0", "8.1.1", "8.1.2", "8.1.3", "8.1.4", "8.10.0", "8.11.0",
"8.11.1", "8.11.2", "8.11.3", "8.11.4", "8.12.0", "8.13.0", "8.14.0", "8.14.1", "8.15.0", "8.15.1",
"8.16.0", "8.2.0", "8.2.1", "8.3.0", "8.4.0", "8.5.0", "8.6.0", "8.7.0", "8.8.0", "8.8.1", "8.9.0",
"8.9.1", "8.9.2", "8.9.3", "8.9.4",
}
objects := genNodeS3ObjectList(releasedVersions, []string{}, "linux-x64")
// Semver requirements pulled from real apps
cases := []Case{
Case{input: "10.x", output: "10.15.3"},
Case{input: "10.*", output: "10.15.3"},
Case{input: "10", output: "10.15.3"},
Case{input: "8.x", output: "8.16.0"},
Case{input: "^8.11.3", output: "8.16.0"},
Case{input: "~8.11.3", output: "8.11.4"},
Case{input: ">= 6.0.0", output: "11.14.0"},
Case{input: "^6.9.0 || ^8.9.0 || ^10.13.0", output: "10.15.3"},
Case{input: "6.* || 8.* || >= 10.*", output: "11.14.0"},
// TODO: these fail to parse with the library
// Case{input: ">= 6.11.1 <= 10", output: "8.16.0"},
// Case{input: ">=8.10 <11", output: "10.15.3"},
}
for _, c := range cases {
result, err := resolveNode(objects, "linux-x64", c.input)
assert.Nil(t, err)
assert.True(t, result.matched)
assert.Equal(t, result.release.version.String(), c.output)
}
for _, c := range cases {
result, err := resolveNode(objects, "darwin-x64", c.input)
assert.Nil(t, err)
assert.False(t, result.matched)
assert.Equal(t, result.versionRequirement, c.input)
}
}
func TestResolveNodeStaging(t *testing.T) {
releasedVersions := []string{
"10.0.0", "10.1.0", "10.10.0", "10.11.0", "10.12.0", "10.13.0", "10.14.0", "10.14.1", "10.14.2", "10.15.0",
"10.15.1", "10.15.2", "10.15.3", "10.2.0", "10.2.1", "10.3.0", "10.4.0", "10.4.1", "10.5.0", "10.6.0",
"10.7.0", "10.8.0", "10.9.0", "11.0.0", "11.1.0", "11.10.0", "11.10.1", "11.11.0", "11.12.0", "11.13.0",
"11.14.0", "11.2.0", "11.3.0", "11.4.0", "11.5.0", "11.6.0", "11.7.0", "11.8.0", "11.9.0", "6.0.0",
"6.1.0", "6.10.0", "6.10.1", "6.10.2", "6.10.3", "6.11.0", "6.11.1", "6.11.2", "6.11.3", "6.11.4",
"6.11.5", "6.12.0", "6.12.1", "6.12.2", "6.12.3", "6.13.0", "6.13.1", "6.14.0", "6.14.1", "6.14.2",
"6.14.3", "6.14.4", "6.15.0", "6.15.1", "6.16.0", "6.17.0", "6.17.1", "6.2.0", "6.2.1", "6.2.2",
"6.3.0", "6.3.1", "6.4.0", "6.5.0", "6.6.0", "6.7.0", "6.8.0", "6.8.1", "6.9.0", "6.9.1", "6.9.2",
"6.9.3", "6.9.4", "6.9.5", "8.0.0", "8.1.0", "8.1.1", "8.1.2", "8.1.3", "8.1.4", "8.10.0", "8.11.0",
"8.11.1", "8.11.2", "8.11.3", "8.11.4", "8.12.0", "8.13.0", "8.14.0", "8.14.1", "8.15.0", "8.15.1",
"8.16.0", "8.2.0", "8.2.1", "8.3.0", "8.4.0", "8.5.0", "8.6.0", "8.7.0", "8.8.0", "8.8.1", "8.9.0",
"8.9.1", "8.9.2", "8.9.3", "8.9.4",
}
platforms := []string{"linux-x64", "darwin-x64"}
for _, platform := range platforms {
// staging has a few releases that were already released, but one: 10.15.4 that has not been
objects := genNodeS3ObjectList(releasedVersions, []string{"10.15.1", "10.15.2", "10.15.3", "10.15.4"}, platform)
result, err := resolveNode(objects, platform, "10.15.1")
if assert.Nil(t, err) {
assert.True(t, result.matched)
assert.Equal(t, result.release.version.String(), "10.15.1")
assert.Equal(t, result.versionRequirement, "10.15.1")
if platform == "linux-x64" {
assert.Equal(t, result.release.url, "https://s3.amazonaws.com/heroku-nodebin/node/release/linux-x64/node-v10.15.1-linux-x64.tar.gz")
} else {
assert.Equal(t, result.release.url, "https://s3.amazonaws.com/heroku-nodebin/node/release/darwin-x64/node-v10.15.1-darwin-x64.tar.gz")
}
}
result, err = resolveNode(objects, platform, "10.15.4")
if assert.Nil(t, err) {
assert.True(t, result.matched)
assert.Equal(t, result.release.version.String(), "10.15.4")
assert.Equal(t, result.versionRequirement, "10.15.4")
if platform == "linux-x64" {
assert.Equal(t, result.release.url, "https://s3.amazonaws.com/heroku-nodebin/node/staging/linux-x64/node-v10.15.4-linux-x64.tar.gz")
} else {
assert.Equal(t, result.release.url, "https://s3.amazonaws.com/heroku-nodebin/node/staging/darwin-x64/node-v10.15.4-darwin-x64.tar.gz")
}
}
result, err = resolveNode(objects, platform, "10.15.5")
if assert.Nil(t, err) {
assert.False(t, result.matched)
assert.Equal(t, result.versionRequirement, "10.15.5")
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment