From 73ae97cdd2107781f95fafc41d13a34d33e799f8 Mon Sep 17 00:00:00 2001 From: Phil Tyler Date: Thu, 2 Feb 2023 16:57:06 -0800 Subject: [PATCH 01/23] WIP Sites.yml parsing package --- go.mod | 10 ++ go.sum | 17 ++ pkg/sites/fixtures/only_api_version_1.yaml | 2 + pkg/sites/sites.go | 74 +++++++++ pkg/sites/sites_test.go | 175 +++++++++++++++++++++ 5 files changed, 278 insertions(+) create mode 100644 go.sum create mode 100644 pkg/sites/fixtures/only_api_version_1.yaml create mode 100644 pkg/sites/sites.go create mode 100644 pkg/sites/sites_test.go diff --git a/go.mod b/go.mod index e23728c..f8336b5 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,13 @@ module pyml-validator go 1.19 + +require ( + github.com/stretchr/testify v1.8.1 + gopkg.in/yaml.v3 v3.0.1 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..2ec90f7 --- /dev/null +++ b/go.sum @@ -0,0 +1,17 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/sites/fixtures/only_api_version_1.yaml b/pkg/sites/fixtures/only_api_version_1.yaml new file mode 100644 index 0000000..da0609b --- /dev/null +++ b/pkg/sites/fixtures/only_api_version_1.yaml @@ -0,0 +1,2 @@ +--- +api_version: 1 \ No newline at end of file diff --git a/pkg/sites/sites.go b/pkg/sites/sites.go new file mode 100644 index 0000000..e46038e --- /dev/null +++ b/pkg/sites/sites.go @@ -0,0 +1,74 @@ +package sites + +import ( + "errors" + "fmt" + "regexp" + + "gopkg.in/yaml.v3" +) + +const ( + MaxDomainMaps = 25 // This could be raised +) + +var ( + ValidMultidevNameRegex = regexp.MustCompile(`^[a-z0-9\-]{1,11}$`) + // See https://github.com/pantheon-systems/titan-mt/blob/master/yggdrasil/lib/pantheon_yml/pantheon_yml_v1_schema.py + ValidHostnameRegex = regexp.MustCompile(`^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$`) + + ErrInvalidAPIVersion = errors.New("Invalid API Version. Must be '1'") +) + +// SitesYml is used to map domains across environments for search and replace with WPMS sites. +type SitesYml struct { + APIVersion int `yaml:"api_version"` + DomainMaps DomainMaps `yaml:"domain_maps"` +} + +type DomainMaps map[string]DomainMapByEnvironment + +// DomainMapByEnvironment is a map of site (blog) domains keyed by blog ID. +type DomainMapByEnvironment map[int]string + +func validate(sites SitesYml) error { + err := validateAPIVersion(sites.APIVersion) + if err != nil { + return err + } + return validateDomainMaps(sites.DomainMaps) +} + +func validateDomainMaps(domainMaps map[string]DomainMapByEnvironment) error { + for env, domainMap := range domainMaps { + if !ValidMultidevNameRegex.MatchString(env) { + return fmt.Errorf("%q is not a valid environment name", env) + } + if len(domainMap) > MaxDomainMaps { + return fmt.Errorf("%q has too many domains listed. Maximum is %d", env, MaxDomainMaps) + } + for _, domain := range domainMap { + if !ValidHostnameRegex.MatchString(domain) { + return fmt.Errorf("%q is not a valid hostname", domain) + } + } + } + return nil +} + +func validateAPIVersion(apiVersion int) error { + if apiVersion != 1 { + return ErrInvalidAPIVersion + } + return nil +} + +func ValidateFromYaml(y []byte) error { + var s SitesYml + + err := yaml.Unmarshal(y, &s) + if err != nil { + return err + } + return validate(s) +} diff --git a/pkg/sites/sites_test.go b/pkg/sites/sites_test.go new file mode 100644 index 0000000..ac3e04f --- /dev/null +++ b/pkg/sites/sites_test.go @@ -0,0 +1,175 @@ +package sites + +import ( + "errors" + "fmt" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestValidateAPIVersion(t *testing.T) { + for _, tc := range []struct { + version int + expected error + }{ + {1, nil}, + {2, ErrInvalidAPIVersion}, + } { + t.Run(fmt.Sprintf("%v", tc.version), func(t *testing.T) { + err := validateAPIVersion(tc.version) + assert.ErrorIs(t, err, tc.expected) + }) + } +} + +func TestValidate(t *testing.T) { + for _, tc := range []struct { + name string + sitesYml SitesYml + expected error + }{ + { + "valid only api version", + SitesYml{APIVersion: 1}, + nil, + }, + { + "invalid api version", + SitesYml{APIVersion: 2}, + ErrInvalidAPIVersion, + }, + { + "valid domain maps", + SitesYml{ + APIVersion: 1, + DomainMaps: DomainMaps{ + "dev": DomainMapByEnvironment{ + 1: "blog1.dev-mysite.pantheonsite.io", + }, + "test": DomainMapByEnvironment{ + 1: "blog1.dev-mysite.pantheonsite.io", + }, + "live": DomainMapByEnvironment{ + 1: "blog1.mysite.com", + }, + "autopilot": DomainMapByEnvironment{ + 1: "blog1.autopilot-mysite.pantheonsite.io", + }, + }, + }, + nil, + }, + { + "invalid domain maps long env", + SitesYml{ + APIVersion: 1, + DomainMaps: DomainMaps{ + "dev": DomainMapByEnvironment{ + 1: "blog1.dev-mysite.pantheonsite.io", + }, + "mylongmultidevname": DomainMapByEnvironment{ + 1: "blog1.mylongmultidevname-mysite.pantheonsite.io", + }, + }, + }, + errors.New(`"mylongmultidevname" is not a valid environment name`), + }, + { + "invalid domain maps bad env name", + SitesYml{ + APIVersion: 1, + DomainMaps: DomainMaps{ + "dev": DomainMapByEnvironment{ + 1: "blog1.dev-mysite.pantheonsite.io", + }, + "feat_branch": DomainMapByEnvironment{ + 1: "blog1.feat-branch-mysite.pantheonsite.io", + }, + }, + }, + errors.New(`"feat_branch" is not a valid environment name`), + }, + { + "invalid domain maps too many domains", + SitesYml{ + APIVersion: 1, + DomainMaps: DomainMaps{ + "dev": DomainMapByEnvironment{ + 1: "blog1.dev-mysite.pantheonsite.io", + 2: "blog2.dev-mysite.pantheonsite.io", + 3: "blog3.dev-mysite.pantheonsite.io", + 4: "blog4.dev-mysite.pantheonsite.io", + 5: "blog5.dev-mysite.pantheonsite.io", + 6: "blog6.dev-mysite.pantheonsite.io", + 7: "blog7.dev-mysite.pantheonsite.io", + 8: "blog8.dev-mysite.pantheonsite.io", + 9: "blog9.dev-mysite.pantheonsite.io", + 10: "blog10.dev-mysite.pantheonsite.io", + 11: "blog11.dev-mysite.pantheonsite.io", + 12: "blog12.dev-mysite.pantheonsite.io", + 13: "blog13.dev-mysite.pantheonsite.io", + 14: "blog14.dev-mysite.pantheonsite.io", + 15: "blog15.dev-mysite.pantheonsite.io", + 16: "blog16.dev-mysite.pantheonsite.io", + 17: "blog17.dev-mysite.pantheonsite.io", + 18: "blog18.dev-mysite.pantheonsite.io", + 19: "blog19.dev-mysite.pantheonsite.io", + 20: "blog20.dev-mysite.pantheonsite.io", + 21: "blog21.dev-mysite.pantheonsite.io", + 22: "blog22.dev-mysite.pantheonsite.io", + 23: "blog23.dev-mysite.pantheonsite.io", + 24: "blog24.dev-mysite.pantheonsite.io", + 25: "blog25.dev-mysite.pantheonsite.io", + 26: "blog26.dev-mysite.pantheonsite.io", + 27: "blog27.dev-mysite.pantheonsite.io", + 28: "blog28.dev-mysite.pantheonsite.io", + 29: "blog29.dev-mysite.pantheonsite.io", + }, + "feat_branch": DomainMapByEnvironment{ + 1: "blog1.feat-branch-mysite.pantheonsite.io", + }, + }, + }, + errors.New(`"dev" has too many domains listed. Maximum is 25`), + }, + } { + t.Run(tc.name, func(t *testing.T) { + err := validate(tc.sitesYml) + if tc.expected == nil { + assert.NoError(t, err) + return + } + assert.EqualError(t, err, tc.expected.Error()) + }) + } +} + +func TestValidateFromYaml(t *testing.T) { + for _, tc := range []struct { + name string + yaml string + expected error + }{ + { + name: "only api_version", + yaml: `api_version: 1`, + expected: nil, + }, + { + name: "invalid api_version ", + yaml: `api_version: 2`, + expected: errors.New("Invalid API Version. Must be '1'"), + }, + } { + t.Run(tc.name, func(t *testing.T) { + yaml := []byte(tc.yaml) + err := ValidateFromYaml(yaml) + if tc.expected == nil { + assert.NoError(t, err) + return + } + assert.EqualError(t, err, tc.expected.Error()) + }) + } +} From 88e1ecdc42014c3c79481f7d49a148a772dc6172 Mon Sep 17 00:00:00 2001 From: Phil Tyler Date: Fri, 3 Feb 2023 13:27:03 -0800 Subject: [PATCH 02/23] cleaner yml in test cases --- pkg/sites/sites_test.go | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/pkg/sites/sites_test.go b/pkg/sites/sites_test.go index ac3e04f..b2713bc 100644 --- a/pkg/sites/sites_test.go +++ b/pkg/sites/sites_test.go @@ -3,6 +3,7 @@ package sites import ( "errors" "fmt" + "strings" "testing" "github.com/stretchr/testify/assert" @@ -152,18 +153,25 @@ func TestValidateFromYaml(t *testing.T) { expected error }{ { - name: "only api_version", - yaml: `api_version: 1`, + name: "only api_version", + yaml: ` + --- + api_version: 1`, expected: nil, }, { - name: "invalid api_version ", - yaml: `api_version: 2`, + name: "invalid api_version ", + yaml: ` + --- + api_version: 2`, expected: errors.New("Invalid API Version. Must be '1'"), }, } { t.Run(tc.name, func(t *testing.T) { - yaml := []byte(tc.yaml) + yaml := []byte( + // Yaml doesn't like tabs, but lets us make our test cases prettier + strings.ReplaceAll(tc.yaml, "\t", ""), + ) err := ValidateFromYaml(yaml) if tc.expected == nil { assert.NoError(t, err) From c51a3c974debfe5fed22d06f2412a16db568ecec Mon Sep 17 00:00:00 2001 From: Phil Tyler Date: Fri, 3 Feb 2023 14:31:47 -0800 Subject: [PATCH 03/23] README --- README.MD | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/README.MD b/README.MD index 04ee2d5..96e623e 100644 --- a/README.MD +++ b/README.MD @@ -1 +1,11 @@ -# Pantheon.yml Validator \ No newline at end of file +# Pantheon.yml | Sites.yml Validator + +A utility for validating a sites.yml file on a pantheon site during WordPress multisites' search-replace tasks. Asprirationally to include pantheon.yml validation in the future. + +# Testing + +`make test` runs linting and testing. + +# Releases + +Automatically releases on merge to main via autotag + goreleaser. See [Autotag Readme](https://github.com/pantheon-systems/autotag) for details on how the SemVer is determined. \ No newline at end of file From 33f62248c4a332da09e139e101683b45be71d81e Mon Sep 17 00:00:00 2001 From: Phil Tyler Date: Fri, 3 Feb 2023 14:32:28 -0800 Subject: [PATCH 04/23] rm test fixtures file --- pkg/sites/fixtures/only_api_version_1.yaml | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 pkg/sites/fixtures/only_api_version_1.yaml diff --git a/pkg/sites/fixtures/only_api_version_1.yaml b/pkg/sites/fixtures/only_api_version_1.yaml deleted file mode 100644 index da0609b..0000000 --- a/pkg/sites/fixtures/only_api_version_1.yaml +++ /dev/null @@ -1,2 +0,0 @@ ---- -api_version: 1 \ No newline at end of file From 16fb8fb3341eb9f22682d16408802ef708769a87 Mon Sep 17 00:00:00 2001 From: Phil Tyler Date: Fri, 3 Feb 2023 19:06:20 -0800 Subject: [PATCH 05/23] Cobra. It's not _not_ working --- cmd/root.go | 31 ++++++++++++++++++++++++ cmd/sites.go | 34 +++++++++++++++++++++++++++ fixtures/invalid_api_version_only.yml | 2 ++ fixtures/valid_api_version_only.yml | 2 ++ go.mod | 3 +++ go.sum | 9 +++++++ main.go | 6 ++++- pkg/sites/errors.go | 10 ++++++++ pkg/sites/sites.go | 7 ++---- 9 files changed, 98 insertions(+), 6 deletions(-) create mode 100644 cmd/root.go create mode 100644 cmd/sites.go create mode 100644 fixtures/invalid_api_version_only.yml create mode 100644 fixtures/valid_api_version_only.yml create mode 100644 pkg/sites/errors.go diff --git a/cmd/root.go b/cmd/root.go new file mode 100644 index 0000000..b13480c --- /dev/null +++ b/cmd/root.go @@ -0,0 +1,31 @@ +package cmd + +import ( + "fmt" + "os" + + "github.com/spf13/cobra" +) + +var FilePath string + +var rootCmd = &cobra.Command{ + Use: "pymlv", + Short: "Pymlv is a validator for sites.yml, pantheon.yml, etc.", + Long: ``, + RunE: func(cmd *cobra.Command, args []string) error { + fmt.Println("Foo") + return nil + }, +} + +func Execute() { + if err := rootCmd.Execute(); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} + +func init() { + rootCmd.PersistentFlags().StringVarP(&FilePath, "file", "f", "", "path/to/file.yml") +} diff --git a/cmd/sites.go b/cmd/sites.go new file mode 100644 index 0000000..5d07a3a --- /dev/null +++ b/cmd/sites.go @@ -0,0 +1,34 @@ +package cmd + +import ( + "fmt" + "os" + "pyml-validator/pkg/sites" + + "github.com/spf13/cobra" +) + +func init() { + rootCmd.AddCommand(sitesCommand) +} + +var sitesCommand = &cobra.Command{ + Use: "sites", + Short: "validate sites.yml", + Long: `Validate sites.yml`, + RunE: func(cmd *cobra.Command, args []string) error { + fmt.Println("Validate sites.yml") + if FilePath != "" { + fmt.Println(fmt.Sprintf("At Path %q", FilePath)) + } + + yFile, err := os.ReadFile(FilePath) + if err != nil { + fmt.Printf("Error reading YAML file: %s\n", err) + return err + } + + err = sites.ValidateFromYaml(yFile) + return err + }, +} diff --git a/fixtures/invalid_api_version_only.yml b/fixtures/invalid_api_version_only.yml new file mode 100644 index 0000000..f56636c --- /dev/null +++ b/fixtures/invalid_api_version_only.yml @@ -0,0 +1,2 @@ +--- +api_version: 2 \ No newline at end of file diff --git a/fixtures/valid_api_version_only.yml b/fixtures/valid_api_version_only.yml new file mode 100644 index 0000000..da0609b --- /dev/null +++ b/fixtures/valid_api_version_only.yml @@ -0,0 +1,2 @@ +--- +api_version: 1 \ No newline at end of file diff --git a/go.mod b/go.mod index f8336b5..d27b6a4 100644 --- a/go.mod +++ b/go.mod @@ -9,5 +9,8 @@ require ( require ( github.com/davecgh/go-spew v1.1.1 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/spf13/cobra v1.6.1 // indirect + github.com/spf13/pflag v1.0.5 // indirect ) diff --git a/go.sum b/go.sum index 2ec90f7..4e43bfc 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,17 @@ +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= +github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= diff --git a/main.go b/main.go index 38dd16d..68348e0 100644 --- a/main.go +++ b/main.go @@ -1,3 +1,7 @@ package main -func main() {} +import "pyml-validator/cmd" + +func main() { + cmd.Execute() +} diff --git a/pkg/sites/errors.go b/pkg/sites/errors.go new file mode 100644 index 0000000..adf34fe --- /dev/null +++ b/pkg/sites/errors.go @@ -0,0 +1,10 @@ +package sites + +import "errors" + +var ( + ErrInvalidAPIVersion = errors.New("Invalid API Version. Must be '1'") +) + +// TODO: More dynamic errors could be refactored here, but likely only worth +// pursuing once we are passing errors back to customers diff --git a/pkg/sites/sites.go b/pkg/sites/sites.go index e46038e..f7baee0 100644 --- a/pkg/sites/sites.go +++ b/pkg/sites/sites.go @@ -1,7 +1,6 @@ package sites import ( - "errors" "fmt" "regexp" @@ -13,11 +12,9 @@ const ( ) var ( - ValidMultidevNameRegex = regexp.MustCompile(`^[a-z0-9\-]{1,11}$`) // See https://github.com/pantheon-systems/titan-mt/blob/master/yggdrasil/lib/pantheon_yml/pantheon_yml_v1_schema.py - ValidHostnameRegex = regexp.MustCompile(`^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$`) - - ErrInvalidAPIVersion = errors.New("Invalid API Version. Must be '1'") + ValidHostnameRegex = regexp.MustCompile(`^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$`) + ValidMultidevNameRegex = regexp.MustCompile(`^[a-z0-9\-]{1,11}$`) ) // SitesYml is used to map domains across environments for search and replace with WPMS sites. From 53042338516751db7505f266cf8bcf23fa1b9869 Mon Sep 17 00:00:00 2001 From: Phil Tyler Date: Fri, 3 Feb 2023 19:08:43 -0800 Subject: [PATCH 06/23] gosimple --- cmd/sites.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/sites.go b/cmd/sites.go index 5d07a3a..3bbf5f6 100644 --- a/cmd/sites.go +++ b/cmd/sites.go @@ -19,7 +19,7 @@ var sitesCommand = &cobra.Command{ RunE: func(cmd *cobra.Command, args []string) error { fmt.Println("Validate sites.yml") if FilePath != "" { - fmt.Println(fmt.Sprintf("At Path %q", FilePath)) + fmt.Printf(fmt.Sprintf("At Path %q\n", FilePath)) } yFile, err := os.ReadFile(FilePath) From a022b14c4dcdda610a45fffb7eaa81ca2dbf43c2 Mon Sep 17 00:00:00 2001 From: Phil Tyler Date: Fri, 3 Feb 2023 19:12:22 -0800 Subject: [PATCH 07/23] not quite sure about error handling but its the end of the day --- cmd/root.go | 5 +---- cmd/sites.go | 6 +++--- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/cmd/root.go b/cmd/root.go index b13480c..ec11466 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -13,10 +13,7 @@ var rootCmd = &cobra.Command{ Use: "pymlv", Short: "Pymlv is a validator for sites.yml, pantheon.yml, etc.", Long: ``, - RunE: func(cmd *cobra.Command, args []string) error { - fmt.Println("Foo") - return nil - }, + Run: func(cmd *cobra.Command, args []string) {}, } func Execute() { diff --git a/cmd/sites.go b/cmd/sites.go index 3bbf5f6..69e051f 100644 --- a/cmd/sites.go +++ b/cmd/sites.go @@ -16,7 +16,7 @@ var sitesCommand = &cobra.Command{ Use: "sites", Short: "validate sites.yml", Long: `Validate sites.yml`, - RunE: func(cmd *cobra.Command, args []string) error { + Run: func(cmd *cobra.Command, args []string) { fmt.Println("Validate sites.yml") if FilePath != "" { fmt.Printf(fmt.Sprintf("At Path %q\n", FilePath)) @@ -25,10 +25,10 @@ var sitesCommand = &cobra.Command{ yFile, err := os.ReadFile(FilePath) if err != nil { fmt.Printf("Error reading YAML file: %s\n", err) - return err + return } err = sites.ValidateFromYaml(yFile) - return err + return }, } From 5fc59b321674bc5b909f0a4906723bc99997582d Mon Sep 17 00:00:00 2001 From: Phil Tyler Date: Mon, 6 Feb 2023 10:16:47 -0800 Subject: [PATCH 08/23] go mod cobra --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index d27b6a4..838c97d 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module pyml-validator go 1.19 require ( + github.com/spf13/cobra v1.6.1 github.com/stretchr/testify v1.8.1 gopkg.in/yaml.v3 v3.0.1 ) @@ -11,6 +12,5 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/spf13/cobra v1.6.1 // indirect github.com/spf13/pflag v1.0.5 // indirect ) From 5ad8b1fc8d995d0ef08c3e9ad8004e698044dba1 Mon Sep 17 00:00:00 2001 From: Phil Tyler Date: Mon, 6 Feb 2023 13:11:05 -0800 Subject: [PATCH 09/23] validator package refactor --- .gitignore | 2 + cmd/pantheon.go | 20 +++++ cmd/root.go | 26 +++--- cmd/sites.go | 23 +++-- pkg/model/sites.go | 12 +++ pkg/{sites => validator}/sites.go | 54 +++++++----- .../errors.go => validator/sites_errors.go} | 2 +- pkg/{sites => validator}/sites_test.go | 88 +++++++++++++------ pkg/validator/validator.go | 6 ++ 9 files changed, 163 insertions(+), 70 deletions(-) create mode 100644 cmd/pantheon.go create mode 100644 pkg/model/sites.go rename pkg/{sites => validator}/sites.go (50%) rename pkg/{sites/errors.go => validator/sites_errors.go} (92%) rename pkg/{sites => validator}/sites_test.go (68%) create mode 100644 pkg/validator/validator.go diff --git a/.gitignore b/.gitignore index 43d732d..1023ed2 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ .DS_Store dist/ + +*.out \ No newline at end of file diff --git a/cmd/pantheon.go b/cmd/pantheon.go new file mode 100644 index 0000000..4b0082d --- /dev/null +++ b/cmd/pantheon.go @@ -0,0 +1,20 @@ +package cmd + +import ( + "log" + + "github.com/spf13/cobra" +) + +func init() { + rootCmd.AddCommand(pantheonCommand) +} + +var pantheonCommand = &cobra.Command{ + Use: "pantheon", + Short: "validate pantheon.yml", + Long: `Validate pantheon.yml`, + Run: func(cmd *cobra.Command, args []string) { + log.Fatal("Not yet implemented.") + }, +} diff --git a/cmd/root.go b/cmd/root.go index ec11466..6cd603f 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -1,28 +1,30 @@ package cmd import ( - "fmt" - "os" + "log" - "github.com/spf13/cobra" + "github.com/spf13/cobra" ) var FilePath string var rootCmd = &cobra.Command{ - Use: "pymlv", - Short: "Pymlv is a validator for sites.yml, pantheon.yml, etc.", - Long: ``, - Run: func(cmd *cobra.Command, args []string) {}, + Use: "pyml-validator", + Short: "Pyml-validator validates pantheon.yml, sites.yml, etc.", + Long: `Pyml-validator is a validator for pantheon.yml or sites.yml. +Ensures that the given config file can be used by the platform.`, } func Execute() { - if err := rootCmd.Execute(); err != nil { - fmt.Fprintln(os.Stderr, err) - os.Exit(1) - } + if err := rootCmd.Execute(); err != nil { + log.Fatal(err) + } } func init() { - rootCmd.PersistentFlags().StringVarP(&FilePath, "file", "f", "", "path/to/file.yml") + rootCmd.PersistentFlags().StringVarP(&FilePath, "file", "f", "", "path/to/file.yml") + err := rootCmd.MarkPersistentFlagRequired("file") + if err != nil { + log.Fatal(err) + } } diff --git a/cmd/sites.go b/cmd/sites.go index 69e051f..cc19624 100644 --- a/cmd/sites.go +++ b/cmd/sites.go @@ -2,8 +2,7 @@ package cmd import ( "fmt" - "os" - "pyml-validator/pkg/sites" + "pyml-validator/pkg/validator" "github.com/spf13/cobra" ) @@ -16,19 +15,19 @@ var sitesCommand = &cobra.Command{ Use: "sites", Short: "validate sites.yml", Long: `Validate sites.yml`, - Run: func(cmd *cobra.Command, args []string) { - fmt.Println("Validate sites.yml") - if FilePath != "" { - fmt.Printf(fmt.Sprintf("At Path %q\n", FilePath)) - } + RunE: func(cmd *cobra.Command, args []string) error { + // Is there a better way to do this? Without this we print usage on error exits. + // If we override at the root level, we don't get usage when we _do_ want it. + cmd.SilenceUsage = true + + v := validator.NewSitesVaidator() + err := v.ValidateFromFilePath(FilePath) - yFile, err := os.ReadFile(FilePath) if err != nil { - fmt.Printf("Error reading YAML file: %s\n", err) - return + return err } - err = sites.ValidateFromYaml(yFile) - return + fmt.Println("✨ sites.yml is valid") + return nil }, } diff --git a/pkg/model/sites.go b/pkg/model/sites.go new file mode 100644 index 0000000..01856c4 --- /dev/null +++ b/pkg/model/sites.go @@ -0,0 +1,12 @@ +package model + +// SitesYml is used to map domains across environments for search and replace with WPMS sites. +type SitesYml struct { + APIVersion int `yaml:"api_version"` + DomainMaps DomainMaps `yaml:"domain_maps"` +} + +type DomainMaps map[string]DomainMapByEnvironment + +// DomainMapByEnvironment is a map of site (blog) domains keyed by blog ID. +type DomainMapByEnvironment map[int]string diff --git a/pkg/sites/sites.go b/pkg/validator/sites.go similarity index 50% rename from pkg/sites/sites.go rename to pkg/validator/sites.go index f7baee0..2ad75ba 100644 --- a/pkg/sites/sites.go +++ b/pkg/validator/sites.go @@ -1,7 +1,9 @@ -package sites +package validator import ( "fmt" + "os" + "pyml-validator/pkg/model" "regexp" "gopkg.in/yaml.v3" @@ -17,18 +19,34 @@ var ( ValidMultidevNameRegex = regexp.MustCompile(`^[a-z0-9\-]{1,11}$`) ) -// SitesYml is used to map domains across environments for search and replace with WPMS sites. -type SitesYml struct { - APIVersion int `yaml:"api_version"` - DomainMaps DomainMaps `yaml:"domain_maps"` +type SitesValidator struct{} + +// NewSitesValidator constructs a new SitesValidator method. +func NewSitesVaidator() Validator { + return &SitesValidator{} } -type DomainMaps map[string]DomainMapByEnvironment +// ValidateFromYaml asserts a given sites.yaml file is valid. +func (v *SitesValidator) ValidateFromYaml(y []byte) error { + var s model.SitesYml -// DomainMapByEnvironment is a map of site (blog) domains keyed by blog ID. -type DomainMapByEnvironment map[int]string + err := yaml.Unmarshal(y, &s) + if err != nil { + return err + } + return v.validate(s) +} -func validate(sites SitesYml) error { +func (v *SitesValidator) ValidateFromFilePath(filePath string) error { + yFile, err := os.ReadFile(filePath) + if err != nil { + return fmt.Errorf("error reading YAML file: %w", err) + } + return v.ValidateFromYaml(yFile) +} + +// validate asserts all aspects of sites.yml are valid. +func (v *SitesValidator) validate(sites model.SitesYml) error { err := validateAPIVersion(sites.APIVersion) if err != nil { return err @@ -36,7 +54,11 @@ func validate(sites SitesYml) error { return validateDomainMaps(sites.DomainMaps) } -func validateDomainMaps(domainMaps map[string]DomainMapByEnvironment) error { +// validateDomainMaps ensures the domain maps provided in sites.yml are valid +// by asserting cloud development environments names are valid, there are not +// too many domain maps listed for any environment, and that the hostnames +// provided are valid Pantheon hostnames. +func validateDomainMaps(domainMaps map[string]model.DomainMapByEnvironment) error { for env, domainMap := range domainMaps { if !ValidMultidevNameRegex.MatchString(env) { return fmt.Errorf("%q is not a valid environment name", env) @@ -53,19 +75,11 @@ func validateDomainMaps(domainMaps map[string]DomainMapByEnvironment) error { return nil } +// validateAPIVersion asserts if sites.yml has a valid api version set. Once +// more than one version is valid, this will need to be more more robust. func validateAPIVersion(apiVersion int) error { if apiVersion != 1 { return ErrInvalidAPIVersion } return nil } - -func ValidateFromYaml(y []byte) error { - var s SitesYml - - err := yaml.Unmarshal(y, &s) - if err != nil { - return err - } - return validate(s) -} diff --git a/pkg/sites/errors.go b/pkg/validator/sites_errors.go similarity index 92% rename from pkg/sites/errors.go rename to pkg/validator/sites_errors.go index adf34fe..682b7a5 100644 --- a/pkg/sites/errors.go +++ b/pkg/validator/sites_errors.go @@ -1,4 +1,4 @@ -package sites +package validator import "errors" diff --git a/pkg/sites/sites_test.go b/pkg/validator/sites_test.go similarity index 68% rename from pkg/sites/sites_test.go rename to pkg/validator/sites_test.go index b2713bc..90132c9 100644 --- a/pkg/sites/sites_test.go +++ b/pkg/validator/sites_test.go @@ -1,14 +1,21 @@ -package sites +package validator import ( "errors" "fmt" + "pyml-validator/pkg/model" "strings" "testing" "github.com/stretchr/testify/assert" ) +func TestNewSitesVaidator(t *testing.T) { + sv := NewSitesVaidator() + + assert.Equal(t, sv, &SitesValidator{}) +} + func TestValidateAPIVersion(t *testing.T) { for _, tc := range []struct { version int @@ -27,34 +34,34 @@ func TestValidateAPIVersion(t *testing.T) { func TestValidate(t *testing.T) { for _, tc := range []struct { name string - sitesYml SitesYml + sitesYml model.SitesYml expected error }{ { "valid only api version", - SitesYml{APIVersion: 1}, + model.SitesYml{APIVersion: 1}, nil, }, { "invalid api version", - SitesYml{APIVersion: 2}, + model.SitesYml{APIVersion: 2}, ErrInvalidAPIVersion, }, { "valid domain maps", - SitesYml{ + model.SitesYml{ APIVersion: 1, - DomainMaps: DomainMaps{ - "dev": DomainMapByEnvironment{ + DomainMaps: model.DomainMaps{ + "dev": model.DomainMapByEnvironment{ 1: "blog1.dev-mysite.pantheonsite.io", }, - "test": DomainMapByEnvironment{ + "test": model.DomainMapByEnvironment{ 1: "blog1.dev-mysite.pantheonsite.io", }, - "live": DomainMapByEnvironment{ + "live": model.DomainMapByEnvironment{ 1: "blog1.mysite.com", }, - "autopilot": DomainMapByEnvironment{ + "autopilot": model.DomainMapByEnvironment{ 1: "blog1.autopilot-mysite.pantheonsite.io", }, }, @@ -63,13 +70,13 @@ func TestValidate(t *testing.T) { }, { "invalid domain maps long env", - SitesYml{ + model.SitesYml{ APIVersion: 1, - DomainMaps: DomainMaps{ - "dev": DomainMapByEnvironment{ + DomainMaps: model.DomainMaps{ + "dev": model.DomainMapByEnvironment{ 1: "blog1.dev-mysite.pantheonsite.io", }, - "mylongmultidevname": DomainMapByEnvironment{ + "mylongmultidevname": model.DomainMapByEnvironment{ 1: "blog1.mylongmultidevname-mysite.pantheonsite.io", }, }, @@ -78,13 +85,13 @@ func TestValidate(t *testing.T) { }, { "invalid domain maps bad env name", - SitesYml{ + model.SitesYml{ APIVersion: 1, - DomainMaps: DomainMaps{ - "dev": DomainMapByEnvironment{ + DomainMaps: model.DomainMaps{ + "dev": model.DomainMapByEnvironment{ 1: "blog1.dev-mysite.pantheonsite.io", }, - "feat_branch": DomainMapByEnvironment{ + "feat_branch": model.DomainMapByEnvironment{ 1: "blog1.feat-branch-mysite.pantheonsite.io", }, }, @@ -93,10 +100,10 @@ func TestValidate(t *testing.T) { }, { "invalid domain maps too many domains", - SitesYml{ + model.SitesYml{ APIVersion: 1, - DomainMaps: DomainMaps{ - "dev": DomainMapByEnvironment{ + DomainMaps: model.DomainMaps{ + "dev": model.DomainMapByEnvironment{ 1: "blog1.dev-mysite.pantheonsite.io", 2: "blog2.dev-mysite.pantheonsite.io", 3: "blog3.dev-mysite.pantheonsite.io", @@ -127,7 +134,7 @@ func TestValidate(t *testing.T) { 28: "blog28.dev-mysite.pantheonsite.io", 29: "blog29.dev-mysite.pantheonsite.io", }, - "feat_branch": DomainMapByEnvironment{ + "feat_branch": model.DomainMapByEnvironment{ 1: "blog1.feat-branch-mysite.pantheonsite.io", }, }, @@ -136,7 +143,8 @@ func TestValidate(t *testing.T) { }, } { t.Run(tc.name, func(t *testing.T) { - err := validate(tc.sitesYml) + v := NewSitesVaidator() + err := v.(*SitesValidator).validate(tc.sitesYml) if tc.expected == nil { assert.NoError(t, err) return @@ -164,7 +172,7 @@ func TestValidateFromYaml(t *testing.T) { yaml: ` --- api_version: 2`, - expected: errors.New("Invalid API Version. Must be '1'"), + expected: ErrInvalidAPIVersion, }, } { t.Run(tc.name, func(t *testing.T) { @@ -172,11 +180,41 @@ func TestValidateFromYaml(t *testing.T) { // Yaml doesn't like tabs, but lets us make our test cases prettier strings.ReplaceAll(tc.yaml, "\t", ""), ) - err := ValidateFromYaml(yaml) + + v := NewSitesVaidator() + err := v.ValidateFromYaml(yaml) if tc.expected == nil { assert.NoError(t, err) return } + // TODO: assert.ErrorIs would be a better test. + assert.EqualError(t, err, tc.expected.Error()) + }) + } +} + +func TestValidateFromFilePath(t *testing.T) { + for _, tc := range []struct { + fixtureName string + expected error + }{ + { + "invalid_api_version_only", ErrInvalidAPIVersion, + }, + { + "valid_api_version_only", nil, + }, + } { + t.Run(tc.fixtureName, func(t *testing.T) { + v := NewSitesVaidator() + filePath := fmt.Sprintf("../../fixtures/%s.yml", tc.fixtureName) + err := v.ValidateFromFilePath(filePath) + if tc.expected == nil { + assert.NoError(t, err) + return + } + + // TODO: assert.ErrorIs would be a better test. assert.EqualError(t, err, tc.expected.Error()) }) } diff --git a/pkg/validator/validator.go b/pkg/validator/validator.go new file mode 100644 index 0000000..3f54815 --- /dev/null +++ b/pkg/validator/validator.go @@ -0,0 +1,6 @@ +package validator + +type Validator interface { + ValidateFromYaml(y []byte) error + ValidateFromFilePath(s string) error +} From f7a1fe60973b191b8607cc127d744cd617cd5545 Mon Sep 17 00:00:00 2001 From: Phil Tyler Date: Mon, 6 Feb 2023 13:27:04 -0800 Subject: [PATCH 10/23] coverage --- fixtures/invalid_yaml.yml | 1 + pkg/validator/sites_test.go | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 fixtures/invalid_yaml.yml diff --git a/fixtures/invalid_yaml.yml b/fixtures/invalid_yaml.yml new file mode 100644 index 0000000..6f57dd2 --- /dev/null +++ b/fixtures/invalid_yaml.yml @@ -0,0 +1 @@ +this is not good yaml. \ No newline at end of file diff --git a/pkg/validator/sites_test.go b/pkg/validator/sites_test.go index 90132c9..e74aee4 100644 --- a/pkg/validator/sites_test.go +++ b/pkg/validator/sites_test.go @@ -8,6 +8,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "gopkg.in/yaml.v3" ) func TestNewSitesVaidator(t *testing.T) { @@ -141,6 +142,24 @@ func TestValidate(t *testing.T) { }, errors.New(`"dev" has too many domains listed. Maximum is 25`), }, + { + "invalid hostname", + model.SitesYml{ + APIVersion: 1, + DomainMaps: model.DomainMaps{ + "dev": model.DomainMapByEnvironment{ + 1: "blog1.dev-mysite.pantheonsite.io", + }, + "test": model.DomainMapByEnvironment{ + 1: "$(sudo do something dangerous)", + }, + "live": model.DomainMapByEnvironment{ + 1: "blog1.mysite.com", + }, + }, + }, + errors.New(`"$(sudo do something dangerous)" is not a valid hostname`), + }, } { t.Run(tc.name, func(t *testing.T) { v := NewSitesVaidator() @@ -174,6 +193,15 @@ func TestValidateFromYaml(t *testing.T) { api_version: 2`, expected: ErrInvalidAPIVersion, }, + { + name: "invalid api_version ", + yaml: `this is not good yaml`, + expected: &yaml.TypeError{ + Errors: []string{ + "line 1: cannot unmarshal !!str `this is...` into model.SitesYml", + }, + }, + }, } { t.Run(tc.name, func(t *testing.T) { yaml := []byte( @@ -204,6 +232,13 @@ func TestValidateFromFilePath(t *testing.T) { { "valid_api_version_only", nil, }, + { + "invalid_yaml", &yaml.TypeError{ + Errors: []string{ + "line 1: cannot unmarshal !!str `this is...` into model.SitesYml", + }, + }, + }, } { t.Run(tc.fixtureName, func(t *testing.T) { v := NewSitesVaidator() From a29d21340448f4fcb7d4a184c173bb6e15ef2430 Mon Sep 17 00:00:00 2001 From: Phil Tyler Date: Mon, 6 Feb 2023 13:30:38 -0800 Subject: [PATCH 11/23] coverage --- fixtures/invalid_yaml.yml | 1 - pkg/validator/sites_test.go | 8 +++----- 2 files changed, 3 insertions(+), 6 deletions(-) delete mode 100644 fixtures/invalid_yaml.yml diff --git a/fixtures/invalid_yaml.yml b/fixtures/invalid_yaml.yml deleted file mode 100644 index 6f57dd2..0000000 --- a/fixtures/invalid_yaml.yml +++ /dev/null @@ -1 +0,0 @@ -this is not good yaml. \ No newline at end of file diff --git a/pkg/validator/sites_test.go b/pkg/validator/sites_test.go index e74aee4..23190c6 100644 --- a/pkg/validator/sites_test.go +++ b/pkg/validator/sites_test.go @@ -233,11 +233,9 @@ func TestValidateFromFilePath(t *testing.T) { "valid_api_version_only", nil, }, { - "invalid_yaml", &yaml.TypeError{ - Errors: []string{ - "line 1: cannot unmarshal !!str `this is...` into model.SitesYml", - }, - }, + "this_file_does_not_exist", errors.New( + "error reading YAML file: open ../../fixtures/this_file_does_not_exist.yml: no such file or directory", + ), }, } { t.Run(tc.fixtureName, func(t *testing.T) { From 2e455fbcee3e1fe46b3de034a3aae030d58a0356 Mon Sep 17 00:00:00 2001 From: Phil Tyler Date: Mon, 6 Feb 2023 14:09:43 -0800 Subject: [PATCH 12/23] cant stop refactoring --- cmd/pantheon.go | 23 ++++++++++-- cmd/sites.go | 8 +++- .../{ => sites}/invalid_api_version_only.yml | 0 .../{ => sites}/valid_api_version_only.yml | 0 pkg/validator/pantheon.go | 22 +++++++++++ pkg/validator/sites.go | 5 --- pkg/validator/sites_test.go | 37 ++++++++++--------- pkg/validator/validator.go | 13 +++++++ pkg/validator/validator_test.go | 30 +++++++++++++++ 9 files changed, 110 insertions(+), 28 deletions(-) rename fixtures/{ => sites}/invalid_api_version_only.yml (100%) rename fixtures/{ => sites}/valid_api_version_only.yml (100%) create mode 100644 pkg/validator/pantheon.go create mode 100644 pkg/validator/validator_test.go diff --git a/cmd/pantheon.go b/cmd/pantheon.go index 4b0082d..dc334cc 100644 --- a/cmd/pantheon.go +++ b/cmd/pantheon.go @@ -1,7 +1,8 @@ package cmd import ( - "log" + "fmt" + "pyml-validator/pkg/validator" "github.com/spf13/cobra" ) @@ -14,7 +15,23 @@ var pantheonCommand = &cobra.Command{ Use: "pantheon", Short: "validate pantheon.yml", Long: `Validate pantheon.yml`, - Run: func(cmd *cobra.Command, args []string) { - log.Fatal("Not yet implemented.") + RunE: func(cmd *cobra.Command, args []string) error { + // Is there a better way to do this? Without this we print usage on error exits. + // If we override at the root level, we don't get usage when we _do_ want it. + cmd.SilenceUsage = true + + v, err := validator.ValidatorFactory("pantheon") + if err != nil { + return err + } + + err = v.ValidateFromFilePath(FilePath) + + if err != nil { + return err + } + + fmt.Println("✨ pantheon.yml is valid") + return nil }, } diff --git a/cmd/sites.go b/cmd/sites.go index cc19624..fd6e2fd 100644 --- a/cmd/sites.go +++ b/cmd/sites.go @@ -20,8 +20,12 @@ var sitesCommand = &cobra.Command{ // If we override at the root level, we don't get usage when we _do_ want it. cmd.SilenceUsage = true - v := validator.NewSitesVaidator() - err := v.ValidateFromFilePath(FilePath) + v, err := validator.ValidatorFactory("sites") + if err != nil { + return err + } + + err = v.ValidateFromFilePath(FilePath) if err != nil { return err diff --git a/fixtures/invalid_api_version_only.yml b/fixtures/sites/invalid_api_version_only.yml similarity index 100% rename from fixtures/invalid_api_version_only.yml rename to fixtures/sites/invalid_api_version_only.yml diff --git a/fixtures/valid_api_version_only.yml b/fixtures/sites/valid_api_version_only.yml similarity index 100% rename from fixtures/valid_api_version_only.yml rename to fixtures/sites/valid_api_version_only.yml diff --git a/pkg/validator/pantheon.go b/pkg/validator/pantheon.go new file mode 100644 index 0000000..ffab567 --- /dev/null +++ b/pkg/validator/pantheon.go @@ -0,0 +1,22 @@ +package validator + +import ( + "errors" + "fmt" + "os" +) + +type PantheonValidator struct{} + +// ValidateFromYaml asserts a given pantheon.yaml file is valid. +func (v *PantheonValidator) ValidateFromYaml(y []byte) error { + return errors.New("Not yet implemented") +} + +func (v *PantheonValidator) ValidateFromFilePath(filePath string) error { + yFile, err := os.ReadFile(filePath) + if err != nil { + return fmt.Errorf("error reading YAML file: %w", err) + } + return v.ValidateFromYaml(yFile) +} diff --git a/pkg/validator/sites.go b/pkg/validator/sites.go index 2ad75ba..991fd76 100644 --- a/pkg/validator/sites.go +++ b/pkg/validator/sites.go @@ -21,11 +21,6 @@ var ( type SitesValidator struct{} -// NewSitesValidator constructs a new SitesValidator method. -func NewSitesVaidator() Validator { - return &SitesValidator{} -} - // ValidateFromYaml asserts a given sites.yaml file is valid. func (v *SitesValidator) ValidateFromYaml(y []byte) error { var s model.SitesYml diff --git a/pkg/validator/sites_test.go b/pkg/validator/sites_test.go index 23190c6..149ae46 100644 --- a/pkg/validator/sites_test.go +++ b/pkg/validator/sites_test.go @@ -8,15 +8,10 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "gopkg.in/yaml.v3" ) -func TestNewSitesVaidator(t *testing.T) { - sv := NewSitesVaidator() - - assert.Equal(t, sv, &SitesValidator{}) -} - func TestValidateAPIVersion(t *testing.T) { for _, tc := range []struct { version int @@ -162,8 +157,9 @@ func TestValidate(t *testing.T) { }, } { t.Run(tc.name, func(t *testing.T) { - v := NewSitesVaidator() - err := v.(*SitesValidator).validate(tc.sitesYml) + v, err := ValidatorFactory("sites") + require.NoError(t, err) + err = v.(*SitesValidator).validate(tc.sitesYml) if tc.expected == nil { assert.NoError(t, err) return @@ -209,8 +205,9 @@ func TestValidateFromYaml(t *testing.T) { strings.ReplaceAll(tc.yaml, "\t", ""), ) - v := NewSitesVaidator() - err := v.ValidateFromYaml(yaml) + v, err := ValidatorFactory("sites") + require.NoError(t, err) + err = v.ValidateFromYaml(yaml) if tc.expected == nil { assert.NoError(t, err) return @@ -223,25 +220,29 @@ func TestValidateFromYaml(t *testing.T) { func TestValidateFromFilePath(t *testing.T) { for _, tc := range []struct { + validator string fixtureName string expected error }{ { - "invalid_api_version_only", ErrInvalidAPIVersion, + "sites", "invalid_api_version_only", ErrInvalidAPIVersion, }, { - "valid_api_version_only", nil, + "sites", "valid_api_version_only", nil, }, { - "this_file_does_not_exist", errors.New( - "error reading YAML file: open ../../fixtures/this_file_does_not_exist.yml: no such file or directory", + "sites", "this_file_does_not_exist", errors.New( + "error reading YAML file: open ../../fixtures/sites/this_file_does_not_exist.yml: no such file or directory", ), }, } { - t.Run(tc.fixtureName, func(t *testing.T) { - v := NewSitesVaidator() - filePath := fmt.Sprintf("../../fixtures/%s.yml", tc.fixtureName) - err := v.ValidateFromFilePath(filePath) + fxTestPathName := fmt.Sprintf(`%s/%s`, tc.validator, tc.fixtureName) + t.Run(fxTestPathName, func(t *testing.T) { + v, err := ValidatorFactory(tc.validator) + require.NoError(t, err) + filePath := fmt.Sprintf("../../fixtures/%s.yml", fxTestPathName) + + err = v.ValidateFromFilePath(filePath) if tc.expected == nil { assert.NoError(t, err) return diff --git a/pkg/validator/validator.go b/pkg/validator/validator.go index 3f54815..f43dfb2 100644 --- a/pkg/validator/validator.go +++ b/pkg/validator/validator.go @@ -1,6 +1,19 @@ package validator +import "fmt" + type Validator interface { ValidateFromYaml(y []byte) error ValidateFromFilePath(s string) error } + +func ValidatorFactory(v string) (Validator, error) { + switch v { + case "sites": + return &SitesValidator{}, nil + case "pantheon": + return &PantheonValidator{}, nil + default: + return nil, fmt.Errorf(`%q is not a valid validator.`, v) + } +} diff --git a/pkg/validator/validator_test.go b/pkg/validator/validator_test.go new file mode 100644 index 0000000..690fac6 --- /dev/null +++ b/pkg/validator/validator_test.go @@ -0,0 +1,30 @@ +package validator + +import ( + "errors" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestValidatorFactory(t *testing.T) { + for _, tc := range []struct { + name string + expected Validator + expectedErr error + }{ + {"sites", &SitesValidator{}, nil}, + {"pantheon", &PantheonValidator{}, nil}, + {"foo", nil, errors.New(`"foo" is not a valid validator.`)}, + } { + t.Run(tc.name, func(t *testing.T) { + result, err := ValidatorFactory(tc.name) + if tc.expectedErr != nil { + assert.EqualError(t, err, tc.expectedErr.Error()) + return + } + assert.NoError(t, err) + assert.Equal(t, result, tc.expected) + }) + } +} From 4869361e57e0305c768c2a94dd93dfc6fb180635 Mon Sep 17 00:00:00 2001 From: Phil Tyler Date: Mon, 6 Feb 2023 14:40:39 -0800 Subject: [PATCH 13/23] README --- README.MD | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.MD b/README.MD index 96e623e..6b4185b 100644 --- a/README.MD +++ b/README.MD @@ -4,8 +4,10 @@ A utility for validating a sites.yml file on a pantheon site during WordPress mu # Testing +[![Coverage Status](https://coveralls.io/repos/github/pantheon-systems/pyml-validator/badge.svg?t=PGhafd)](https://coveralls.io/github/pantheon-systems/pyml-validator) + `make test` runs linting and testing. # Releases -Automatically releases on merge to main via autotag + goreleaser. See [Autotag Readme](https://github.com/pantheon-systems/autotag) for details on how the SemVer is determined. \ No newline at end of file +Automatically releases on merge to main via autotag + goreleaser. See [Autotag Readme](https://github.com/pantheon-systems/autotag) for details on how the SemVer is determined. Note, with goreleaser, each commit merged will become a line item in the release's Changelog. Take note to use squashing and/or rebase to ensure helpful and informative commit messages. \ No newline at end of file From b8d01c47c7c97b9a3ae2f1458de5d42edca50b1f Mon Sep 17 00:00:00 2001 From: Phil Tyler Date: Mon, 6 Feb 2023 14:44:17 -0800 Subject: [PATCH 14/23] fixup --- pkg/validator/pantheon.go | 4 ++-- pkg/validator/validator.go | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/pkg/validator/pantheon.go b/pkg/validator/pantheon.go index ffab567..1ef104d 100644 --- a/pkg/validator/pantheon.go +++ b/pkg/validator/pantheon.go @@ -1,7 +1,6 @@ package validator import ( - "errors" "fmt" "os" ) @@ -9,8 +8,9 @@ import ( type PantheonValidator struct{} // ValidateFromYaml asserts a given pantheon.yaml file is valid. +// As this has not been implemented, nothing is invalid. func (v *PantheonValidator) ValidateFromYaml(y []byte) error { - return errors.New("Not yet implemented") + return nil } func (v *PantheonValidator) ValidateFromFilePath(filePath string) error { diff --git a/pkg/validator/validator.go b/pkg/validator/validator.go index f43dfb2..8d1bbce 100644 --- a/pkg/validator/validator.go +++ b/pkg/validator/validator.go @@ -1,6 +1,8 @@ package validator -import "fmt" +import ( + "fmt" +) type Validator interface { ValidateFromYaml(y []byte) error From c1b0eb262d3e963b4871267c31068861c97bc5d5 Mon Sep 17 00:00:00 2001 From: Phil Tyler Date: Mon, 6 Feb 2023 15:00:51 -0800 Subject: [PATCH 15/23] tighten up cmds --- cmd/pantheon.go | 37 ----------------------------------- cmd/sites.go | 36 ++++++++++++++++++---------------- cmd/validators.go | 49 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 54 deletions(-) delete mode 100644 cmd/pantheon.go create mode 100644 cmd/validators.go diff --git a/cmd/pantheon.go b/cmd/pantheon.go deleted file mode 100644 index dc334cc..0000000 --- a/cmd/pantheon.go +++ /dev/null @@ -1,37 +0,0 @@ -package cmd - -import ( - "fmt" - "pyml-validator/pkg/validator" - - "github.com/spf13/cobra" -) - -func init() { - rootCmd.AddCommand(pantheonCommand) -} - -var pantheonCommand = &cobra.Command{ - Use: "pantheon", - Short: "validate pantheon.yml", - Long: `Validate pantheon.yml`, - RunE: func(cmd *cobra.Command, args []string) error { - // Is there a better way to do this? Without this we print usage on error exits. - // If we override at the root level, we don't get usage when we _do_ want it. - cmd.SilenceUsage = true - - v, err := validator.ValidatorFactory("pantheon") - if err != nil { - return err - } - - err = v.ValidateFromFilePath(FilePath) - - if err != nil { - return err - } - - fmt.Println("✨ pantheon.yml is valid") - return nil - }, -} diff --git a/cmd/sites.go b/cmd/sites.go index fd6e2fd..b31d7d2 100644 --- a/cmd/sites.go +++ b/cmd/sites.go @@ -16,22 +16,24 @@ var sitesCommand = &cobra.Command{ Short: "validate sites.yml", Long: `Validate sites.yml`, RunE: func(cmd *cobra.Command, args []string) error { - // Is there a better way to do this? Without this we print usage on error exits. - // If we override at the root level, we don't get usage when we _do_ want it. - cmd.SilenceUsage = true - - v, err := validator.ValidatorFactory("sites") - if err != nil { - return err - } - - err = v.ValidateFromFilePath(FilePath) - - if err != nil { - return err - } - - fmt.Println("✨ sites.yml is valid") - return nil + return validatorCommand(cmd) }, } + +func validatorCommand(cmd *cobra.Command) error { + // Is there a better way to do this? Without this we print usage on error exits. + // If we override at the root level, we don't get usage when we _do_ want it. + cmd.SilenceUsage = true + + v, err := validator.ValidatorFactory(cmd.Use) + if err != nil { + return err + } + + err = v.ValidateFromFilePath(FilePath) + if err != nil { + return err + } + fmt.Printf("✨ %s.yml is valid\n", cmd.Use) + return nil +} diff --git a/cmd/validators.go b/cmd/validators.go new file mode 100644 index 0000000..23832ed --- /dev/null +++ b/cmd/validators.go @@ -0,0 +1,49 @@ +package cmd + +import ( + "fmt" + "pyml-validator/pkg/validator" + + "github.com/spf13/cobra" +) + +func validatorCommand(cmd *cobra.Command) error { + // Is there a better way to do this? Without this we print usage on error exits. + // If we override at the root level, we don't get usage when we _do_ want it. + cmd.SilenceUsage = true + + v, err := validator.ValidatorFactory(cmd.Use) + if err != nil { + return err + } + + err = v.ValidateFromFilePath(FilePath) + if err != nil { + return err + } + fmt.Printf("✨ %s.yml is valid\n", cmd.Use) + return nil +} + +var sitesCommand = &cobra.Command{ + Use: "sites", + Short: "validate sites.yml", + Long: `Validate sites.yml`, + RunE: func(cmd *cobra.Command, args []string) error { + return validatorCommand(cmd) + }, +} + +var pantheonCommand = &cobra.Command{ + Use: "pantheon", + Short: "validate pantheon.yml", + Long: `Validate pantheon.yml. For more information, see https://pantheon.io/docs/pantheon-yml`, + RunE: func(cmd *cobra.Command, args []string) error { + return validatorCommand(cmd) + }, +} + +func init() { + rootCmd.AddCommand(pantheonCommand) + rootCmd.AddCommand(sitesCommand) +} From fb0f82c1fb8de316f0b9159a5ffc9228bca33814 Mon Sep 17 00:00:00 2001 From: Phil Tyler Date: Mon, 6 Feb 2023 15:09:55 -0800 Subject: [PATCH 16/23] valid fixture --- cmd/sites.go | 39 ------------------------------------- fixtures/sites/valid.yml | 19 ++++++++++++++++++ pkg/validator/sites_test.go | 3 +++ 3 files changed, 22 insertions(+), 39 deletions(-) delete mode 100644 cmd/sites.go create mode 100644 fixtures/sites/valid.yml diff --git a/cmd/sites.go b/cmd/sites.go deleted file mode 100644 index b31d7d2..0000000 --- a/cmd/sites.go +++ /dev/null @@ -1,39 +0,0 @@ -package cmd - -import ( - "fmt" - "pyml-validator/pkg/validator" - - "github.com/spf13/cobra" -) - -func init() { - rootCmd.AddCommand(sitesCommand) -} - -var sitesCommand = &cobra.Command{ - Use: "sites", - Short: "validate sites.yml", - Long: `Validate sites.yml`, - RunE: func(cmd *cobra.Command, args []string) error { - return validatorCommand(cmd) - }, -} - -func validatorCommand(cmd *cobra.Command) error { - // Is there a better way to do this? Without this we print usage on error exits. - // If we override at the root level, we don't get usage when we _do_ want it. - cmd.SilenceUsage = true - - v, err := validator.ValidatorFactory(cmd.Use) - if err != nil { - return err - } - - err = v.ValidateFromFilePath(FilePath) - if err != nil { - return err - } - fmt.Printf("✨ %s.yml is valid\n", cmd.Use) - return nil -} diff --git a/fixtures/sites/valid.yml b/fixtures/sites/valid.yml new file mode 100644 index 0000000..8c467b6 --- /dev/null +++ b/fixtures/sites/valid.yml @@ -0,0 +1,19 @@ +--- +api_version: 1 +domain_maps: + dev: + 1: about.dev-mysite.pantheonsite.io + 2: employee-resources.dev-mysite.pantheonsite.io + 3: staff-portal.dev-mysite.pantheonsite.io + test: + 1: about.dev-mysite.pantheonsite.io + 2: employee-resources.dev-mysite.pantheonsite.io + 3: staff-portal.dev-mysite.pantheonsite.io + live: + 1: about.mysite.com + 2: employee-resources.mysite.com + 3: staff-portal.mysite.com + autopilot: + 1: about.autopilot-mysite.pantheonsite.io + 2: employee-resources.autopilot-mysite.pantheonsite.io + 3: staff-portal.autopilot-mysite.pantheonsite.io \ No newline at end of file diff --git a/pkg/validator/sites_test.go b/pkg/validator/sites_test.go index 149ae46..f9b1dc0 100644 --- a/pkg/validator/sites_test.go +++ b/pkg/validator/sites_test.go @@ -230,6 +230,9 @@ func TestValidateFromFilePath(t *testing.T) { { "sites", "valid_api_version_only", nil, }, + { + "sites", "valid", nil, + }, { "sites", "this_file_does_not_exist", errors.New( "error reading YAML file: open ../../fixtures/sites/this_file_does_not_exist.yml: no such file or directory", From 615b2c61bcbd0776b768c578ee604583fccd4cc5 Mon Sep 17 00:00:00 2001 From: Phil Tyler Date: Mon, 6 Feb 2023 15:17:50 -0800 Subject: [PATCH 17/23] valid yml with explanation comments --- fixtures/sites/valid.yml | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/fixtures/sites/valid.yml b/fixtures/sites/valid.yml index 8c467b6..85cf479 100644 --- a/fixtures/sites/valid.yml +++ b/fixtures/sites/valid.yml @@ -1,7 +1,17 @@ --- -api_version: 1 +api_version: 1 # Currently only one api version. + +# "domain_maps" is a collection of blog URLs for each environment used to +# facilitate search-replace of a WordPress Multisite (WPMS) across pantheon +# environments. Each key of "domain_maps" must be a valid environment name. domain_maps: + # environment: + # i.e. dev, test, live, feat-branch, &c. dev: + # each environment collection maps the blog ID to its URL. A url must be + # set in both the target and source environments for search-replace to be + # run. + # i.e. 1: blog1-mysite.com 1: about.dev-mysite.pantheonsite.io 2: employee-resources.dev-mysite.pantheonsite.io 3: staff-portal.dev-mysite.pantheonsite.io @@ -16,4 +26,6 @@ domain_maps: autopilot: 1: about.autopilot-mysite.pantheonsite.io 2: employee-resources.autopilot-mysite.pantheonsite.io - 3: staff-portal.autopilot-mysite.pantheonsite.io \ No newline at end of file + 3: staff-portal.autopilot-mysite.pantheonsite.io + +# Anything else in the file will be ignored, but not rejected. \ No newline at end of file From 59c0145c25046473d14ed8e47fe6852c31ea5b37 Mon Sep 17 00:00:00 2001 From: Phil Tyler Date: Mon, 6 Feb 2023 15:20:37 -0800 Subject: [PATCH 18/23] Readme usage --- README.MD | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.MD b/README.MD index 6b4185b..8b88d03 100644 --- a/README.MD +++ b/README.MD @@ -2,6 +2,20 @@ A utility for validating a sites.yml file on a pantheon site during WordPress multisites' search-replace tasks. Asprirationally to include pantheon.yml validation in the future. +# Usage + + +## Sites.yml +``` +$ pyml-validator sites -f path/to/sites.yml +``` + +## Pantheon.yml +Note, validation of pantheon.yml is unimplemented, so any file reads as valid. +``` +$ pyml-validator pantheon -f path/to/pantheon.yml +``` + # Testing [![Coverage Status](https://coveralls.io/repos/github/pantheon-systems/pyml-validator/badge.svg?t=PGhafd)](https://coveralls.io/github/pantheon-systems/pyml-validator) From 94a0c413505c4837958ec7b27019b822b90b312c Mon Sep 17 00:00:00 2001 From: Phil Tyler Date: Mon, 6 Feb 2023 15:26:46 -0800 Subject: [PATCH 19/23] sites test --- pkg/validator/sites_test.go | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/pkg/validator/sites_test.go b/pkg/validator/sites_test.go index f9b1dc0..16f108d 100644 --- a/pkg/validator/sites_test.go +++ b/pkg/validator/sites_test.go @@ -218,32 +218,24 @@ func TestValidateFromYaml(t *testing.T) { } } -func TestValidateFromFilePath(t *testing.T) { +func TestValidateSitesFromFilePath(t *testing.T) { for _, tc := range []struct { - validator string fixtureName string expected error }{ + {"invalid_api_version_only", ErrInvalidAPIVersion}, + {"valid_api_version_only", nil}, + {"valid", nil}, { - "sites", "invalid_api_version_only", ErrInvalidAPIVersion, - }, - { - "sites", "valid_api_version_only", nil, - }, - { - "sites", "valid", nil, - }, - { - "sites", "this_file_does_not_exist", errors.New( + "this_file_does_not_exist", errors.New( "error reading YAML file: open ../../fixtures/sites/this_file_does_not_exist.yml: no such file or directory", ), }, } { - fxTestPathName := fmt.Sprintf(`%s/%s`, tc.validator, tc.fixtureName) - t.Run(fxTestPathName, func(t *testing.T) { - v, err := ValidatorFactory(tc.validator) + t.Run(tc.fixtureName, func(t *testing.T) { + v, err := ValidatorFactory("sites") require.NoError(t, err) - filePath := fmt.Sprintf("../../fixtures/%s.yml", fxTestPathName) + filePath := fmt.Sprintf("../../fixtures/sites/%s.yml", tc.fixtureName) err = v.ValidateFromFilePath(filePath) if tc.expected == nil { From 25ac70e6aee3d812a5859af6a10af16fe8e0d6c4 Mon Sep 17 00:00:00 2001 From: Phil Tyler Date: Mon, 6 Feb 2023 15:32:22 -0800 Subject: [PATCH 20/23] fixture typo and readme link --- README.MD | 3 ++- fixtures/sites/valid.yml | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/README.MD b/README.MD index 8b88d03..e2f30da 100644 --- a/README.MD +++ b/README.MD @@ -4,12 +4,13 @@ A utility for validating a sites.yml file on a pantheon site during WordPress mu # Usage - ## Sites.yml ``` $ pyml-validator sites -f path/to/sites.yml ``` +See [this annotated fixture](./fixtures/sites.valid.yml) for an example of a valid sites.yml file. + ## Pantheon.yml Note, validation of pantheon.yml is unimplemented, so any file reads as valid. ``` diff --git a/fixtures/sites/valid.yml b/fixtures/sites/valid.yml index 85cf479..e0fa0d4 100644 --- a/fixtures/sites/valid.yml +++ b/fixtures/sites/valid.yml @@ -16,9 +16,9 @@ domain_maps: 2: employee-resources.dev-mysite.pantheonsite.io 3: staff-portal.dev-mysite.pantheonsite.io test: - 1: about.dev-mysite.pantheonsite.io - 2: employee-resources.dev-mysite.pantheonsite.io - 3: staff-portal.dev-mysite.pantheonsite.io + 1: about.test-mysite.pantheonsite.io + 2: employee-resources.test-mysite.pantheonsite.io + 3: staff-portal.test-mysite.pantheonsite.io live: 1: about.mysite.com 2: employee-resources.mysite.com From 772f4ec01b45714626e930b0c1818de1518c0fb5 Mon Sep 17 00:00:00 2001 From: Phil Tyler Date: Mon, 6 Feb 2023 15:33:06 -0800 Subject: [PATCH 21/23] fixup --- README.MD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.MD b/README.MD index e2f30da..0b61378 100644 --- a/README.MD +++ b/README.MD @@ -9,7 +9,7 @@ A utility for validating a sites.yml file on a pantheon site during WordPress mu $ pyml-validator sites -f path/to/sites.yml ``` -See [this annotated fixture](./fixtures/sites.valid.yml) for an example of a valid sites.yml file. +See [this annotated fixture](./fixtures/sites/valid.yml) for an example of a valid sites.yml file. ## Pantheon.yml Note, validation of pantheon.yml is unimplemented, so any file reads as valid. From 74aaf40ca5a4b7b354b1403253ada7ea0e8f1ae1 Mon Sep 17 00:00:00 2001 From: Phil Tyler Date: Mon, 6 Feb 2023 17:26:46 -0800 Subject: [PATCH 22/23] replace resolves dependency vuln --- go.mod | 2 ++ go.sum | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 838c97d..873d994 100644 --- a/go.mod +++ b/go.mod @@ -14,3 +14,5 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/spf13/pflag v1.0.5 // indirect ) + +replace gopkg.in/yaml.v3 => gopkg.in/yaml.v3 v3.0.1 diff --git a/go.sum b/go.sum index 4e43bfc..c7e9ea7 100644 --- a/go.sum +++ b/go.sum @@ -21,6 +21,5 @@ github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKs github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 438b21353aa5077d90ae848659fa3dec955fc448 Mon Sep 17 00:00:00 2001 From: Phil Tyler Date: Wed, 8 Feb 2023 15:23:54 -0800 Subject: [PATCH 23/23] domain map count in error message --- pkg/validator/sites.go | 5 +++-- pkg/validator/sites_test.go | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/pkg/validator/sites.go b/pkg/validator/sites.go index 991fd76..c2128ad 100644 --- a/pkg/validator/sites.go +++ b/pkg/validator/sites.go @@ -58,8 +58,9 @@ func validateDomainMaps(domainMaps map[string]model.DomainMapByEnvironment) erro if !ValidMultidevNameRegex.MatchString(env) { return fmt.Errorf("%q is not a valid environment name", env) } - if len(domainMap) > MaxDomainMaps { - return fmt.Errorf("%q has too many domains listed. Maximum is %d", env, MaxDomainMaps) + domainMapCount := len(domainMap) + if domainMapCount > MaxDomainMaps { + return fmt.Errorf("%q has too many domains listed (%d). Maximum is %d", env, domainMapCount, MaxDomainMaps) } for _, domain := range domainMap { if !ValidHostnameRegex.MatchString(domain) { diff --git a/pkg/validator/sites_test.go b/pkg/validator/sites_test.go index 16f108d..40b8ea9 100644 --- a/pkg/validator/sites_test.go +++ b/pkg/validator/sites_test.go @@ -135,7 +135,7 @@ func TestValidate(t *testing.T) { }, }, }, - errors.New(`"dev" has too many domains listed. Maximum is 25`), + errors.New(`"dev" has too many domains listed (29). Maximum is 25`), }, { "invalid hostname",