Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
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
82 changes: 82 additions & 0 deletions 82 compare.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,88 @@ func Only(platform specs.Platform) MatchComparer {
return Ordered(platformVector(Normalize(platform))...)
}

// OnlyOS returns a match comparer that matches only platforms with the same
// OS, OS version, and OS features, regardless of architecture. When comparing,
// it always ranks the best architecture match highest using the default
// platform resolution logic.
func OnlyOS(platform specs.Platform) MatchComparer {
normalized := Normalize(platform)
return onlyOSComparer{
platform: normalized,
osvM: newOSVersionMatcher(normalized),
archOrder: orderedPlatformComparer{
matchers: []Matcher{NewMatcher(normalized)},
},
}
}

func newOSVersionMatcher(platform specs.Platform) osVerMatcher {
if platform.OS == "windows" {
return &windowsVersionMatcher{
windowsOSVersion: getWindowsOSVersion(platform.OSVersion),
}
}
return nil
}

type onlyOSComparer struct {
platform specs.Platform
osvM osVerMatcher
archOrder orderedPlatformComparer
}

func (c onlyOSComparer) matchOS(platform specs.Platform) bool {
normalized := Normalize(platform)
if c.platform.OS != normalized.OS {
return false
}
if c.osvM != nil {
if !c.osvM.Match(platform.OSVersion) {
return false
}
}
if len(normalized.OSFeatures) > 0 {
if len(c.platform.OSFeatures) < len(normalized.OSFeatures) {
return false
}
j := 0
for _, feature := range normalized.OSFeatures {
found := false
for ; j < len(c.platform.OSFeatures); j++ {
if feature == c.platform.OSFeatures[j] {
found = true
j++
break
}
if feature < c.platform.OSFeatures[j] {
return false
}
}
if !found {
return false
}
}
}
return true
}

func (c onlyOSComparer) Match(platform specs.Platform) bool {
return c.matchOS(platform)
}

func (c onlyOSComparer) Less(p1, p2 specs.Platform) bool {
p1m := c.matchOS(p1)
p2m := c.matchOS(p2)
if p1m && !p2m {
return true
}
if !p1m {
return false
}
// Both match — rank by architecture preference
return c.archOrder.Less(p1, p2)
}

// OnlyStrict returns a match comparer for a single platform.
//
// Unlike Only, OnlyStrict does not match sub platforms.
Expand Down
120 changes: 120 additions & 0 deletions 120 compare_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,126 @@ func TestOnlyStrict(t *testing.T) {
}
}

func TestOnlyOS(t *testing.T) {
for _, tc := range []struct {
platform string
matches map[bool][]string
}{
{
platform: "linux/amd64",
matches: map[bool][]string{
true: {
"linux/amd64",
"linux/arm64",
"linux/arm/v7",
"linux/386",
},
false: {
"windows/amd64",
"darwin/arm64",
},
},
},
{
platform: "windows(10.0.17763)/amd64",
matches: map[bool][]string{
true: {
"windows/amd64",
"windows/arm64",
"windows(10.0.17763)/amd64",
"windows(10.0.17763)/arm64",
},
false: {
"linux/amd64",
},
},
},
{
platform: "linux(+gpu)/amd64",
matches: map[bool][]string{
true: {
"linux/arm64",
"linux(+gpu)/amd64",
},
false: {
"windows/amd64",
"linux(+gpu+simd)/amd64",
},
},
},
} {
testcase := tc
t.Run(testcase.platform, func(t *testing.T) {
p, err := Parse(testcase.platform)
if err != nil {
t.Fatal(err)
}
m := OnlyOS(p)
for shouldMatch, platforms := range testcase.matches {
for _, matchPlatform := range platforms {
mp, err := Parse(matchPlatform)
if err != nil {
t.Fatal(err)
}
if match := m.Match(mp); shouldMatch != match {
t.Errorf("OnlyOS(%q).Match(%q) should return %v, but returns %v", testcase.platform, matchPlatform, shouldMatch, match)
}
}
}
})
}
}

func TestOnlyOSLess(t *testing.T) {
for _, tc := range []struct {
platform string
platforms []string
expected []string
}{
{
// Exact architecture match ranks first, others are unordered but before non-OS matches
platform: "linux/amd64",
platforms: []string{"linux/arm64", "linux/386", "linux/amd64", "windows/amd64"},
expected: []string{"linux/amd64", "linux/arm64", "linux/386", "windows/amd64"},
},
{
// Strict: only exact arch/variant match is preferred
platform: "linux/arm64",
platforms: []string{"linux/amd64", "linux/arm/v7", "linux/arm64", "windows/arm64"},
expected: []string{"linux/arm64", "linux/amd64", "linux/arm/v7", "windows/arm64"},
},
{
// Non-matching OS should always sort last
platform: "linux/amd64",
platforms: []string{"windows/amd64", "darwin/amd64", "linux/arm64", "linux/amd64"},
expected: []string{"linux/amd64", "linux/arm64", "windows/amd64", "darwin/amd64"},
},
} {
testcase := tc
t.Run(testcase.platform, func(t *testing.T) {
p, err := Parse(testcase.platform)
if err != nil {
t.Fatal(err)
}
mc := OnlyOS(p)
platforms, err := ParseAll(testcase.platforms)
if err != nil {
t.Fatal(err)
}
sort.Slice(platforms, func(i, j int) bool {
return mc.Less(platforms[i], platforms[j])
})
actual := make([]string, len(platforms))
for i, ps := range platforms {
actual[i] = FormatAll(ps)
}
if !reflect.DeepEqual(testcase.expected, actual) {
t.Errorf("Wrong platform order:\nExpected: %#v\nActual: %#v", testcase.expected, actual)
}
})
}
}

func TestCompareOSFeatures(t *testing.T) {
for _, tc := range []struct {
platform string
Expand Down
Loading
Morty Proxy This is a proxified and sanitized view of the page, visit original site.