diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e10625..f4d7e3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ ## Unreleased +## 0.6.0 + +### Added +- The `provider::semvers::constrained` function for checking semver constrains + +### Changed +- updating google.golang.org/grpc v1.68.1 -> v1.69.0 + ## 0.5.5 ### Changed @@ -28,7 +36,7 @@ ## 0.5.1 ### Added -- The `provider::semvers::equals` function for checking equality of two semver +- The `provider::semvers::equals` function for checking equality of two semver ### Fixed - spacing consistency @@ -38,7 +46,7 @@ ## 0.5.0 ### Added -- The `provider::semvers::compare` function for comparing semver strings +- The `provider::semvers::compare` function for comparing semver strings - Tests covering invalid values ## 0.4.2 @@ -54,7 +62,7 @@ ## 0.4.0 ### Added -- The `provider::semvers::pick` function which takes list of semver strings, +- The `provider::semvers::pick` function which takes list of semver strings, and semver constraint, and returns a list of filtered semver strings, sorted and deduped, matching the constraint. See [Masterminds/semver](https://github.com/Masterminds/semver/tree/master?tab=readme-ov-file#checking-version-constraints) for constraint syntax. @@ -69,7 +77,7 @@ ## 0.3.0 ### Added -- The `provider::semvers::sort` function which takes list of semver strings, +- The `provider::semvers::sort` function which takes list of semver strings, and returns a list of semver strings, sorted and deduped ## 0.2.1 diff --git a/README.md b/README.md index e17815f..7d24a0f 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ Implements a data-source `semvers_list` to make semver sorting easy in TF, and provider functions (TF >= 1.8 is required): - `data.semvers_list`: sorts a list of semver strings - `provider::semvers::compare`: compares two semver strings +- `provider::semvers::constrained`: checks whether semver is within constrains - `provider::semvers::equals`: checks two semver strings for equality - `provider::semvers::pick`: filters a list of semver strings by constraint - `provider::semvers::sort`: sorts a list of semver strings, returns sorted one diff --git a/docs/functions/constrained.md b/docs/functions/constrained.md new file mode 100644 index 0000000..f55cd2a --- /dev/null +++ b/docs/functions/constrained.md @@ -0,0 +1,69 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "constrained function - semvers" +subcategory: "" +description: |- + Checks whether semver is within constrain, returns a boolean +--- + +# function: constrained + +Checks whether semver is within constrain, returns a boolean + +## Example Usage + +```terraform +terraform { + required_providers { + semvers = { + source = "anapsix/semvers" + } + } +} + +provider "semvers" {} + +output "semvers_constrained_results" { + value = [ + { + expected = true + arguments = "0.1.1, >= 0.1" + result = provider::semvers::constrained("0.1.1", ">= 0.1") + }, + { + expected = false + arguments = "0.1, >= 0.1.1" + result = provider::semvers::constrained("0.1", ">= 0.1.1") + }, + { + expected = true + arguments = "0.1.2, ~> 0.1" + result = provider::semvers::constrained("0.1.2", "~> 0.1") + }, + { + expected = false + arguments = "0.2, ~> 0.1" + result = provider::semvers::constrained("0.2", "~> 0.1") + }, + { + expected = true + arguments = "0.2, 0.1.1 - 0.2.0" + result = provider::semvers::constrained("0.2", "0.1.1 - 0.2.0") + }, + ] +} +``` + +## Signature + + +```text +constrained(version string, constraint string) bool +``` + +## Arguments + + +1. `version` (String) Semver string version used as base for comparison +1. `constraint` (String) Constraint string + diff --git a/docs/index.md b/docs/index.md index f7a933e..dd8dede 100644 --- a/docs/index.md +++ b/docs/index.md @@ -3,12 +3,12 @@ page_title: "semvers Provider" subcategory: "" description: |- - Implements semvers_list data source, sort and pick provider function. All functionality is based on github.com/Masterminds/semver/v3. Usage of provider functions requires Terraform version 1.8 and above. + Implements semvers_list data source, compare, constrained, equals, sort and pick provider function. All functionality is based on github.com/Masterminds/semver/v3. Usage of provider functions requires Terraform version 1.8 and above. --- # semvers Provider -Implements `semvers_list` data source, `sort` and `pick` provider function. All functionality is based on `github.com/Masterminds/semver/v3`. Usage of provider functions requires Terraform version 1.8 and above. +Implements `semvers_list` data source, `compare`, `constrained`, `equals`, `sort` and `pick` provider function. All functionality is based on `github.com/Masterminds/semver/v3`. Usage of provider functions requires Terraform version 1.8 and above. ## Example Usage diff --git a/examples/functions/constrained/function.tf b/examples/functions/constrained/function.tf new file mode 100644 index 0000000..6d8ed6a --- /dev/null +++ b/examples/functions/constrained/function.tf @@ -0,0 +1,39 @@ +terraform { + required_providers { + semvers = { + source = "anapsix/semvers" + } + } +} + +provider "semvers" {} + +output "semvers_constrained_results" { + value = [ + { + expected = true + arguments = "0.1.1, >= 0.1" + result = provider::semvers::constrained("0.1.1", ">= 0.1") + }, + { + expected = false + arguments = "0.1, >= 0.1.1" + result = provider::semvers::constrained("0.1", ">= 0.1.1") + }, + { + expected = true + arguments = "0.1.2, ~> 0.1" + result = provider::semvers::constrained("0.1.2", "~> 0.1") + }, + { + expected = false + arguments = "0.2, ~> 0.1" + result = provider::semvers::constrained("0.2", "~> 0.1") + }, + { + expected = true + arguments = "0.2, 0.1.1 - 0.2.0" + result = provider::semvers::constrained("0.2", "0.1.1 - 0.2.0") + }, + ] +} diff --git a/go.mod b/go.mod index 2d4c66f..42e6842 100644 --- a/go.mod +++ b/go.mod @@ -78,7 +78,7 @@ require ( golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 // indirect - google.golang.org/grpc v1.68.1 // indirect + google.golang.org/grpc v1.69.0 // indirect google.golang.org/protobuf v1.35.2 // indirect gopkg.in/yaml.v2 v2.3.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 87e18e9..ac4ce64 100644 --- a/go.sum +++ b/go.sum @@ -48,6 +48,10 @@ github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+ github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow= github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys= github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= @@ -206,6 +210,16 @@ github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6 github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM= go.abhg.dev/goldmark/frontmatter v0.2.0 h1:P8kPG0YkL12+aYk2yU3xHv4tcXzeVnN+gU0tJ5JnxRw= go.abhg.dev/goldmark/frontmatter v0.2.0/go.mod h1:XqrEkZuM57djk7zrlRUB02x8I5J0px76YjkOzhB4YlU= +go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY= +go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE= +go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE= +go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY= +go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk= +go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0= +go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc= +go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8= +go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys= +go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= @@ -266,8 +280,8 @@ google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAs google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 h1:8ZmaLZE4XWrtU3MyClkYqqtl6Oegr3235h7jxsDyqCY= google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= -google.golang.org/grpc v1.68.1 h1:oI5oTa11+ng8r8XMMN7jAOmWfPZWbYpCFaMUTACxkM0= -google.golang.org/grpc v1.68.1/go.mod h1:+q1XYFJjShcqn0QZHvCyeR4CXPA+llXIeUIfIe00waw= +google.golang.org/grpc v1.69.0 h1:quSiOM1GJPmPH5XtU+BCoVXcDVJJAzNcoyfC2cCjGkI= +google.golang.org/grpc v1.69.0/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= diff --git a/internal/helpers/shelper.go b/internal/helpers/shelper.go index bdf5821..a764f02 100644 --- a/internal/helpers/shelper.go +++ b/internal/helpers/shelper.go @@ -117,3 +117,17 @@ func PickFromSemverStrings(list []string, contraint string) ([]string, error) { return semvers_filtered, nil } + +func Constrained(version string, constraint string) (bool, error) { + c, err := semver.NewConstraint(constraint) + if err != nil { + return false, err + } + + v, err := semver.NewVersion(version) + if err != nil { + return false, err + } + + return c.Check(v), nil +} diff --git a/internal/provider/function_constrained.go b/internal/provider/function_constrained.go new file mode 100644 index 0000000..c3232d2 --- /dev/null +++ b/internal/provider/function_constrained.go @@ -0,0 +1,68 @@ +// Copyright (c) HashiCorp, Inc. +// Copyright (c) Anastas Dancha +// SPDX-License-Identifier: MPL-2.0 + +package provider + +import ( + "context" + + shelper "github.com/anapsix/terraform-provider-semvers/internal/helpers" + "github.com/hashicorp/terraform-plugin-framework/function" +) + +var ( + _ function.Function = SemversConstrainedFunction{} +) + +func NewSemversConstrainedFunction() function.Function { + return SemversConstrainedFunction{} +} + +type SemversConstrainedFunction struct{} + +func (r SemversConstrainedFunction) Metadata(_ context.Context, req function.MetadataRequest, resp *function.MetadataResponse) { + resp.Name = "constrained" +} + +func (r SemversConstrainedFunction) Definition(_ context.Context, _ function.DefinitionRequest, resp *function.DefinitionResponse) { + resp.Definition = function.Definition{ + Summary: "Checks whether semver is within constrain, returns a boolean", + MarkdownDescription: "Checks whether semver is within constrain, returns a boolean", + Parameters: []function.Parameter{ + function.StringParameter{ + AllowNullValue: false, + AllowUnknownValues: false, + Name: "version", + MarkdownDescription: "Semver string version used as base for comparison", + }, + function.StringParameter{ + AllowNullValue: false, + AllowUnknownValues: false, + Name: "constraint", + MarkdownDescription: "Constraint string", + }, + }, + Return: function.BoolReturn{}, + } +} + +func (r SemversConstrainedFunction) Run(ctx context.Context, req function.RunRequest, resp *function.RunResponse) { + var version string + var constraint string + var compare_results bool + + resp.Error = function.ConcatFuncErrors(req.Arguments.Get(ctx, &version, &constraint)) + if resp.Error != nil { + return + } + + compare_results, err := shelper.Constrained(version, constraint) + + if err != nil { + resp.Error = function.ConcatFuncErrors(resp.Error, function.NewFuncError("Error performing operation: "+err.Error())) + return + } + + resp.Error = function.ConcatFuncErrors(resp.Error, resp.Result.Set(ctx, compare_results)) +} diff --git a/internal/provider/function_constrained_test.go b/internal/provider/function_constrained_test.go new file mode 100644 index 0000000..57b0062 --- /dev/null +++ b/internal/provider/function_constrained_test.go @@ -0,0 +1,86 @@ +// Copyright (c) HashiCorp, Inc. +// Copyright (c) Anastas Dancha +// SPDX-License-Identifier: MPL-2.0 + +package provider + +import ( + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/knownvalue" + "github.com/hashicorp/terraform-plugin-testing/statecheck" + "github.com/hashicorp/terraform-plugin-testing/tfversion" +) + +func TestSemversConstrainedFunction_Known(t *testing.T) { + t.Parallel() + + resource.UnitTest(t, resource.TestCase{ + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.SkipBelow(tfversion.Version1_8_0), + }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: `output "results_one" { + value = provider::semvers::constrained("0.1.1", ">= 0.1") + } + output "results_two" { + value = provider::semvers::constrained("0.1", ">= 0.1.1") + } + output "results_three" { + value = provider::semvers::constrained("0.1.2", "~> 0.1") + } + output "results_four" { + value = provider::semvers::constrained("0.2", "~> 0.1") + } + output "results_five" { + value = provider::semvers::constrained("0.2", "0.1.1 - 0.2.0") + }`, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownOutputValue( + "results_one", + knownvalue.Bool(true), + ), + statecheck.ExpectKnownOutputValue( + "results_two", + knownvalue.Bool(false), + ), + statecheck.ExpectKnownOutputValue( + "results_three", + knownvalue.Bool(true), + ), + statecheck.ExpectKnownOutputValue( + "results_four", + knownvalue.Bool(false), + ), + statecheck.ExpectKnownOutputValue( + "results_five", + knownvalue.Bool(true), + ), + }, + }, + }, + }) +} + +func TestSemversConstrainedFunction_Invalid(t *testing.T) { + t.Parallel() + + resource.UnitTest(t, resource.TestCase{ + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.SkipBelow(tfversion.Version1_8_0), + }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: `output "results_one" { + value = provider::semvers::constrained("0.1.1", "blah") + }`, + ExpectError: regexp.MustCompile(`improper constraint`), + }, + }, + }) +} diff --git a/internal/provider/provider.go b/internal/provider/provider.go index d8678bd..d8e57d3 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -41,7 +41,7 @@ func (p *semversProvider) Metadata(ctx context.Context, req provider.MetadataReq // Schema defines the provider-level schema for configuration data. func (p *semversProvider) Schema(_ context.Context, _ provider.SchemaRequest, resp *provider.SchemaResponse) { resp.Schema = schema.Schema{ - Description: "Implements `semvers_list` data source, `sort` and `pick` provider function. All functionality is based on `github.com/Masterminds/semver/v3`. Usage of provider functions requires Terraform version 1.8 and above. ", + Description: "Implements `semvers_list` data source, `compare`, `constrained`, `equals`, `sort` and `pick` provider function. All functionality is based on `github.com/Masterminds/semver/v3`. Usage of provider functions requires Terraform version 1.8 and above.", Attributes: nil, } } @@ -69,5 +69,6 @@ func (p *semversProvider) Functions(_ context.Context) []func() function.Functio NewSemversPickFunction, NewSemversCompareFunction, NewSemversEqualsFunction, + NewSemversConstrainedFunction, } } diff --git a/test/main.tf b/test/main.tf index 0662bcc..e06a7b5 100644 --- a/test/main.tf +++ b/test/main.tf @@ -145,3 +145,33 @@ output "last_local" { output "last_local_noprerelease" { value = local.local_last_no_prerelease } + +output "semvers_constrained_results" { + value = [ + { + expected = true + arguments = "0.1.1, >= 0.1" + result = provider::semvers::constrained("0.1.1", ">= 0.1") + }, + { + expected = false + arguments = "0.1, >= 0.1.1" + result = provider::semvers::constrained("0.1", ">= 0.1.1") + }, + { + expected = true + arguments = "0.1.2, ~> 0.1" + result = provider::semvers::constrained("0.1.2", "~> 0.1") + }, + { + expected = false + arguments = "0.2, ~> 0.1" + result = provider::semvers::constrained("0.2", "~> 0.1") + }, + { + expected = true + arguments = "0.2, 0.1.1 - 0.2.0" + result = provider::semvers::constrained("0.2", "0.1.1 - 0.2.0") + }, + ] +}