-
Notifications
You must be signed in to change notification settings - Fork 551
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Alex Goodman <[email protected]>
- Loading branch information
Showing
9 changed files
with
461 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package version | ||
|
||
import "fmt" | ||
|
||
var _ Constraint = (*jvmConstraint)(nil) | ||
|
||
type jvmConstraint struct { | ||
raw string | ||
expression constraintExpression | ||
} | ||
|
||
func newJvmConstraint(raw string) (jvmConstraint, error) { | ||
constraints, err := newConstraintExpression(raw, newJvmComparator) | ||
if err != nil { | ||
return jvmConstraint{}, err | ||
} | ||
return jvmConstraint{ | ||
expression: constraints, | ||
raw: raw, | ||
}, nil | ||
} | ||
|
||
func (g jvmConstraint) String() string { | ||
if g.raw == "" { | ||
return "none (jvm)" | ||
} | ||
return fmt.Sprintf("%s (jvm)", g.raw) | ||
} | ||
|
||
func (g jvmConstraint) Satisfied(version *Version) (bool, error) { | ||
if g.raw == "" { | ||
return true, nil // the empty constraint is always satisfied | ||
} | ||
return g.expression.satisfied(version) | ||
} | ||
|
||
func newJvmComparator(unit constraintUnit) (Comparator, error) { | ||
ver, err := newJvmVersion(unit.version) | ||
if err != nil { | ||
return nil, fmt.Errorf("unable to parse constraint version (%s): %w", unit.version, err) | ||
} | ||
return ver, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
package version | ||
|
||
import ( | ||
"github.com/stretchr/testify/require" | ||
"testing" | ||
) | ||
|
||
func TestVersionConstraintJVM(t *testing.T) { | ||
tests := []testCase{ | ||
// pre jep 223 versions | ||
{version: "1.7.0_80", constraint: "< 1.8.0", satisfied: true}, | ||
{version: "1.8.0_131", constraint: "> 1.8.0", satisfied: true}, | ||
{version: "1.8.0_131", constraint: "< 1.8.0_132", satisfied: true}, | ||
{version: "1.8.0_131-b11", constraint: "< 1.8.0_132", satisfied: true}, | ||
|
||
{version: "1.7.0_80", constraint: "> 1.8.0", satisfied: false}, | ||
{version: "1.8.0_131", constraint: "< 1.8.0", satisfied: false}, | ||
{version: "1.8.0_131", constraint: "> 1.8.0_132", satisfied: false}, | ||
{version: "1.8.0_131-b11", constraint: "> 1.8.0_132", satisfied: false}, | ||
|
||
{version: "1.7.0_80", constraint: "= 1.8.0", satisfied: false}, | ||
{version: "1.8.0_131", constraint: "= 1.8.0", satisfied: false}, | ||
{version: "1.8.0_131", constraint: "= 1.8.0_132", satisfied: false}, | ||
{version: "1.8.0_131-b11", constraint: "= 1.8.0_132", satisfied: false}, | ||
|
||
{version: "1.8.0_80", constraint: "= 1.8.0_80", satisfied: true}, | ||
{version: "1.8.0_131", constraint: ">= 1.8.0_131", satisfied: true}, | ||
{version: "1.8.0_131", constraint: "= 1.8.0_131-b001", satisfied: true}, // builds should not matter | ||
{version: "1.8.0_131-ea-b11", constraint: "= 1.8.0_131-ea", satisfied: true}, | ||
|
||
// jep 223 versions | ||
{version: "8.0.4", constraint: "> 8.0.3", satisfied: true}, | ||
{version: "8.0.4", constraint: "< 8.0.5", satisfied: true}, | ||
{version: "9.0.0", constraint: "> 8.0.5", satisfied: true}, | ||
{version: "9.0.0", constraint: "< 9.1.0", satisfied: true}, | ||
{version: "11.0.4", constraint: "<= 11.0.4", satisfied: true}, | ||
{version: "11.0.5", constraint: "> 11.0.4", satisfied: true}, | ||
|
||
{version: "8.0.4", constraint: "< 8.0.3", satisfied: false}, | ||
{version: "8.0.4", constraint: "> 8.0.5", satisfied: false}, | ||
{version: "9.0.0", constraint: "< 8.0.5", satisfied: false}, | ||
{version: "9.0.0", constraint: "> 9.1.0", satisfied: false}, | ||
{version: "11.0.4", constraint: "> 11.0.4", satisfied: false}, | ||
{version: "11.0.5", constraint: "< 11.0.4", satisfied: false}, | ||
|
||
// mixed versions | ||
{version: "1.8.0_131", constraint: "< 9.0.0", satisfied: true}, // 1.8.0_131 -> 8.0.131 | ||
{version: "9.0.0", constraint: "> 1.8.0_131", satisfied: true}, // 1.8.0_131 -> 8.0.131 | ||
{version: "1.8.0_131", constraint: "<= 8.0.131", satisfied: true}, | ||
{version: "1.8.0_131", constraint: "> 7.0.79", satisfied: true}, | ||
{version: "1.8.0_131", constraint: "= 8.0.131", satisfied: true}, | ||
{version: "1.8.0_131", constraint: ">= 9.0.0", satisfied: false}, | ||
{version: "9.0.1", constraint: "< 8.0.131", satisfied: false}, | ||
|
||
// pre-release versions | ||
{version: "1.8.0_131-ea", constraint: "< 1.8.0_131", satisfied: true}, | ||
{version: "1.8.0_131", constraint: "> 1.8.0_131-ea", satisfied: true}, | ||
{version: "9.0.0-ea", constraint: "< 9.0.0", satisfied: true}, | ||
{version: "9.0.0-ea", constraint: "> 1.8.0_131", satisfied: true}, | ||
} | ||
|
||
for _, test := range tests { | ||
t.Run(test.version+"_constraint_"+test.constraint, func(t *testing.T) { | ||
constraint, err := newJvmConstraint(test.constraint) | ||
require.NoError(t, err) | ||
test.assertVersionConstraint(t, JVMFormat, constraint) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
package version | ||
|
||
import ( | ||
"fmt" | ||
hashiVer "github.com/anchore/go-version" | ||
"github.com/anchore/grype/internal" | ||
"github.com/anchore/grype/internal/log" | ||
"regexp" | ||
"strings" | ||
) | ||
|
||
var _ Comparator = (*jvmVersion)(nil) | ||
|
||
var preJep223VersionPattern = regexp.MustCompile(`^1\.(?P<major>\d+)(\.(?P<minor>\d+)(_(?P<patch>\d+))?(-(?P<prerelease>[^b][^-]+))?(-b(?P<build>\d+))?)?`) | ||
|
||
type jvmVersion struct { | ||
isPreJep223 bool | ||
semVer *hashiVer.Version | ||
} | ||
|
||
func newJvmVersion(raw string) (*jvmVersion, error) { | ||
isPreJep233 := strings.HasPrefix(raw, "1.") | ||
|
||
if isPreJep233 { | ||
// convert the pre-JEP 223 version to semver | ||
raw = convertPreJep223Version(raw) | ||
} | ||
verObj, err := hashiVer.NewVersion(raw) | ||
if err != nil { | ||
return nil, fmt.Errorf("unable to create semver obj for JVM version: %w", err) | ||
} | ||
|
||
return &jvmVersion{ | ||
isPreJep223: isPreJep233, | ||
semVer: verObj, | ||
}, nil | ||
} | ||
|
||
func (v *jvmVersion) Compare(other *Version) (int, error) { | ||
if other.Format != JVMFormat { | ||
return -1, fmt.Errorf("unable to compare JVM to given format: %s", other.Format) | ||
} | ||
|
||
if other.rich.jvmVersion == nil { | ||
return -1, fmt.Errorf("given empty jvmVersion object") | ||
} | ||
|
||
return other.rich.jvmVersion.compare(*v), nil | ||
} | ||
|
||
func (v jvmVersion) compare(other jvmVersion) int { | ||
return v.semVer.Compare(other.semVer) | ||
} | ||
|
||
func convertPreJep223Version(version string) string { | ||
// convert the following pre JEP 223 version strings to semvers | ||
// 1.8.0_302-b08 --> 8.0.302+8 | ||
// 1.9.0-ea-b19 --> 9.0.0-ea+19 | ||
// NOTE: this makes an assumption that the old update field is the patch version in semver... | ||
// this is NOT strictly in the spec, but for 1.8 this tends to be true (especially for temurin-based builds) | ||
version = strings.TrimSpace(version) | ||
|
||
matches := internal.MatchNamedCaptureGroups(preJep223VersionPattern, version) | ||
if len(matches) == 0 { | ||
log.WithFields("version", version).Trace("unable to convert pre-JEP 223 JVM version") | ||
return version | ||
} | ||
|
||
// extract relevant parts from the matches | ||
majorVersion := trim0sFromLeft(matches["major"]) | ||
minorVersion := trim0sFromLeft(matches["minor"]) | ||
patchVersion := trim0sFromLeft(matches["patch"]) | ||
preRelease := trim0sFromLeft(matches["prerelease"]) | ||
build := trim0sFromLeft(matches["build"]) | ||
|
||
if minorVersion == "" { | ||
minorVersion = "0" | ||
} | ||
if patchVersion == "" { | ||
patchVersion = "0" | ||
} | ||
|
||
// build the semver string | ||
var semver strings.Builder | ||
semver.WriteString(fmt.Sprintf("%s.%s.%s", majorVersion, minorVersion, patchVersion)) | ||
|
||
if preRelease != "" { | ||
semver.WriteString(fmt.Sprintf("-%s", preRelease)) | ||
} | ||
if build != "" { | ||
semver.WriteString(fmt.Sprintf("+%s", build)) | ||
} | ||
|
||
return semver.String() | ||
} | ||
|
||
func trim0sFromLeft(v string) string { | ||
if v == "0" { | ||
return v | ||
} | ||
return strings.TrimLeft(v, "0") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
package version | ||
|
||
import ( | ||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
"testing" | ||
) | ||
|
||
func TestVersionJVM(t *testing.T) { | ||
tests := []struct { | ||
v1 string | ||
v2 string | ||
expected int | ||
}{ | ||
// pre jep223 versions | ||
{"1.8", "1.8.0", 0}, | ||
{"1.8.0", "1.8.0_0", 0}, | ||
{"1.8.0", "1.8.0", 0}, | ||
{"1.7.0", "1.8.0", -1}, | ||
{"1.8.0_131", "1.8.0_131", 0}, | ||
{"1.8.0_131", "1.8.0_132", -1}, | ||
|
||
// builds should not matter | ||
{"1.8.0_131", "1.8.0_130", 1}, | ||
{"1.8.0_131", "1.8.0_132-b11", -1}, | ||
{"1.8.0_131-b11", "1.8.0_132-b11", -1}, | ||
{"1.8.0_131-b11", "1.8.0_131-b12", 0}, | ||
{"1.8.0_131-b11", "1.8.0_131-b10", 0}, | ||
{"1.8.0_131-b11", "1.8.0_131", 0}, | ||
{"1.8.0_131-b11", "1.8.0_131-b11", 0}, | ||
|
||
// jep223 versions (semver) | ||
{"8.0.4", "8.0.4", 0}, | ||
{"8.0.4", "8.0.5", -1}, | ||
{"8.0.4", "8.0.3", 1}, | ||
{"8.0.4", "8.0.4+b1", 0}, | ||
|
||
// mix comparison | ||
{"1.8.0_131", "8.0.4", 1}, // 1.8.0_131 --> 8.0.131 | ||
{"8.0.4", "1.8.0_131", -1}, // doesn't matter which side the comparison is on | ||
{"1.8.0_131-b002", "8.0.131+b2", 0}, // builds should not matter | ||
{"1.8.0_131-b002", "8.0.131+b1", 0}, // builds should not matter | ||
{"1.6.0", "8.0.1", -1}, // 1.6.0 --> 6.0.0 | ||
|
||
// prerelease | ||
{"1.8.0_13-ea-b002", "1.8.0_13-ea-b001", 0}, | ||
{"1.8.0_13-ea", "1.8.0_13-ea-b001", 0}, | ||
{"1.8.0_13-ea-b002", "8.0.13-ea+b2", 0}, | ||
{"1.8.0_13-ea-b002", "8.0.13+b2", -1}, | ||
{"1.8.0_13-b002", "8.0.13-ea+b2", 1}, | ||
|
||
// pre 1.8 (when the jep 223 was introduced) | ||
{"1.7.0", "7.0.0", 0}, // there is no v7 of the JVM, but we want to honor this comparison since it may be someone mistakenly using the wrong version format | ||
} | ||
|
||
for _, test := range tests { | ||
name := test.v1 + "_vs_" + test.v2 | ||
t.Run(name, func(t *testing.T) { | ||
v1, err := newJvmVersion(test.v1) | ||
require.NotNil(t, v1) | ||
require.NoError(t, err) | ||
|
||
v2, err := newJvmVersion(test.v2) | ||
require.NotNil(t, v2) | ||
require.NoError(t, err) | ||
|
||
actual := v1.compare(*v2) | ||
assert.Equal(t, test.expected, actual) | ||
}) | ||
} | ||
} | ||
|
||
func TestVersionJVM_invalid(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
version string | ||
wantErr require.ErrorAssertionFunc | ||
}{ | ||
{ | ||
name: "outside of pre jep223 major version range", | ||
version: "2.8.0_131-b11", | ||
wantErr: require.Error, | ||
}, | ||
{ | ||
name: "invalid version", | ||
version: "1.a", | ||
wantErr: require.Error, | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
if tt.wantErr == nil { | ||
tt.wantErr = require.NoError | ||
} | ||
v, err := newJvmVersion(tt.version) | ||
assert.Nil(t, v) | ||
tt.wantErr(t, err) | ||
}) | ||
} | ||
} |
Oops, something went wrong.