Skip to content
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
34 changes: 18 additions & 16 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,22 @@ on: [push, pull_request]
name: Test
jobs:
test:
strategy:
matrix:
go-version: [1.14.x, 1.15.x]
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
steps:
- name: Install Go
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go-version }}
- name: Checkout code
uses: actions/checkout@v2
- name: golangci-lint
uses: golangci/golangci-lint-action@v2
with:
version: v1.32
- name: Test
run: go test ./...

- name: Checkout code
uses: actions/checkout@v4

- name: Install Go
uses: actions/setup-go@v5
with:
go-version-file: 'go.mod'

- name: golangci-lint
uses: golangci/[email protected]
with:
version: v2.1
args: --verbose

- name: Test
run: go test ./...
11 changes: 10 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
module github.com/aquasecurity/go-npm-version

go 1.15
go 1.24

require (
github.com/aquasecurity/go-version v0.0.0-20201107203531-5e48ac5d022a
github.com/stretchr/testify v1.6.1
github.com/urfave/cli/v2 v2.3.0
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
)

require (
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d // indirect
github.com/davecgh/go-spew v1.1.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/russross/blackfriday/v2 v2.0.1 // indirect
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
)
67 changes: 39 additions & 28 deletions pkg/constraint.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ var (
validConstraintRegexp *regexp.Regexp
)

type operatorFunc func(v, c Version) bool
type operatorFunc func(v, c Version, conf conf) bool

