diff --git a/README.md b/README.md index 25d5849b..e5b6c807 100644 --- a/README.md +++ b/README.md @@ -217,11 +217,11 @@ Without a parameter, the version to use is resolved automatically (see resolutio If a parameter is passed, available options include: -- an exact [Semver 2.0.0](https://semver.org/) version string to install -- a [version constraint](https://opentofu.org/docs/language/expressions/version-constraints) string (checked against versions available at `_REMOTE` url) -- `latest`, `latest-stable` (old name of `latest`) or `latest-pre` (include unstable version), which are checked against versions available at `_REMOTE` url) -- `latest-allowed` or `min-required` to scan your IAC files to detect which version is maximally allowed or minimally required. - See [required_version](#required_version) docs. +- an exact [Semver 2.0.0](https://semver.org/) version string to install. +- a [version constraint](https://opentofu.org/docs/language/expressions/version-constraints) string (checked against versions available at `_REMOTE` url). +- `latest`, `latest-stable` (old name of `latest`) or `latest-pre` (include unstable version), which are checked against versions available at `_REMOTE` url. +- `latest:` or `min:` to get first version matching with `` as a [regexp](https://github.com/google/re2/wiki/Syntax) after a descending or ascending version sort. +- `latest-allowed` or `min-required` to scan your IAC files to detect which version is maximally allowed or minimally required. See [required_version](#required_version) docs. ```console tenv tofu install @@ -258,12 +258,11 @@ Switch the default tool version to use (set in `TENV_ROOT//version` file). Available parameter options: -- an exact [Semver 2.0.0](https://semver.org/) version string to use -- a [version constraint](https://opentofu.org/docs/language/expressions/version-constraints) string (checked against versions available in TENV_ROOT directory) -- `latest`, `latest-stable` (old name of `latest`) or `latest-pre` (include unstable version), which are checked against versions available in TENV_ROOT directory -- `latest-allowed` or `min-required` to scan your IAC files to detect which version is maximally allowed or minimally required. - -See [required_version](#required_version) docs. +- an exact [Semver 2.0.0](https://semver.org/) version string to use. +- a [version constraint](https://opentofu.org/docs/language/expressions/version-constraints) string (checked against versions available in TENV_ROOT directory). +- `latest`, `latest-stable` (old name of `latest`) or `latest-pre` (include unstable version), which are checked against versions available in TENV_ROOT directory. +- `latest:` or `min:` to get first version matching with `` as a [regexp](https://github.com/google/re2/wiki/Syntax) after a descending or ascending version sort. +- `latest-allowed` or `min-required` to scan your IAC files to detect which version is maximally allowed or minimally required. See [required_version](#required_version) docs. ```console tenv tofu use v1.6.0-beta5 diff --git a/cmd/tenv/subcmd.go b/cmd/tenv/subcmd.go index 416e771b..1c50687c 100644 --- a/cmd/tenv/subcmd.go +++ b/cmd/tenv/subcmd.go @@ -120,9 +120,9 @@ If a parameter is passed, available options: descBuilder.WriteString(params.remoteEnvName) descBuilder.WriteString(" url)\n- latest, latest-stable or latest-pre (checked against version available at ") descBuilder.WriteString(params.remoteEnvName) - descBuilder.WriteString(" url)\n- latest-allowed or min-required to scan your ") + descBuilder.WriteString(" url)\n- latest: or min: to get first version matching with as a regexp after a version sort\n- latest-allowed or min-required to scan your ") descBuilder.WriteString(versionManager.FolderName) - descBuilder.WriteString(" files to detect which version is maximally allowed or minimally required.") + descBuilder.WriteString(" files to detect which version is maximally allowed or minimally required") installCmd := &cobra.Command{ Use: "install [version]", @@ -324,9 +324,10 @@ Available parameter options: - an exact Semver 2.0.0 version string to use - a version constraint expression (checked against version available in TENV_ROOT directory) - latest, latest-stable or latest-pre (checked against version available in TENV_ROOT directory) +- latest: or min: to get first version matching with as a regexp after a version sort - latest-allowed or min-required to scan your `) descBuilder.WriteString(versionManager.FolderName) - descBuilder.WriteString(" files to detect which version is maximally allowed or minimally required.") + descBuilder.WriteString(" files to detect which version is maximally allowed or minimally required") workingDir := false diff --git a/versionmanager/semantic/semantic.go b/versionmanager/semantic/semantic.go index d9488121..b17d68c0 100644 --- a/versionmanager/semantic/semantic.go +++ b/versionmanager/semantic/semantic.go @@ -19,6 +19,9 @@ package semantic import ( + "regexp" + "strings" + "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-version" "github.com/tofuutils/tenv/config" @@ -33,6 +36,9 @@ const ( LatestStableKey = "latest-stable" LatestKey = "latest" MinRequiredKey = "min-required" + + LatestPrefix = "latest:" + MinPrefix = "min:" ) var TfPredicateReaders = []types.PredicateReader{readTfFiles} //nolint @@ -56,12 +62,12 @@ func CmpVersion(v1Str string, v2Str string) int { func ParsePredicate(behaviourOrConstraint string, displayName string, constraintInfo types.ConstraintInfo, predicateReaders []types.PredicateReader, conf *config.Config) (types.PredicateInfo, error) { reverseOrder := true - switch behaviourOrConstraint { - case MinRequiredKey: + switch { + case behaviourOrConstraint == MinRequiredKey: reverseOrder = false // start with older fallthrough // same predicate retrieving - case LatestAllowedKey: + case behaviourOrConstraint == LatestAllowedKey: for _, reader := range predicateReaders { predicate, err := reader(constraintInfo, conf) if err != nil { @@ -75,10 +81,23 @@ func ParsePredicate(behaviourOrConstraint string, displayName string, constraint conf.Displayer.Display(loghelper.Concat("No ", displayName, " version requirement found in project files, fallback to ", LatestKey, " strategy")) fallthrough // fallback to latest - case LatestKey, LatestStableKey: + case behaviourOrConstraint == LatestKey, behaviourOrConstraint == LatestStableKey: return types.PredicateInfo{Predicate: StableVersion, ReverseOrder: true}, nil - case LatestPreKey: + case behaviourOrConstraint == LatestPreKey: return types.PredicateInfo{Predicate: alwaysTrue, ReverseOrder: true}, nil + case strings.HasPrefix(behaviourOrConstraint, MinPrefix): + reverseOrder = false // start with older + + fallthrough // same behaviour + case strings.HasPrefix(behaviourOrConstraint, LatestPrefix): + conf.Displayer.Display("Use of regexp is discouraged, try version constraint instead") + + re, err := regexp.Compile(behaviourOrConstraint[strings.Index(behaviourOrConstraint, ":")+1:]) + if err != nil { + return types.PredicateInfo{}, err + } + + return types.PredicateInfo{Predicate: re.MatchString, ReverseOrder: reverseOrder}, nil default: constraint, err := addDefaultConstraint(constraintInfo, conf, behaviourOrConstraint) if err != nil {