Skip to content

Commit

Permalink
feat: add support for comparing Red Hat versions locally (#1355)
Browse files Browse the repository at this point in the history
This adds support for comparing versions in the Red Hat ecosystem
locally, in the same manner as the native `rpm` tool does.

Huge shout out to @jantman whose fantastically concise [blog
post](https://blog.jasonantman.com/2014/07/how-yum-and-rpm-compare-versions/#how-rpm-compares-version-parts)
on how the comparator works provided just about everything I needed to
implement this without involvement from other sources - the only thing
it was missing is the caret special character, which I assume was added
(probably long) after that blog post was written.

I did have a peek at his PR for Puppet and some of the original sources
for the manual test fixtures, but I appreciated not having to actively
dig into the C code like I had to with Alpine 😅

Note that for the first time such a pull request for `semantic` does
_not_ include a fixture generator because there are so many
advisories/packages/versions in the osv.dev database that the file came
out as over 1mb, so I'm going to do a dedicated pull request for the
generator which will avoid committing the actual file but instead rely
on the weekly `semantic` runs.

Also, I believe some of the other ecosystems use this format under the
hood (AlmaLinux and SUSE were mentioned by @hogo6002 , and I feel like I
heard somewhere that Rocky Linux also might use `rpm`?); I have
purposely not looked into those since I'm not familiar with this part of
the digital world and ultimately support for them would be best landed
in a dedicated pull request.

Resolves #1336
  • Loading branch information
G-Rath authored Nov 7, 2024
1 parent 9863c2f commit 9fcf53f
Show file tree
Hide file tree
Showing 4 changed files with 407 additions and 0 deletions.
4 changes: 4 additions & 0 deletions internal/semantic/compare_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,10 @@ func TestVersion_Compare_Ecosystems(t *testing.T) {
name: "Alpine",
file: "alpine-versions-generated.txt",
},
{
name: "Red Hat",
file: "redhat-versions.txt",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down
172 changes: 172 additions & 0 deletions internal/semantic/fixtures/redhat-versions.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
1.0 = 1.0
1.0 < 2.0
2.0 > 1.0
2.0.1 = 2.0.1
2.0 < 2.0.1
2.0.1 > 2.0
2.0.1a = 2.0.1a
2.0.1a > 2.0.1
2.0.1 < 2.0.1a
5.5p1 = 5.5p1
5.5p1 < 5.5p2
5.5p2 > 5.5p1
5.5p10 = 5.5p10
5.5p1 < 5.5p10
5.5p10 > 5.5p1
10xyz < 10.1xyz
10.1xyz > 10xyz
xyz10 = xyz10
xyz10 < xyz10.1
xyz10.1 > xyz10
xyz.4 = xyz.4
xyz.4 < 8
8 > xyz.4
xyz.4 < 2
2 > xyz.4
5.5p2 < 5.6p1
5.6p1 > 5.5p2
5.6p1 < 6.5p1
6.5p1 > 5.6p1
6.0.rc1 > 6.0
6.0 < 6.0.rc1
10b2 > 10a1
10a2 < 10b2
1.0aa = 1.0aa
1.0a < 1.0aa
1.0aa > 1.0a
10.0001 = 10.0001
10.0001 = 10.1
10.1 = 10.0001
10.0001 < 10.0039
10.0039 > 10.0001
4.999.9 < 5.0
5.0 > 4.999.9
20101121 = 20101121
20101121 < 20101122
20101122 > 20101121
2_0 = 2_0
2.0 = 2_0
2_0 = 2.0
a = a
a+ = a+
a+ = a_
a_ = a+
+a = +a
+a = _a
_a = +a
+_ = +_
_+ = +_
_+ = _+
+ = _
_ = +
1.0~rc1 = 1.0~rc1

1.0~rc1 < 1.0
1.0 > 1.0~rc1

1.0~rc1 < 1.0~rc2
1.0~rc2 > 1.0~rc1

1.0~rc1~git123 = 1.0~rc1~git123
1.0~rc1~git123 < 1.0~rc1
1.0~rc1 < 1.0arc1

# epochs
1:1 = 1:1
0:1 < 1:1
0:1 < 1:2
0:1~1 < 1:2
3:1~1 > 1:2
1~1 < 1:2
3 < 1:2
13 < 14:2
13:5 < 14:2
13:5 > 04:2
13:5 > 004:2
013:5 > 004:2
130:5 > 004:2
184:2 > 177:5
01:2 = 1:2

a:1 = a:1
a:1 < b:1
a:1 < a:2
a:1~1 < a:2
c:1~1 > a:2

a1:1 = a1:1
a1:1 < b1:1
a1:1 < a1:2
a2:1 > a1:2

b1:1 > a2:1
a:1 < a1:1
b:1 > a1:1
b~1:1 > a1:1

# releases
1-123 > 1-2
1-1 = 1-1
1-2 > 1-1
1 < 1-1
1 < 1-0
1 < 1-
1- = 1-

1-123 > 1-1.el7

# arch
0:3.10.0-229.el7 < 0:3.10.0-229.1.2.ael7b
0:2.4.21-9.EL < 0:2.4.21-9.0.1.EL

0:3.10.0-229.el7 < 0:3.10.0-229.1.2.ael7b
1-1.a > 1-1.
1-1. = 1-1.
1-1. = 1-1
1-1 < 1-1.1
1. = 1
1-1.a = 1-1.a
1-1.a < 1-1.e
1-1.c > 1-1.b
1-1.4 > 1-1.3
1-1.1 < 1-1.2
1-1.ael7b < 1-1.el7
0:3.10.0-229.1.2.ael7b < 0:3.10.0-229.1.2.el7

1 < 1-
0:3.10.0-229.1.2.ael7b < 0:3.10.0-229.4.2.ael7b

1.0^ = 1.0^
1.0^ > 1.0
1.0 < 1.0^
1.0^git1 = 1.0^git1
1.0^git1 > 1.0
1.0 < 1.0^git1
1.0^git1 < 1.0^git2
1.0^git2 > 1.0^git1
1.0^git1 < 1.01
1.01 > 1.0^git1
1.0^20160101 = 1.0^20160101
1.0^20160101 < 1.0.1
1.0.1 > 1.0^20160101
1.0^20160101^git1 = 1.0^20160101^git1
1.0^20160102 > 1.0^20160101^git1
1.0^20160101^git1 < 1.0^20160102

1.0~rc1^git1 = 1.0~rc1^git1
1.0~rc1^git1 > 1.0~rc1
1.0~rc1 < 1.0~rc1^git1
1.0^git1~pre = 1.0^git1~pre
1.0^git1 > 1.0^git1~pre
1.0^git1~pre < 1.0^git1

1.1.α = 1.1.α
1.1.α = 1.1.β
1.1.β = 1.1.α
1.1.αα = 1.1.α
1.1.α = 1.1.ββ
1.1.ββ = 1.1.αα

1.1.a < 1.2.ββ
1.1.a < 0:1.2.ββ
1.1.α < 1.1.a
2 changes: 2 additions & 0 deletions internal/semantic/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ func Parse(str string, ecosystem models.Ecosystem) (Version, error) {
return parseSemverVersion(str), nil
case "PyPI":
return parsePyPIVersion(str), nil
case "Red Hat":
return parseRedHatVersion(str), nil
case "RubyGems":
return parseRubyGemsVersion(str), nil
case "Ubuntu":
Expand Down
Loading

0 comments on commit 9fcf53f

Please sign in to comment.