* Working tests

* More tests, a bugfix (yay tests) and docs

* Changed filter description adding v prefix to versions

* Updated docs to be more precise

* More robust, changed docs

* Fixed copy paste bug
This commit is contained in:
Federico Paolinelli
2019-03-19 06:07:27 +01:00
committed by Aaron Schlesinger
parent 2816e01ce5
commit 04c6bbed90
4 changed files with 196 additions and 18 deletions
+23
View File
@@ -68,3 +68,26 @@ D enterprise.github.com/company
</pre>
In the above example, any module not in the rules will be excluded. All modules from `enterprise.github.com/company` are fetched directly from the source. The `github.com/gomods/athens` module will be stored in the proxy storage, but only for version `v0.4.1` and any patch versions under `v0.1` and `v0.2` minor versions.
### Versions Filter Modifiers
Athens provides advanced filter modifiers to cover cases such as API compatibility or when a given dependency changes its license from a given versions. The modifiers are intended to be used in the pattern list of the filter file.
<pre>
-
# external dependency approved list
+ github.com/gomods/athens <v1.2.3
</pre>
The currently supported modifiers are
* `~1.2.3` will enable all patch versions from 1.2.3 and above (e.g. 1.2.3, 1.2.4, 1.2.5)
* Formally, `1.2.x` where `x >= 3`
* `^1.2.3` will enable all patch and minor versions from 1.2.3 and above (e.g. 1.2.4, 1.3.0 and 1.4.5)
* Formally, `1.x.y` where `x >= 2` and `y >= 3`
* `<1.2.3` will enable all versions lower than 1.2.3 (e.g. 1.2.2, 1.0.0 and 0.58.9)
* Formally, `x.y.z` where `x <= 1`, `y < = 2` and `z < 3`
This kind of modifiers will work only if a three parts semantic version is specified. For example, `~4.5.6` will work while `~4.5` won't.
+101 -15
View File
@@ -10,7 +10,8 @@ import (
)
var (
pathSeparator = "/"
pathSeparator = "/"
versionSeparator = "."
)
// Filter is a filter of modules
@@ -42,7 +43,7 @@ func NewFilter(filterFilePath string) (*Filter, error) {
}
// AddRule adds rule for specified path
func (f *Filter) AddRule(path string, versions []string, rule FilterRule) {
func (f *Filter) AddRule(path string, qualifiers []string, rule FilterRule) {
f.ensurePath(path)
segments := getPathSegments(path)
@@ -62,7 +63,7 @@ func (f *Filter) AddRule(path string, versions []string, rule FilterRule) {
last := segments[len(segments)-1]
rn := latest.next[last]
rn.rule = rule
rn.vers = versions
rn.qualifiers = qualifiers
latest.next[last] = rn
}
@@ -102,9 +103,9 @@ func (f *Filter) getAssociatedRule(version string, path ...string) FilterRule {
}
rn = rn.next[p]
// default to true if no version filter, false otherwise
match := len(rn.vers) == 0
for _, ver := range rn.vers {
if strings.HasPrefix(version, ver) {
match := len(rn.qualifiers) == 0
for _, q := range rn.qualifiers {
if matches(version, q) {
match = true
break
}
@@ -172,30 +173,115 @@ func initFromConfig(filePath string) (*Filter, error) {
f.AddRule("", nil, rule)
continue
}
var vers []string
var qual []string
if len(split) == 3 {
vers = strings.Split(split[2], ",")
for i := range vers {
vers[i] = strings.TrimRight(vers[i], "*")
if vers[i][len(vers[i])-1] != '.' && strings.Count(vers[i], ".") < 2 {
vers[i] += "."
qual = strings.Split(split[2], ",")
for i := range qual {
qual[i] = strings.TrimRight(qual[i], "*")
if qual[i][len(qual[i])-1] != '.' && strings.Count(qual[i], ".") < 2 {
qual[i] += "."
}
}
}
path := strings.TrimSpace(split[1])
f.AddRule(path, vers, rule)
f.AddRule(path, qual, rule)
}
return f, nil
}
// matches checks if the given version matches the given qualifier.
// Qualifiers can be:
// - plain versions
// - v1.2.3 enables v1.2.3
// - ~1.2.3: enables 1.2.x which are at least 1.2.3
// - ^1.2.3: enables 1.x.x which are at least 1.2.3
// - <1.2.3: enables everything lower than 1.2.3 includes 1.2.2 and 0.58.9 as well
func matches(version, qualifier string) bool {
if len(qualifier) < 2 || len(version) < 1 {
return false
}
prefix := qualifier[0]
first := qualifier[1]
// v1.2.3 means we accept every version starting with v1.2.3
// handle this special case first, then go for ~v1.2.3 and similar
if prefix == 'v' && first >= '0' && first <= '9' { // a number
return strings.HasPrefix(version, qualifier)
}
v, err := getVersionSegments(version[1:])
if err != nil {
return false
}
q, err := getVersionSegments(qualifier[2:])
if err != nil {
return false
}
if len(v) != len(q) {
return false
}
// no semver
if len(v) != 3 || len(q) != 3 {
return false
}
switch prefix {
case '~':
if v[0] == q[0] && v[1] == q[1] && v[2] >= q[2] {
return true
}
return false
case '^':
if v[0] == q[0] && v[1] > q[1] {
return true
}
if v[0] == q[0] && v[1] == q[1] && v[2] >= q[2] {
return true
}
return false
case '<':
if v[0] < q[0] {
return true
}
if v[0] == q[0] && v[1] < q[1] {
return true
}
if v[0] == q[0] && v[1] == q[1] && v[2] <= q[2] {
return true
}
return false
}
return false
}
func getPathSegments(path string) []string {
return getSegments(path, pathSeparator)
}
func getVersionSegments(path string) ([]int, error) {
vv := getSegments(path, versionSeparator)
res := make([]int, len(vv))
for i, v := range vv {
n, err := strconv.Atoi(v)
if err != nil {
return nil, err
}
res[i] = n
}
return res, nil
}
func getSegments(path, separator string) []string {
path = strings.TrimSpace(path)
path = strings.Trim(path, pathSeparator)
path = strings.Trim(path, separator)
if path == "" {
return []string{}
}
return strings.Split(path, pathSeparator)
return strings.Split(path, separator)
}
func newRule(r FilterRule) ruleNode {
+3 -3
View File
@@ -1,7 +1,7 @@
package module
type ruleNode struct {
next map[string]ruleNode
rule FilterRule
vers []string
next map[string]ruleNode
rule FilterRule
qualifiers []string
}
+69
View File
@@ -133,6 +133,75 @@ func (t *FilterTests) Test_versionFilter() {
r.Equal(Include, f.Rule("github.com/a/b/c/d", "v1.3.4"))
}
func (t *FilterTests) Test_versionFilterMinor() {
r := t.Require()
filter := tempFilterFile(t.T())
defer os.Remove(filter)
f, err := NewFilter(filter)
r.NoError(err)
f.AddRule("", nil, Exclude)
f.AddRule("github.com/a/b", []string{"~v1.2.3", "~v2.3.40"}, Include)
r.Equal(Include, f.Rule("github.com/a/b", "v1.2.3"))
r.Equal(Include, f.Rule("github.com/a/b", "v1.2.5"))
r.Equal(Exclude, f.Rule("github.com/a/b", "v1.2.2"))
r.Equal(Exclude, f.Rule("github.com/a/b", "v1.3.3"))
r.Equal(Include, f.Rule("github.com/a/b", "v2.3.45"))
r.Equal(Exclude, f.Rule("github.com/a/b", "v2.2.45"))
r.Equal(Exclude, f.Rule("github.com/a/b", "v2.3.20"))
}
func (t *FilterTests) Test_versionFilterMiddle() {
r := t.Require()
filter := tempFilterFile(t.T())
defer os.Remove(filter)
f, err := NewFilter(filter)
r.NoError(err)
f.AddRule("", nil, Exclude)
f.AddRule("github.com/a/b", []string{"^v1.2.3", "^v2.3.40"}, Include)
r.Equal(Include, f.Rule("github.com/a/b", "v1.2.3"))
r.Equal(Include, f.Rule("github.com/a/b", "v1.2.5"))
r.Equal(Include, f.Rule("github.com/a/b", "v1.4.2"))
r.Equal(Exclude, f.Rule("github.com/a/b", "v1.2.1"))
r.Equal(Include, f.Rule("github.com/a/b", "v2.3.45"))
r.Equal(Include, f.Rule("github.com/a/b", "v2.4.1"))
r.Equal(Exclude, f.Rule("github.com/a/b", "v2.2.45"))
}
func (t *FilterTests) Test_versionFilterLess() {
r := t.Require()
filter := tempFilterFile(t.T())
defer os.Remove(filter)
f, err := NewFilter(filter)
r.NoError(err)
f.AddRule("", nil, Exclude)
f.AddRule("github.com/a/b", []string{"<v2.3.40"}, Include)
r.Equal(Include, f.Rule("github.com/a/b", "v1.2.3"))
r.Equal(Include, f.Rule("github.com/a/b", "v1.4.2"))
r.Equal(Include, f.Rule("github.com/a/b", "v1.2.1"))
r.Equal(Include, f.Rule("github.com/a/b", "v2.3.39"))
r.Equal(Include, f.Rule("github.com/a/b", "v2.2.45"))
r.Equal(Exclude, f.Rule("github.com/a/b", "v2.4.1"))
}
func (t *FilterTests) Test_versionFilterRobust() {
r := t.Require()
filter := tempFilterFile(t.T())
defer os.Remove(filter)
f, err := NewFilter(filter)
r.NoError(err)
f.AddRule("", nil, Exclude)
f.AddRule("github.com/a/b", []string{"abcd"}, Include)
f.AddRule("github.com/c/d", []string{"e"}, Include)
r.Equal(Exclude, f.Rule("github.com/a/b", "a"))
r.Equal(Exclude, f.Rule("github.com/c/d", "fg"))
}
func (t *FilterTests) Test_initFromConfig() {
r := t.Require()
filterFile := tempFilterFile(t.T())