-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* ⭐ epoch support in version Add support for epochs in MQL's `semver` type. ```mql > semver("1:1.2.3") < semver("2:1.0.0") true ``` This works for both deb/rpm-style epochs ("NN:version") and python-style epochs ([PEP440](https://peps.python.org/pep-0440/)). Additionally you can access the epoch: ```mql > semver("5:1.2.3").epoch 5 > semver("3!2.1").epoch 3 ``` You can compare versions with and without epochs as well: ```mql > semver("1:1.0") > semver("2009.12.03") true ``` Note: Technically epochs aren't semver, so the name `semver` is hitting its limit here. We could introduce the `version` type as a replacement or as a temporary holdover (because we also don't want to clutter the global namespace too much). Internal note: This required a new wrapper around the version, which can now be extended with more metadata. Currently we have the epoch stored. Signed-off-by: Dominik Richter <[email protected]> * 🧹 soft-deprecate semver in favor of version The soft part of this deprecation is that we don't show a deprecation warning anywhere at all. So semver is still technically included, since we released it in v9.x, but we will deprecate it formally with the next major release. --------- Signed-off-by: Dominik Richter <[email protected]>
- Loading branch information
Showing
12 changed files
with
347 additions
and
169 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
This file was deleted.
Oops, something went wrong.
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,142 @@ | ||
// Copyright (c) Mondoo, Inc. | ||
// SPDX-License-Identifier: BUSL-1.1 | ||
|
||
package llx | ||
|
||
import ( | ||
"regexp" | ||
"strconv" | ||
|
||
"github.com/Masterminds/semver" | ||
"go.mondoo.com/cnquery/v11/types" | ||
) | ||
|
||
// Version type, an abstract representation of a software version. | ||
// It is designed to parse and compare version strings. | ||
// It is built on semver and adds support for epochs (deb, rpm, python). | ||
type Version struct { | ||
*semver.Version | ||
src string | ||
epoch int | ||
} | ||
|
||
var reEpoch = regexp.MustCompile("^[0-9]+[:!]") | ||
|
||
func NewVersion(s string) Version { | ||
epoch := 0 | ||
|
||
x, err := semver.NewVersion(s) | ||
if err != nil { | ||
x, epoch = parseEpoch(s) | ||
} | ||
|
||
return Version{ | ||
Version: x, | ||
src: s, | ||
epoch: epoch, | ||
} | ||
} | ||
|
||
func parseEpoch(v string) (*semver.Version, int) { | ||
prefix := reEpoch.FindString(v) | ||
if prefix == "" { | ||
return nil, 0 | ||
} | ||
|
||
remainder := v[len(prefix):] | ||
epochStr := v[:len(prefix)-1] | ||
res, err := semver.NewVersion(remainder) | ||
if err != nil { | ||
return nil, 0 | ||
} | ||
|
||
// invalid epoch means we discard the entire version string | ||
epoch, err := strconv.Atoi(epochStr) | ||
if err != nil { | ||
return nil, 0 | ||
} | ||
|
||
return res, epoch | ||
} | ||
|
||
// Compare compares this version to another one. It returns -1, 0, or 1 if | ||
// the version smaller, equal, or larger than the other version. | ||
// | ||
// Versions are compared by X.Y.Z. Build metadata is ignored. Prerelease is | ||
// lower than the version without a prerelease. | ||
func (v Version) Compare(o Version) int { | ||
if v.epoch != o.epoch { | ||
return v.epoch - o.epoch | ||
} | ||
if v.Version == nil || o.Version == nil { | ||
if v.src < o.src { | ||
return -1 | ||
} else if v.src > o.src { | ||
return 1 | ||
} | ||
return 0 | ||
} | ||
|
||
return v.Version.Compare(o.Version) | ||
} | ||
|
||
func versionLT(left interface{}, right interface{}) *RawData { | ||
l := NewVersion(left.(string)) | ||
r := NewVersion(right.(string)) | ||
return BoolData(l.Compare(r) < 0) | ||
} | ||
|
||
func versionGT(left interface{}, right interface{}) *RawData { | ||
l := NewVersion(left.(string)) | ||
r := NewVersion(right.(string)) | ||
return BoolData(l.Compare(r) > 0) | ||
} | ||
|
||
func versionLTE(left interface{}, right interface{}) *RawData { | ||
l := NewVersion(left.(string)) | ||
r := NewVersion(right.(string)) | ||
return BoolData(l.Compare(r) <= 0) | ||
} | ||
|
||
func versionGTE(left interface{}, right interface{}) *RawData { | ||
l := NewVersion(left.(string)) | ||
r := NewVersion(right.(string)) | ||
return BoolData(l.Compare(r) >= 0) | ||
} | ||
|
||
func versionCmpVersion(e *blockExecutor, bind *RawData, chunk *Chunk, ref uint64) (*RawData, uint64, error) { | ||
return nonNilDataOpV2(e, bind, chunk, ref, types.Bool, func(left, right interface{}) *RawData { | ||
return BoolData(left.(string) == right.(string)) | ||
}) | ||
} | ||
|
||
func versionNotVersion(e *blockExecutor, bind *RawData, chunk *Chunk, ref uint64) (*RawData, uint64, error) { | ||
return nonNilDataOpV2(e, bind, chunk, ref, types.Bool, func(left, right interface{}) *RawData { | ||
return BoolData(left.(string) != right.(string)) | ||
}) | ||
} | ||
|
||
func versionLTversion(e *blockExecutor, bind *RawData, chunk *Chunk, ref uint64) (*RawData, uint64, error) { | ||
return nonNilDataOpV2(e, bind, chunk, ref, types.Bool, versionLT) | ||
} | ||
|
||
func versionGTversion(e *blockExecutor, bind *RawData, chunk *Chunk, ref uint64) (*RawData, uint64, error) { | ||
return nonNilDataOpV2(e, bind, chunk, ref, types.Bool, versionGT) | ||
} | ||
|
||
func versionLTEversion(e *blockExecutor, bind *RawData, chunk *Chunk, ref uint64) (*RawData, uint64, error) { | ||
return nonNilDataOpV2(e, bind, chunk, ref, types.Bool, versionLTE) | ||
} | ||
|
||
func versionGTEversion(e *blockExecutor, bind *RawData, chunk *Chunk, ref uint64) (*RawData, uint64, error) { | ||
return nonNilDataOpV2(e, bind, chunk, ref, types.Bool, versionGTE) | ||
} | ||
|
||
func versionEpoch(e *blockExecutor, bind *RawData, chunk *Chunk, ref uint64) (*RawData, uint64, error) { | ||
if bind.Value == nil { | ||
return &RawData{Type: types.Int, Error: bind.Error}, 0, nil | ||
} | ||
|
||
v := NewVersion(bind.Value.(string)) | ||
return IntData(v.epoch), 0, nil | ||
} |
Oops, something went wrong.