diff --git a/.gitignore b/.gitignore index 3cd0ead..04e81b9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ **/.DS_Store **/.terraform.lock.hcl **/.terraform +**/.vscode **/terraform.tfplan **/terraform.tfstate **/terraform.tfstate.backup diff --git a/CHANGELOG.md b/CHANGELOG.md index f1adbf3..54df4d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## 0.4.0 + +### Added: +- 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. + ## 0.3.1 ### Fixed @@ -12,7 +19,6 @@ - The `provider::semvers::sort` function which takes list of semver strings, and returns a list of semver strings, sorted and deduped - ## 0.2.1 ### Added: diff --git a/docs/data-sources/list.md b/docs/data-sources/list.md index 37acad1..b352de4 100644 --- a/docs/data-sources/list.md +++ b/docs/data-sources/list.md @@ -1,6 +1,6 @@ --- # generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "semvers_list Data Source - terraform-provider-semvers" +page_title: "semvers_list Data Source - semvers" subcategory: "" description: |- Parses and sorts a list of semver strings. diff --git a/docs/functions/pick.md b/docs/functions/pick.md new file mode 100644 index 0000000..baeea2c --- /dev/null +++ b/docs/functions/pick.md @@ -0,0 +1,26 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "pick function - semvers" +subcategory: "" +description: |- + Returns semver from list of semvers according to contraint +--- + +# function: pick + +Returns semver from list of semvers according to contraint + + + +## Signature + + +```text +pick(versions list of string, constraint string) list of string +``` + +## Arguments + + +1. `versions` (List of String) List of semver strigs +1. `constraint` (String) Semver constraint diff --git a/docs/functions/sort.md b/docs/functions/sort.md index 6666603..17020e4 100644 --- a/docs/functions/sort.md +++ b/docs/functions/sort.md @@ -1,6 +1,6 @@ --- # generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "sort function - terraform-provider-semvers" +page_title: "sort function - semvers" subcategory: "" description: |- Returns sorted list of semver strings diff --git a/go.mod b/go.mod index 39ed5ce..f095458 100644 --- a/go.mod +++ b/go.mod @@ -68,17 +68,17 @@ require ( github.com/yuin/goldmark-meta v1.1.0 // indirect github.com/zclconf/go-cty v1.15.0 // indirect go.abhg.dev/goldmark/frontmatter v0.2.0 // indirect - golang.org/x/crypto v0.26.0 // indirect + golang.org/x/crypto v0.27.0 // indirect golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df // indirect golang.org/x/mod v0.19.0 // indirect - golang.org/x/net v0.28.0 // indirect + golang.org/x/net v0.29.0 // indirect golang.org/x/sync v0.8.0 // indirect golang.org/x/sys v0.25.0 // indirect golang.org/x/text v0.18.0 // indirect 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-20240903143218-8af14fe29dc1 // indirect - google.golang.org/grpc v1.66.0 // indirect + google.golang.org/grpc v1.66.2 // indirect google.golang.org/protobuf v1.34.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 82f12d4..c13cd45 100644 --- a/go.sum +++ b/go.sum @@ -211,6 +211,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= +golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= +golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME= golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -223,6 +225,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -268,6 +272,8 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1: google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= google.golang.org/grpc v1.66.0 h1:DibZuoBznOxbDQxRINckZcUvnCEvrW9pcWIE2yF9r1c= google.golang.org/grpc v1.66.0/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= +google.golang.org/grpc v1.66.2 h1:3QdXkuq3Bkh7w+ywLdLvM56cmGvQHUMZpiCzt6Rqaoo= +google.golang.org/grpc v1.66.2/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= 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.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= diff --git a/internal/helpers/shelper.go b/internal/helpers/shelper.go index 67e13c7..b331d51 100644 --- a/internal/helpers/shelper.go +++ b/internal/helpers/shelper.go @@ -1,8 +1,9 @@ package shelper import ( - "github.com/Masterminds/semver/v3" "sort" + + "github.com/Masterminds/semver/v3" ) // RemoveDups removes duplicate versions from a list of semver.Version pointers @@ -54,3 +55,33 @@ func StringsToStrings(list []string) ([]string, error) { } return SemversToStrings(semvers), nil } + +func PickFromSemverStrings(list []string, contraint string) ([]string, error) { + var semvers_filtered []string + semvers_list, err := StringsToSemvers(list) + if err != nil { + return nil, err + } + semver_compare, err := semver.NewConstraint(contraint) + if err != nil { + return nil, err + } + + for _, v := range semvers_list { + match := semver_compare.Check(v) + // match, msgs := semver_compare.Validate(v) + // for _, m := range msgs { + // fmt.Println(m) + // } + if match { + semvers_filtered = append(semvers_filtered, v.String()) + } + } + + if len(semvers_filtered) == 0 { + var empty_results []string + return empty_results, nil + } + + return semvers_filtered, nil +} diff --git a/internal/provider/function_pick.go b/internal/provider/function_pick.go new file mode 100644 index 0000000..2214ef6 --- /dev/null +++ b/internal/provider/function_pick.go @@ -0,0 +1,76 @@ +// 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" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-log/tflog" +) + +var ( + _ function.Function = SemversPickFunction{} +) + +func NewSemversPickFunction() function.Function { + return SemversPickFunction{} +} + +type SemversPickFunction struct{} + +func (r SemversPickFunction) Metadata(_ context.Context, req function.MetadataRequest, resp *function.MetadataResponse) { + resp.Name = "pick" +} + +func (r SemversPickFunction) Definition(_ context.Context, _ function.DefinitionRequest, resp *function.DefinitionResponse) { + resp.Definition = function.Definition{ + Summary: "Returns semver from list of semvers according to contraint", + MarkdownDescription: "Returns semver from list of semvers according to contraint", + Parameters: []function.Parameter{ + function.ListParameter{ + AllowNullValue: false, + AllowUnknownValues: false, + ElementType: types.StringType, + Name: "versions", + MarkdownDescription: "List of semver strigs", + }, + function.StringParameter{ + AllowNullValue: false, + AllowUnknownValues: false, + Name: "constraint", + MarkdownDescription: "Semver constraint", + }, + }, + Return: function.ListReturn{ + ElementType: types.StringType, + }, + } +} + +func (r SemversPickFunction) Run(ctx context.Context, req function.RunRequest, resp *function.RunResponse) { + var versions []string + var constraint string + + resp.Error = function.ConcatFuncErrors(req.Arguments.Get(ctx, &versions, &constraint)) + if resp.Error != nil { + return + } + + filtered_semvers, err := shelper.PickFromSemverStrings(versions, constraint) + + if err != nil { + tflog.Error(ctx, "Error in shelper.PickFromSemverStrings()") + } + + if len(filtered_semvers) == 0 { + empty_list := make([]string, 0) + resp.Error = function.ConcatFuncErrors(resp.Result.Set(ctx, empty_list)) + } else { + resp.Error = function.ConcatFuncErrors(resp.Result.Set(ctx, filtered_semvers)) + } +} diff --git a/internal/provider/function_pick_test.go b/internal/provider/function_pick_test.go new file mode 100644 index 0000000..8c8ef97 --- /dev/null +++ b/internal/provider/function_pick_test.go @@ -0,0 +1,124 @@ +// Copyright (c) HashiCorp, Inc. +// Copyright (c) Anastas Dancha +// SPDX-License-Identifier: MPL-2.0 + +package provider + +import ( + "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 TestSemversPickFunction_Known(t *testing.T) { + resource.UnitTest(t, resource.TestCase{ + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.SkipBelow(tfversion.Version1_8_0), + }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: ` + output "semvers_filtered" { + value = provider::semvers::pick( + ["0.1.1-rc1+a231f59", "0.1.1", "0.1.10", "0.1.2-rc1", "0.2.1"], + "~> 0.2" + ) + } + `, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownOutputValue( + "semvers_filtered", + knownvalue.ListExact([]knownvalue.Check{ + knownvalue.StringExact("0.2.1"), + }), + ), + }, + }, + { + Config: ` + output "semvers_filtered" { + value = provider::semvers::pick( + ["0.1.0", "0.1.1-rc1+a231f59", "0.1.1", "0.1.10", "0.1.2-rc1", "0.2.1"], + ">= 0.1.1" + ) + } + `, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownOutputValue( + "semvers_filtered", + knownvalue.ListExact([]knownvalue.Check{ + knownvalue.StringExact("0.1.1"), + knownvalue.StringExact("0.1.10"), + knownvalue.StringExact("0.2.1"), + }), + ), + }, + }, + { + Config: ` + output "semvers_filtered" { + value = provider::semvers::pick( + ["0.1.0", "0.1.1-rc1+a231f59", "0.1.1", "0.1.10", "0.1.2-rc1", "0.2.1"], + ">= 3.0" + ) + } + `, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownOutputValue( + "semvers_filtered", + knownvalue.ListSizeExact(0), + ), + }, + }, + }, + }) +} + +// func TPick_Null(t *testing.T) { +// resource.UnitTest(t, resource.TestCase{ +// TerraformVersionChecks: []tfversion.TerraformVersionCheck{ +// tfversion.SkipBelow(tfversion.Version1_8_0), +// }, +// ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, +// Steps: []resource.TestStep{ +// { +// Config: ` +// output "test" { +// value = provider::semvers::sort_semvers(null) +// } +// `, +// // The parameter does not enable AllowNullValue +// ExpectError: regexp.MustCompile(`argument must not be null`), +// }, +// }, +// }) +// } + +// func TPick_Unknown(t *testing.T) { +// resource.UnitTest(t, resource.TestCase{ +// TerraformVersionChecks: []tfversion.TerraformVersionCheck{ +// tfversion.SkipBelow(tfversion.Version1_8_0), +// }, +// ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, +// Steps: []resource.TestStep{ +// { +// Config: ` +// resource "terraform_data" "test" { +// input = "testvalue" +// } + +// output "test" { +// value = provider::semvers::sort_semvers(terraform_data.test.output) +// } +// `, +// Check: resource.ComposeAggregateTestCheckFunc( +// resource.TestCheckOutput("test", "testvalue"), +// ), +// }, +// }, +// }) +// } diff --git a/internal/provider/function_sort.go b/internal/provider/function_sort.go index da33710..d46cd2f 100644 --- a/internal/provider/function_sort.go +++ b/internal/provider/function_sort.go @@ -7,7 +7,7 @@ package provider import ( "context" - "github.com/anapsix/terraform-provider-semvers/internal/helpers" + shelper "github.com/anapsix/terraform-provider-semvers/internal/helpers" "github.com/hashicorp/terraform-plugin-framework/function" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-log/tflog" diff --git a/internal/provider/function_sort_test.go b/internal/provider/function_sort_test.go index cd2f434..c7ec71b 100644 --- a/internal/provider/function_sort_test.go +++ b/internal/provider/function_sort_test.go @@ -1,4 +1,5 @@ // Copyright (c) HashiCorp, Inc. +// Copyright (c) Anastas Dancha // SPDX-License-Identifier: MPL-2.0 package provider diff --git a/internal/provider/provider.go b/internal/provider/provider.go index fafd4b8..ba2e50b 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -66,5 +66,6 @@ func (p *semversProvider) Configure(ctx context.Context, req provider.ConfigureR func (p *semversProvider) Functions(_ context.Context) []func() function.Function { return []func() function.Function{ NewSemversSortFunction, + NewSemversPickFunction, } } diff --git a/internal/provider/semvers_list_data_source.go b/internal/provider/semvers_list_data_source.go index c23e1f6..3daa4ed 100644 --- a/internal/provider/semvers_list_data_source.go +++ b/internal/provider/semvers_list_data_source.go @@ -14,7 +14,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-log/tflog" - "github.com/anapsix/terraform-provider-semvers/internal/helpers" + shelper "github.com/anapsix/terraform-provider-semvers/internal/helpers" ) // Data Source definition diff --git a/test/main.tf b/test/main.tf index b4c24d8..fa40de5 100644 --- a/test/main.tf +++ b/test/main.tf @@ -55,13 +55,17 @@ locals { local_last_no_prerelease = [for v in reverse(data.semvers_list.example.sorted_versions) : v if v.prerelease == ""][0] sorted_by_function = provider::semvers::sort(local.versions) - + picked_by_function = provider::semvers::pick(local.versions, "~> 0.9") } output "semvers_sorted_by_function" { value = local.sorted_by_function } +output "semvers_picked" { + value = local.picked_by_function +} + output "semvers_list_sorted_versions" { value = data.semvers_list.example.sorted_versions }