func init() {
ops := make([]string, 0, len(constraintOperators))
Expand All @@ -54,7 +54,10 @@ func init() {

// Constraints is one or more constraint that a npm version can be
// checked against.
type Constraints [][]constraint
type Constraints struct {
constraints [][]constraint
conf conf
}

type constraint struct {
version Version
Expand All @@ -63,7 +66,13 @@ type constraint struct {
}

// NewConstraints parses the given string and returns an instance of Constraints
func NewConstraints(v string) (Constraints, error) {
func NewConstraints(v string, opts ...ConstraintOption) (Constraints, error) {
config := new(conf)
// Apply options
for _, o := range opts {
o.apply(config)
}

var css [][]constraint
for _, vv := range strings.Split(v, "||") {
// Validate the segment
Expand All @@ -87,8 +96,10 @@ func NewConstraints(v string) (Constraints, error) {
css = append(css, cs)
}

return css, nil

return Constraints{
constraints: css,
conf: *config,
}, nil
}

func newConstraint(c string) (constraint, error) {
Expand Down Expand Up @@ -126,9 +137,9 @@ func newPart(p string) part.Part {
return part.NewPart(p)
}

func (c constraint) check(v Version) bool {
func (c constraint) check(v Version, conf conf) bool {
op := preCheck(c.operator)
return op(v, c.version)
return op(v, c.version, conf)
}

func (c constraint) String() string {
Expand All @@ -137,8 +148,8 @@ func (c constraint) String() string {

// Check tests if a version satisfies all the constraints.
func (cs Constraints) Check(v Version) bool {
for _, c := range cs {
if andCheck(v, c) {
for _, c := range cs.constraints {
if andCheck(v, c, cs.conf) {
return true
}
}
Expand All @@ -149,7 +160,7 @@ func (cs Constraints) Check(v Version) bool {
// Returns the string format of the constraints
func (cs Constraints) String() string {
var csStr []string
for _, orC := range cs {
for _, orC := range cs.constraints {
var cstr []string
for _, andC := range orC {
cstr = append(cstr, andC.String())
Expand All @@ -160,9 +171,9 @@ func (cs Constraints) String() string {
return strings.Join(csStr, "||")
}

func andCheck(v Version, constraints []constraint) bool {
func andCheck(v Version, constraints []constraint, conf conf) bool {
for _, c := range constraints {
if !c.check(v) {
if !c.check(v, conf) {
return false
}
}
Expand All @@ -173,52 +184,52 @@ func andCheck(v Version, constraints []constraint) bool {
// Constraint functions
//-------------------------------------------------------------------

func constraintEqual(v, c Version) bool {
func constraintEqual(v, c Version, _ conf) bool {
return v.Equal(c)
}

func constraintGreaterThan(v, c Version) bool {
if c.IsPreRelease() && v.IsPreRelease() {
func constraintGreaterThan(v, c Version, conf conf) bool {
if !conf.includePreRelease && (c.IsPreRelease() && v.IsPreRelease()) {
return v.Release().Equal(c.Release()) && v.GreaterThan(c)
}
return v.GreaterThan(c)
}

func constraintLessThan(v, c Version) bool {
if c.IsPreRelease() && v.IsPreRelease() {
func constraintLessThan(v, c Version, conf conf) bool {
if !conf.includePreRelease && (c.IsPreRelease() && v.IsPreRelease()) {
return v.Release().Equal(c.Release()) && v.LessThan(c)
}
return v.LessThan(c)
}

func constraintGreaterThanEqual(v, c Version) bool {
if c.IsPreRelease() && v.IsPreRelease() {
func constraintGreaterThanEqual(v, c Version, conf conf) bool {
if !conf.includePreRelease && (c.IsPreRelease() && v.IsPreRelease()) {
return v.Release().Equal(c.Release()) && v.GreaterThanOrEqual(c)
}
return v.GreaterThanOrEqual(c)
}

func constraintLessThanEqual(v, c Version) bool {
if c.IsPreRelease() && v.IsPreRelease() {
func constraintLessThanEqual(v, c Version, conf conf) bool {
if !conf.includePreRelease && (c.IsPreRelease() && v.IsPreRelease()) {
return v.Release().Equal(c.Release()) && v.LessThanOrEqual(c)
}
return v.LessThanOrEqual(c)
}

func constraintTilde(v, c Version) bool {
func constraintTilde(v, c Version, conf conf) bool {
// ~*, ~>* --> >= 0.0.0 (any)
// ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0, <3.0.0
// ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0, <2.1.0
// ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0, <1.3.0
// ~1.2.3, ~>1.2.3 --> >=1.2.3, <1.3.0
// ~1.2.0, ~>1.2.0 --> >=1.2.0, <1.3.0
if c.IsPreRelease() && v.IsPreRelease() {
if !conf.includePreRelease && (c.IsPreRelease() && v.IsPreRelease()) {
return v.GreaterThanOrEqual(c) && v.LessThan(c.Release())
}
return v.GreaterThanOrEqual(c) && v.LessThan(c.TildeBump())
}

func constraintCaret(v, c Version) bool {
func constraintCaret(v, c Version, conf conf) bool {
// ^* --> (any)
// ^1.2.3 --> >=1.2.3 <2.0.0
// ^1.2 --> >=1.2.0 <2.0.0
Expand All @@ -228,19 +239,19 @@ func constraintCaret(v, c Version) bool {
// ^0.0.3 --> >=0.0.3 <0.0.4
// ^0.0 --> >=0.0.0 <0.1.0
// ^0 --> >=0.0.0 <1.0.0
if c.IsPreRelease() && v.IsPreRelease() {
if !conf.includePreRelease && (c.IsPreRelease() && v.IsPreRelease()) {
return v.GreaterThanOrEqual(c) && v.LessThan(c.Release())
}
return v.GreaterThanOrEqual(c) && v.LessThan(c.CaretBump())
}

func preCheck(f operatorFunc) operatorFunc {
return func(v, c Version) bool {
if v.IsPreRelease() && !c.IsPreRelease() {
return func(v, c Version, conf conf) bool {
if !conf.includePreRelease && (v.IsPreRelease() && !c.IsPreRelease()) {
return false
} else if c.IsPreRelease() && c.IsAny() {
return false
}
return f(v, c)
return f(v, c, conf)
}
}
15 changes: 15 additions & 0 deletions pkg/constraint_option.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package npm

type conf struct {
includePreRelease bool
}

type ConstraintOption interface {
apply(*conf)
}

type WithPreRelease bool

func (o WithPreRelease) apply(c *conf) {
c.includePreRelease = bool(o)
}
71 changes: 71 additions & 0 deletions pkg/constraint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,74 @@ func TestConstraints_Check(t *testing.T) {
})
}
}

func TestConstraints_CheckWithIncludePreRelease(t *testing.T) {
tests := []struct {
version string
constraint string
want bool
}{
{"1.4.4-lts.1", ">=1.4.4-lts.1, <2.0.0", true},
{"1.4.5-lts.1", ">=1.4.4-lts.1, <2.0.0", true},
{"1.4.5", ">=1.4.4-lts.1, <2.0.0", true},

// Auto-generated tests with pre-release option
{"1.2.3-alpha", "<1.2.3-beta", true},
{"1.2.2-alpha", "<1.2.3-beta", true},
{"1.2.2", "<1.2.3-beta", true},
{"0.0.0-alpha", "<0", false},
{"0.0.0-alpha", "<0-z", false},
{"0.0.0-alpha", "<0.0.0-z", true},
{"1.2.3-beta", "<=1.2.3-beta", true},
{"1.2.3-alpha", "<=1.2.3-beta", true},
{"1.2.2-alpha", "<=1.2.3-beta", true},
{"0.0.1-alpha", ">0", false},
{"0.0.1-alpha", ">0.0", false},
{"0.0.1-alpha", ">0-0", false},
{"0.0.1-alpha", ">0.0-0", false},
{"0.0.0-alpha", ">0", false},
{"0.0.0-alpha", ">0-0", false},
{"0.0.0-alpha", ">0.0.0-0", true},
{"0.0.0-alpha", ">0.0.0-0", true},
{"1.2.3-alpha.2", ">1.2.3-alpha.1", true},
{"1.3.3-alpha.2", ">1.2.3-alpha.1", true},
{"0.0.1-alpha", ">=0", true},
{"0.0.1-alpha", ">=0.0", true},
{"0.0.1-alpha", ">=0-0", false},
{"0.0.1-alpha", ">=0.0-0", false},
{"0.0.0-alpha", ">=0", true},
{"0.0.0-alpha", ">=0-0", false},
{"0.0.0-alpha", ">=0.0.0-0", true},
{"3.4.5-beta.1", ">=0.0.0-0", true},
{"1.2.3-alpha.1", ">=1.2.3-alpha.1", true},
{"1.2.3-alpha.2", ">=1.2.3-alpha.1", true},
{"1.3.3-alpha.2", ">=1.2.3-alpha.1", true},
{"1.2.3-alpha.1", "", true},
{"0.3.0-alpha", "~0.2", true},
{"1.0.0-beta", "~0", true},
{"1.0.1", "~0", false},
{"1.2.4-beta.2", "~1.2.3-beta.2", true},
{"1.3.4-beta.2", "~1.2.3-beta.2", false},
{"1.2.1-alpha.1", "^1.2.0", true},
{"1.2.1-alpha.1", "^1.2.0-alpha.0", true},
{"1.2.1-alpha.0", "^1.2.0-alpha.0", true},
{"1.2.0-alpha.1", "^1.2.0-alpha.2", false},
{"0.2.3-beta.4", "^0.2.3-beta.2", true},
{"0.2.4-beta.2", "^0.2.3-beta.2", true},
{"0.3.4-beta.2", "^0.2.3-beta.2", false},
{"0.2.3-beta.2", "^0.2.3-beta.2", true},
}

for _, tc := range tests {
t.Run(fmt.Sprintf("%s vs %s", tc.constraint, tc.version), func(t *testing.T) {
c, err := NewConstraints(tc.constraint, WithPreRelease(true))
require.NoError(t, err)

v, err := NewVersion(tc.version)
require.NoError(t, err)

got := c.Check(v)
assert.Equal(t, tc.want, got)
})
}
}