Skip to content

Commit

Permalink
Compile the regex once in the NewOption func
Browse files Browse the repository at this point in the history
  • Loading branch information
fr12k committed Jan 23, 2024
1 parent 9f63ba1 commit e1441c9
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 72 deletions.
29 changes: 12 additions & 17 deletions tfupdate/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ var moduleSourceRegexp = regexp.MustCompile(`(.+)\?ref=v([0-9]+(\.[0-9]+)*(-.*)*

// ModuleUpdater is a updater implementation which updates the module version constraint.
type ModuleUpdater struct {
name string
sourceMatchType string
version string
name string
nameRegex *regexp.Regexp
version string
}

// NewModuleUpdater is a factory method which returns an ModuleUpdater instance.
func NewModuleUpdater(name string, version string, sourceMatchType string) (Updater, error) {
func NewModuleUpdater(name string, version string, nameRegex *regexp.Regexp) (Updater, error) {
if len(name) == 0 {
return nil, errors.Errorf("failed to new module updater. name is required")
}
Expand All @@ -36,9 +36,9 @@ func NewModuleUpdater(name string, version string, sourceMatchType string) (Upda
}

return &ModuleUpdater{
name: name,
sourceMatchType: sourceMatchType,
version: version,
name: name,
nameRegex: nameRegex,
version: version,
}, nil
}

Expand All @@ -53,24 +53,19 @@ func (u *ModuleUpdater) Update(_ context.Context, _ *ModuleContext, filename str
return u.updateModuleBlock(f)
}

func match(match, matchType, name string) bool {
switch matchType {
case "regex":
var re = regexp.MustCompile(match)
return len(re.FindAllString(name, 1)) > 0
case "full":
fallthrough
default:
return match == name
func (u *ModuleUpdater) match(name string) bool {
if u.nameRegex == nil {
return u.name == name
}
return u.nameRegex.MatchString(name)
}

func (u *ModuleUpdater) updateModuleBlock(f *hclwrite.File) error {
for _, m := range allMatchingBlocksByType(f.Body(), "module") {
if s := m.Body().GetAttribute("source"); s != nil {
name, version := parseModuleSource(s)
// If this module is a target module
if match(u.name, u.sourceMatchType, name) {
if u.match(name) {
if len(version) == 0 {
// The source attribute doesn't have a version number.
// Set a version to attribute value only if the version key exists.
Expand Down
20 changes: 13 additions & 7 deletions tfupdate/module_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package tfupdate
import (
"context"
"reflect"
"regexp"
"testing"

"github.com/hashicorp/hcl/v2"
Expand All @@ -22,9 +23,9 @@ func TestNewModuleUpdater(t *testing.T) {
sourceMatchType: "full",
version: "2.17.0",
want: &ModuleUpdater{
name: "terraform-aws-modules/vpc/aws",
sourceMatchType: "full",
version: "2.17.0",
name: "terraform-aws-modules/vpc/aws",
nameRegex: nil,
version: "2.17.0",
},
ok: true,
},
Expand All @@ -45,7 +46,7 @@ func TestNewModuleUpdater(t *testing.T) {
}

for _, tc := range cases {
got, err := NewModuleUpdater(tc.name, tc.version, tc.sourceMatchType)
got, err := NewModuleUpdater(tc.name, tc.version, nil)
if tc.ok && err != nil {
t.Errorf("NewModuleUpdater() with name = %s, version = %s returns unexpected err: %+v", tc.name, tc.version, err)
}
Expand Down Expand Up @@ -227,9 +228,14 @@ module "vpc2" {

for _, tc := range cases {
u := &ModuleUpdater{
name: tc.name,
sourceMatchType: tc.sourceMatchType,
version: tc.version,
name: tc.name,
nameRegex: func() *regexp.Regexp {
if tc.sourceMatchType == "regex" {
return regexp.MustCompile(tc.name)
}
return nil
}(),
version: tc.version,
}
f, diags := hclwrite.ParseConfig([]byte(tc.src), tc.filename, hcl.Pos{Line: 1, Column: 1})
if diags.HasErrors() {
Expand Down
41 changes: 29 additions & 12 deletions tfupdate/option.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ package tfupdate
import (
"fmt"
"regexp"
"strings"

"golang.org/x/exp/slices"
)

// Option is a set of parameters to update.
Expand Down Expand Up @@ -32,8 +35,10 @@ type Option struct {
// An array of regular expression for paths to ignore.
ignorePaths []*regexp.Regexp

// Define how to match module source URLs. Valid values are "full" or "regex".
sourceMatchType string
// This field stores the compiled RE2 regex from the provide name parameter.
// In case the sourceMatchType is set to regex this field is used to match the name.
// In case the provided sourceMatchType is full this field is nil.
nameRegex *regexp.Regexp
}

// NewOption returns an option.
Expand All @@ -46,23 +51,35 @@ func NewOption(updateType string, name string, version string, platforms []strin

r, err := regexp.Compile(ignorePath)
if err != nil {
return Option{}, fmt.Errorf("faild to compile regexp for ignorePath: %s", err)
return Option{}, fmt.Errorf("failed to compile regexp for ignorePath: %s", err)
}
regexps = append(regexps, r)
}

if sourceMatchType == "" {
sourceMatchType = "full"
var nameRegex *regexp.Regexp
validSourceMatchTypes := []string{"full", "regex"}
if !slices.Contains[string](validSourceMatchTypes, sourceMatchType) {
return Option{}, fmt.Errorf("invalid sourceMatchType: %s valid options [%s]", sourceMatchType, strings.Join(validSourceMatchTypes, ","))
} else if sourceMatchType == "regex" {
if len(name) == 0 {
return Option{}, fmt.Errorf("name is required when sourceMatchType is regex")
}

r, err := regexp.Compile(name)
if err != nil {
return Option{}, fmt.Errorf("failed to compile regexp for name: %s with error: %s", name, err)
}
nameRegex = r
}

return Option{
updateType: updateType,
name: name,
version: version,
platforms: platforms,
recursive: recursive,
ignorePaths: regexps,
sourceMatchType: sourceMatchType,
updateType: updateType,
name: name,
version: version,
platforms: platforms,
recursive: recursive,
ignorePaths: regexps,
nameRegex: nameRegex,
}, nil
}

Expand Down
138 changes: 103 additions & 35 deletions tfupdate/option_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@ func TestNewOption(t *testing.T) {
platforms: []string{},
recursive: true,
ignorePaths: []string{},
sourceMatchType: "",
sourceMatchType: "full",
want: Option{
updateType: "terraform",
version: "0.12.7",
platforms: []string{},
recursive: true,
ignorePaths: []*regexp.Regexp{},
sourceMatchType: "full",
updateType: "terraform",
version: "0.12.7",
platforms: []string{},
recursive: true,
ignorePaths: []*regexp.Regexp{},
nameRegex: nil,
},
ok: true,
},
Expand All @@ -44,13 +44,13 @@ func TestNewOption(t *testing.T) {
ignorePaths: []string{},
sourceMatchType: "full",
want: Option{
updateType: "provider",
name: "aws",
version: "2.23.0",
platforms: []string{},
recursive: true,
ignorePaths: []*regexp.Regexp{},
sourceMatchType: "full",
updateType: "provider",
name: "aws",
version: "2.23.0",
platforms: []string{},
recursive: true,
ignorePaths: []*regexp.Regexp{},
nameRegex: nil,
},
ok: true,
},
Expand All @@ -60,14 +60,14 @@ func TestNewOption(t *testing.T) {
platforms: []string{},
recursive: true,
ignorePaths: []string{"hoge", "fuga"},
sourceMatchType: "regex",
sourceMatchType: "full",
want: Option{
updateType: "terraform",
version: "0.12.7",
platforms: []string{},
recursive: true,
ignorePaths: []*regexp.Regexp{regexp.MustCompile("hoge"), regexp.MustCompile("fuga")},
sourceMatchType: "regex",
updateType: "terraform",
version: "0.12.7",
platforms: []string{},
recursive: true,
ignorePaths: []*regexp.Regexp{regexp.MustCompile("hoge"), regexp.MustCompile("fuga")},
nameRegex: nil,
},
ok: true,
},
Expand All @@ -77,14 +77,14 @@ func TestNewOption(t *testing.T) {
platforms: []string{},
recursive: true,
ignorePaths: []string{""},
sourceMatchType: "invalid",
sourceMatchType: "full",
want: Option{
updateType: "terraform",
version: "0.12.7",
platforms: []string{},
recursive: true,
ignorePaths: []*regexp.Regexp{},
sourceMatchType: "invalid",
updateType: "terraform",
version: "0.12.7",
platforms: []string{},
recursive: true,
ignorePaths: []*regexp.Regexp{},
nameRegex: nil,
},
ok: true,
},
Expand All @@ -104,17 +104,85 @@ func TestNewOption(t *testing.T) {
platforms: []string{"darwin_arm64", "darwin_amd64", "linux_amd64"},
recursive: true,
ignorePaths: []string{},
sourceMatchType: "invalid",
sourceMatchType: "full",
want: Option{
updateType: "lock",
version: "",
platforms: []string{"darwin_arm64", "darwin_amd64", "linux_amd64"},
recursive: true,
ignorePaths: []*regexp.Regexp{},
nameRegex: nil,
},
ok: true,
},
{
updateType: "module",
name: "terraform-aws-modules/vpc/aws",
version: "0.12.7",
platforms: []string{},
recursive: true,
ignorePaths: []string{},
sourceMatchType: "full",
want: Option{
updateType: "module",
name: "terraform-aws-modules/vpc/aws",
version: "0.12.7",
platforms: []string{},
recursive: true,
ignorePaths: []*regexp.Regexp{},
nameRegex: nil,
},
ok: true,
},
{
updateType: "module",
name: `terraform-aws-modules\.git/vpc/aws`,
version: "0.12.7",
platforms: []string{},
recursive: true,
ignorePaths: []string{},
sourceMatchType: "regex",
want: Option{
updateType: "lock",
version: "",
platforms: []string{"darwin_arm64", "darwin_amd64", "linux_amd64"},
recursive: true,
ignorePaths: []*regexp.Regexp{},
sourceMatchType: "invalid",
updateType: "module",
name: `terraform-aws-modules\.git/vpc/aws`,
version: "0.12.7",
platforms: []string{},
recursive: true,
ignorePaths: []*regexp.Regexp{},
nameRegex: regexp.MustCompile(`terraform-aws-modules\.git/vpc/aws`),
},
ok: true,
},
{
updateType: "module",
name: "",
version: "0.12.7",
platforms: []string{},
recursive: true,
ignorePaths: []string{},
sourceMatchType: "regex",
ok: false,
},
{
updateType: "module",
name: `\`,
version: "0.12.7",
platforms: []string{},
recursive: true,
ignorePaths: []string{},
sourceMatchType: "regex",
ok: false,
},
{
updateType: "module",
name: "",
version: "0.12.7",
platforms: []string{},
recursive: true,
ignorePaths: []string{},
sourceMatchType: "invalid",
ok: false,
},
}

for _, tc := range cases {
Expand Down
2 changes: 1 addition & 1 deletion tfupdate/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func NewUpdater(o Option) (Updater, error) {
case "provider":
return NewProviderUpdater(o.name, o.version)
case "module":
return NewModuleUpdater(o.name, o.version, o.sourceMatchType)
return NewModuleUpdater(o.name, o.version, o.nameRegex)
case "lock":
return NewLockUpdater(o.platforms, lockIndex)
default:
Expand Down

0 comments on commit e1441c9

Please sign in to comment.