diff --git a/go.mod b/go.mod index cd6f42d91..ea9539226 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/argoproj/gitops-engine v0.5.2 github.com/cenkalti/backoff/v4 v4.1.2 github.com/cheggaaa/pb v1.0.29 + github.com/go-playground/validator/v10 v10.11.0 github.com/go-resty/resty/v2 v2.7.0 github.com/google/go-cmp v0.5.6 github.com/google/go-github/v42 v42.0.0 @@ -21,7 +22,7 @@ require ( github.com/stretchr/testify v1.7.0 github.com/tcnksm/go-input v0.0.0-20180404061846-548a7d7a8ee8 github.com/xanzy/go-gitlab v0.55.1 - golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa + golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 gopkg.in/gookit/color.v1 v1.1.6 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b @@ -78,6 +79,8 @@ require ( github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonreference v0.19.5 // indirect github.com/go-openapi/swag v0.19.14 // indirect + github.com/go-playground/locales v0.14.0 // indirect + github.com/go-playground/universal-translator v0.18.0 // indirect github.com/go-redis/cache/v8 v8.4.2 // indirect github.com/go-redis/redis/v8 v8.11.3 // indirect github.com/gobwas/glob v0.2.3 // indirect @@ -110,6 +113,7 @@ require ( github.com/klauspost/compress v1.13.6 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect + github.com/leodido/go-urn v1.2.1 // indirect github.com/lib/pq v1.10.0 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/magiconair/properties v1.8.5 // indirect diff --git a/go.sum b/go.sum index 5b706b98a..4ffbe5baf 100644 --- a/go.sum +++ b/go.sum @@ -528,6 +528,14 @@ github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/ github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= github.com/go-ozzo/ozzo-validation v3.5.0+incompatible/go.mod h1:gsEKFIVnabGBt6mXmxK0MoFy+cZoTJY6mu5Ll3LVLBU= +github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= +github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= +github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= +github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= +github.com/go-playground/validator/v10 v10.11.0 h1:0W+xRM511GY47Yy3bZUbJVitCNg2BOGlCyvTqsp/xIw= +github.com/go-playground/validator/v10 v10.11.0/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= github.com/go-redis/cache/v8 v8.4.2 h1:8YbsmnU1Ws3TKS6T+qALzYE/MlGE+A/lrlx1XTA3p6M= github.com/go-redis/cache/v8 v8.4.2/go.mod h1:X7Jjd69Ssbrf3xBQLtIDE0g3WcSbFoQiSGeb8QfEJ+g= github.com/go-redis/redis/v8 v8.11.3 h1:GCjoYp8c+yQTJfc0n69iwSiHjvuAdruxl7elnZCxgt8= @@ -813,8 +821,9 @@ github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -824,6 +833,8 @@ github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= +github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.0 h1:Zx5DJFEYQXio93kgXnQ09fXNiUKsqv4OUEu2UtGcB1E= github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= @@ -1032,6 +1043,7 @@ github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoU github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -1097,6 +1109,9 @@ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6So github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= +github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.21.0/go.mod h1:ZPhntP/xmq1nnND05hhpAh2QMhSsA4UN3MGZ6O2J3hM= @@ -1336,8 +1351,8 @@ golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa h1:idItI2DDfCokpg0N51B2VtiLdJ4vAuXC9fnCb2gACo4= -golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 h1:0es+/5331RGQPcXlMfP+WrnIIS6dNnNRe0WB02W0F4M= +golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1444,6 +1459,7 @@ golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211209124913-491a49abca63 h1:iocB37TsdFuN6IBRZ+ry36wrkoV51/tl5vOWqkcPGvY= golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1570,6 +1586,7 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211113001501-0c823b97ae02 h1:7NCfEGl0sfUojmX78nK9pBJuUlSZWEJA/TwASvfiPLo= golang.org/x/sys v0.0.0-20211113001501-0c823b97ae02/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= diff --git a/internal/pkg/plugin/argocdapp/options.go b/internal/pkg/plugin/argocdapp/options.go index 7a0483caf..4b18bec34 100644 --- a/internal/pkg/plugin/argocdapp/options.go +++ b/internal/pkg/plugin/argocdapp/options.go @@ -9,7 +9,7 @@ type Options struct { // App is the struct for an ArgoCD app. type App struct { - Name string + Name string `validate:"required,dns1123subdomain"` Namespace string } @@ -22,6 +22,6 @@ type Destination struct { // Source is the struct for the source of an ArgoCD app. type Source struct { Valuefile string - Path string - RepoURL string + Path string `validate:"required"` + RepoURL string `validate:"required"` } diff --git a/internal/pkg/plugin/argocdapp/validate.go b/internal/pkg/plugin/argocdapp/validate.go index fdf0e6721..90004bf7b 100644 --- a/internal/pkg/plugin/argocdapp/validate.go +++ b/internal/pkg/plugin/argocdapp/validate.go @@ -1,31 +1,11 @@ package argocdapp import ( - "fmt" - - "k8s.io/apimachinery/pkg/util/validation" + "github.com/devstream-io/devstream/pkg/util/validator" ) // validate validates the options provided by the core. func validate(opts *Options) []error { - retErrors := make([]error, 0) - - if opts.App.Name == "" { - retErrors = append(retErrors, fmt.Errorf("app.name is empty")) - } - if errs := validation.IsDNS1123Subdomain(opts.App.Name); len(errs) != 0 { - for _, e := range errs { - retErrors = append(retErrors, fmt.Errorf("app.name %s is invalid: %s", opts.App.Name, e)) - } - } - - if opts.Source.Path == "" { - retErrors = append(retErrors, fmt.Errorf("source.path is empty")) - } - if opts.Source.RepoURL == "" { - retErrors = append(retErrors, fmt.Errorf("source.repoURL is empty")) - } - - return retErrors + return validator.Struct(opts) } diff --git a/internal/pkg/plugin/common/reposcaffolding/options.go b/internal/pkg/plugin/common/reposcaffolding/options.go index 7223b48f5..488df8644 100644 --- a/internal/pkg/plugin/common/reposcaffolding/options.go +++ b/internal/pkg/plugin/common/reposcaffolding/options.go @@ -1,12 +1,16 @@ package reposcaffolding -import "fmt" +import ( + "fmt" + + "github.com/devstream-io/devstream/pkg/util/validator" +) type Options struct { - Owner string - Org string - Repo string - Branch string + Owner string `validate:"required_without=Org"` + Org string `validate:"required_without=Owner"` + Repo string `validate:"required"` + Branch string `validate:"required"` PathWithNamespace string ImageRepo string `mapstructure:"image_repo"` } @@ -14,20 +18,9 @@ type Options struct { // Validate validates the options provided by the core. func Validate(opts *Options) []error { retErrors := make([]error, 0) - - // owner/org/repo/branch - if opts.Owner == "" && opts.Org == "" { - retErrors = append(retErrors, fmt.Errorf("owner and org are empty")) - } - if opts.Repo == "" { - retErrors = append(retErrors, fmt.Errorf("repo is empty")) - } - + retErrors = append(retErrors, validator.Struct(opts)...) // set PathWithNamespace for GitLab. GitHub won't need to use this opts.PathWithNamespace = fmt.Sprintf("%s/%s", opts.Owner, opts.Repo) - if opts.Branch == "" { - retErrors = append(retErrors, fmt.Errorf("branch is empty")) - } return retErrors } diff --git a/internal/pkg/plugin/githubactions/golang/jobs.go b/internal/pkg/plugin/githubactions/golang/jobs.go index be00547b1..3ab8a211c 100644 --- a/internal/pkg/plugin/githubactions/golang/jobs.go +++ b/internal/pkg/plugin/githubactions/golang/jobs.go @@ -50,22 +50,6 @@ type Tag struct { type Image struct { } -func (b *Build) Validate() []error { - retErrors := make([]error, 0) - - // TODO(daniel-hutao): what should we validate here? - - return retErrors -} - -func (t *Test) Validate() []error { - retErrors := make([]error, 0) - - // TODO(daniel-hutao): what should we validate here? - - return retErrors -} - func (d *Docker) Validate() []error { retErrors := make([]error, 0) diff --git a/internal/pkg/plugin/githubactions/golang/options.go b/internal/pkg/plugin/githubactions/golang/options.go index d13e6b6bf..6aed83c98 100644 --- a/internal/pkg/plugin/githubactions/golang/options.go +++ b/internal/pkg/plugin/githubactions/golang/options.go @@ -6,12 +6,12 @@ import ( // Options is the struct for configurations of the githubactions plugin. type Options struct { - Owner string - Org string - Repo string - Branch string - Language *ga.Language + Owner string `validate:"required_without=Org"` + Org string `validate:"required_without=Owner"` + Repo string `validate:"required"` + Branch string `validate:"required"` + Language *ga.Language `validate:"required"` Build *Build - Test *Test + Test *Test `validate:"required"` Docker *Docker } diff --git a/internal/pkg/plugin/githubactions/golang/validate.go b/internal/pkg/plugin/githubactions/golang/validate.go index c9703ae21..77a843ec0 100644 --- a/internal/pkg/plugin/githubactions/golang/validate.go +++ b/internal/pkg/plugin/githubactions/golang/validate.go @@ -2,43 +2,18 @@ package golang import ( "fmt" + + "github.com/devstream-io/devstream/pkg/util/validator" ) // validate validates the options provided by the core. func validate(opts *Options) []error { retErrors := make([]error, 0) - // owner/org/repo/branch - if opts.Owner == "" && opts.Org == "" { - retErrors = append(retErrors, fmt.Errorf("owner and org are empty")) - } - if opts.Repo == "" { - retErrors = append(retErrors, fmt.Errorf("repo is empty")) - } - if opts.Branch == "" { - retErrors = append(retErrors, fmt.Errorf("branch is empty")) - } - - // language - if opts.Language == nil { - retErrors = append(retErrors, fmt.Errorf("language is empty")) - } - if errs := opts.Language.Validate(); len(errs) != 0 { - for _, e := range errs { - retErrors = append(retErrors, fmt.Errorf("language is invalid: %s", e)) - } - } - - // jobs - if opts.Test == nil { - retErrors = append(retErrors, fmt.Errorf("test is empty")) + if errs := validator.Struct(opts); len(errs) != 0 { + retErrors = append(retErrors, errs...) } - if errs := opts.Test.Validate(); len(errs) != 0 { - for _, e := range errs { - retErrors = append(retErrors, fmt.Errorf("test is invalid: %s", e)) - } - } - + // too complex to validate automatically if opts.Docker == nil { return retErrors } diff --git a/internal/pkg/plugin/githubactions/nodejs/options.go b/internal/pkg/plugin/githubactions/nodejs/options.go index a9a4a45bd..7e0429b80 100644 --- a/internal/pkg/plugin/githubactions/nodejs/options.go +++ b/internal/pkg/plugin/githubactions/nodejs/options.go @@ -6,9 +6,9 @@ import ( // Options is the struct for configurations of the githubactions plugin. type Options struct { - Owner string - Org string - Repo string - Branch string - Language *ga.Language + Owner string `validate:"required_without=Org"` + Org string `validate:"required_without=Owner"` + Repo string `validate:"required"` + Branch string `validate:"required"` + Language *ga.Language `validate:"required"` } diff --git a/internal/pkg/plugin/githubactions/nodejs/validate.go b/internal/pkg/plugin/githubactions/nodejs/validate.go index ec71da42b..c6abc3ddd 100644 --- a/internal/pkg/plugin/githubactions/nodejs/validate.go +++ b/internal/pkg/plugin/githubactions/nodejs/validate.go @@ -1,30 +1,9 @@ package nodejs -import "fmt" +import ( + "github.com/devstream-io/devstream/pkg/util/validator" +) func validate(opts *Options) []error { - retErrors := make([]error, 0) - - // owner/org/repo/branch - if opts.Owner == "" && opts.Org == "" { - retErrors = append(retErrors, fmt.Errorf("owner and org are empty")) - } - if opts.Repo == "" { - retErrors = append(retErrors, fmt.Errorf("repo is empty")) - } - if opts.Branch == "" { - retErrors = append(retErrors, fmt.Errorf("branch is empty")) - } - - // language - if opts.Language == nil { - retErrors = append(retErrors, fmt.Errorf("language is empty")) - } - if errs := opts.Language.Validate(); len(errs) != 0 { - for _, e := range errs { - retErrors = append(retErrors, fmt.Errorf("language is invalid: %s", e)) - } - } - - return retErrors + return validator.Struct(opts) } diff --git a/internal/pkg/plugin/githubactions/python/options.go b/internal/pkg/plugin/githubactions/python/options.go index 240fc7942..7fef5c4c3 100644 --- a/internal/pkg/plugin/githubactions/python/options.go +++ b/internal/pkg/plugin/githubactions/python/options.go @@ -6,9 +6,9 @@ import ( // Options is the struct for configurations of the githubactions plugin. type Options struct { - Owner string - Org string - Repo string - Branch string - Language *ga.Language + Owner string `validate:"required_without=Org"` + Org string `validate:"required_without=Owner"` + Repo string `validate:"required"` + Branch string `validate:"required"` + Language *ga.Language `validate:"required"` } diff --git a/internal/pkg/plugin/githubactions/python/validate.go b/internal/pkg/plugin/githubactions/python/validate.go index 853c47177..a7de579d0 100644 --- a/internal/pkg/plugin/githubactions/python/validate.go +++ b/internal/pkg/plugin/githubactions/python/validate.go @@ -1,33 +1,10 @@ package python import ( - "fmt" + "github.com/devstream-io/devstream/pkg/util/validator" ) // validate validates the options provided by the core. func validate(opts *Options) []error { - retErrors := make([]error, 0) - - // owner/org/repo/branch - if opts.Owner == "" && opts.Org == "" { - retErrors = append(retErrors, fmt.Errorf("owner and org are empty")) - } - if opts.Repo == "" { - retErrors = append(retErrors, fmt.Errorf("repo is empty")) - } - if opts.Branch == "" { - retErrors = append(retErrors, fmt.Errorf("branch is empty")) - } - - // language - if opts.Language == nil { - retErrors = append(retErrors, fmt.Errorf("language is empty")) - } - if errs := opts.Language.Validate(); len(errs) != 0 { - for _, e := range errs { - retErrors = append(retErrors, fmt.Errorf("language is invalid: %s", e)) - } - } - - return retErrors + return validator.Struct(opts) } diff --git a/internal/pkg/plugin/githubactions/workflow.go b/internal/pkg/plugin/githubactions/workflow.go index 7625bab6a..2b0ea0f74 100644 --- a/internal/pkg/plugin/githubactions/workflow.go +++ b/internal/pkg/plugin/githubactions/workflow.go @@ -1,7 +1,5 @@ package githubactions -import "fmt" - const ( CommitMessage = "GitHub Actions workflow, created by DevStream" PRBuilderFileName = "pr-builder.yml" @@ -10,16 +8,6 @@ const ( // Language is the struct containing details of a programming language specified in the GitHub Actions Workflow. type Language struct { - Name string + Name string `validate:"required"` Version string } - -func (l *Language) Validate() []error { - retErrors := make([]error, 0) - - if l.Name == "" { - retErrors = append(retErrors, fmt.Errorf("name is empty")) - } - - return retErrors -} diff --git a/internal/pkg/plugin/gitlabci/generic/options.go b/internal/pkg/plugin/gitlabci/generic/options.go index 927233dce..81999f6d5 100644 --- a/internal/pkg/plugin/gitlabci/generic/options.go +++ b/internal/pkg/plugin/gitlabci/generic/options.go @@ -2,8 +2,8 @@ package generic // Options is the struct for configurations of the gitlabci-generic plugin. type Options struct { - PathWithNamespace string - Branch string - TemplateURL string + PathWithNamespace string `validate:"required"` + Branch string `validate:"required"` + TemplateURL string `validate:"required"` TemplateVariables map[string]interface{} } diff --git a/internal/pkg/plugin/gitlabci/generic/validate.go b/internal/pkg/plugin/gitlabci/generic/validate.go index 2e47f0191..8a840f914 100644 --- a/internal/pkg/plugin/gitlabci/generic/validate.go +++ b/internal/pkg/plugin/gitlabci/generic/validate.go @@ -1,20 +1,10 @@ package generic -import "fmt" +import ( + "github.com/devstream-io/devstream/pkg/util/validator" +) // validate validates the options provided by the core. func validate(options *Options) []error { - retErrors := make([]error, 0) - - if options.PathWithNamespace == "" { - retErrors = append(retErrors, fmt.Errorf("pathWithNamespace is empty")) - } - if options.Branch == "" { - retErrors = append(retErrors, fmt.Errorf("branch is empty")) - } - if options.TemplateURL == "" { - retErrors = append(retErrors, fmt.Errorf("templateURL is empty")) - } - - return retErrors + return validator.Struct(options) } diff --git a/internal/pkg/plugin/gitlabci/golang/gitlabci.go b/internal/pkg/plugin/gitlabci/golang/gitlabci.go index 3f6986111..9616f3cde 100644 --- a/internal/pkg/plugin/gitlabci/golang/gitlabci.go +++ b/internal/pkg/plugin/gitlabci/golang/gitlabci.go @@ -6,8 +6,8 @@ const ( ) type Options struct { - PathWithNamespace string - Branch string + PathWithNamespace string `validate:"required"` + Branch string `validate:"required"` } func buildState(opts *Options) map[string]interface{} { diff --git a/internal/pkg/plugin/gitlabci/golang/validate.go b/internal/pkg/plugin/gitlabci/golang/validate.go index bb007cb43..23dd9bddb 100644 --- a/internal/pkg/plugin/gitlabci/golang/validate.go +++ b/internal/pkg/plugin/gitlabci/golang/validate.go @@ -1,17 +1,9 @@ package golang -import "fmt" +import ( + "github.com/devstream-io/devstream/pkg/util/validator" +) func validate(opts *Options) []error { - retErrors := make([]error, 0) - - if opts.PathWithNamespace == "" { - retErrors = append(retErrors, fmt.Errorf("pathWithNamespace is empty")) - } - - if opts.Branch == "" { - retErrors = append(retErrors, fmt.Errorf("branch is empty")) - } - - return retErrors + return validator.Struct(opts) } diff --git a/internal/pkg/plugin/jiragithub/options.go b/internal/pkg/plugin/jiragithub/options.go index dde170072..ac848d8df 100644 --- a/internal/pkg/plugin/jiragithub/options.go +++ b/internal/pkg/plugin/jiragithub/options.go @@ -2,11 +2,11 @@ package jiragithub // Options is the struct for configurations of the jiragithub plugin. type Options struct { - Owner string - Org string - Repo string - JiraBaseUrl string - JiraUserEmail string - JiraProjectKey string - Branch string + Owner string `validate:"required_without=Org"` + Org string `validate:"required_without=Owner"` + Repo string `validate:"required"` + JiraBaseUrl string `validate:"required"` + JiraUserEmail string `validate:"required"` + JiraProjectKey string `validate:"required"` + Branch string `validate:"required"` } diff --git a/internal/pkg/plugin/jiragithub/validate.go b/internal/pkg/plugin/jiragithub/validate.go index edce5ad00..a74c1fbce 100644 --- a/internal/pkg/plugin/jiragithub/validate.go +++ b/internal/pkg/plugin/jiragithub/validate.go @@ -1,21 +1,10 @@ package jiragithub -import "fmt" +import ( + "github.com/devstream-io/devstream/pkg/util/validator" +) // validate validates the options provided by the core. func validate(opts *Options) []error { - retErrors := make([]error, 0) - - // owner/org/repo/branch - if opts.Owner == "" && opts.Org == "" { - retErrors = append(retErrors, fmt.Errorf("owner and org are empty")) - } - if opts.Repo == "" { - retErrors = append(retErrors, fmt.Errorf("repo is empty")) - } - if opts.Branch == "" { - retErrors = append(retErrors, fmt.Errorf("branch is empty")) - } - - return retErrors + return validator.Struct(opts) } diff --git a/internal/pkg/plugin/trello/trello.go b/internal/pkg/plugin/trello/trello.go index d2d5a1e1e..9d16f0f18 100644 --- a/internal/pkg/plugin/trello/trello.go +++ b/internal/pkg/plugin/trello/trello.go @@ -9,9 +9,9 @@ import ( // Options is the struct for configurations of the trellogithub plugin. type Options struct { - Owner string - Org string - Repo string + Owner string `validate:"required_without=Org"` + Org string `validate:"required_without=Owner"` + Repo string `validate:"required"` KanbanBoardName string } diff --git a/internal/pkg/plugin/trello/validate.go b/internal/pkg/plugin/trello/validate.go index 93c27e338..3a419778d 100644 --- a/internal/pkg/plugin/trello/validate.go +++ b/internal/pkg/plugin/trello/validate.go @@ -1,17 +1,9 @@ package trello -import "fmt" +import ( + "github.com/devstream-io/devstream/pkg/util/validator" +) func validate(opts *Options) []error { - retErrors := make([]error, 0) - - // owner/org/repo/branch - if opts.Owner == "" && opts.Org == "" { - retErrors = append(retErrors, fmt.Errorf("owner and org are empty")) - } - if opts.Repo == "" { - retErrors = append(retErrors, fmt.Errorf("repo is empty")) - } - - return retErrors + return validator.Struct(opts) } diff --git a/internal/pkg/plugin/trellogithub/validate.go b/internal/pkg/plugin/trellogithub/validate.go index d580e62cd..a1eb826c0 100644 --- a/internal/pkg/plugin/trellogithub/validate.go +++ b/internal/pkg/plugin/trellogithub/validate.go @@ -1,22 +1,9 @@ package trellogithub -import "fmt" +import ( + "github.com/devstream-io/devstream/pkg/util/validator" +) func validate(opts *Options) []error { - retErrors := make([]error, 0) - - // owner/org/repo/branch - if opts.Owner == "" && opts.Org == "" { - retErrors = append(retErrors, fmt.Errorf("owner and org are empty")) - } - if opts.Repo == "" { - retErrors = append(retErrors, fmt.Errorf("repo is empty")) - } - if opts.Branch == "" { - retErrors = append(retErrors, fmt.Errorf("branch is empty")) - } - - // TODO(daniel-hutao): Add more validations after refactor this package. - - return retErrors + return validator.Struct(opts) } diff --git a/internal/pkg/plugin/trellogithub/workflow.go b/internal/pkg/plugin/trellogithub/workflow.go index 6d5c6aada..0980e332d 100644 --- a/internal/pkg/plugin/trellogithub/workflow.go +++ b/internal/pkg/plugin/trellogithub/workflow.go @@ -18,10 +18,10 @@ var trelloWorkflow = &github.Workflow{ // Options is the struct for configurations of the trellogithub plugin. type Options struct { - Owner string - Org string - Repo string - Branch string + Owner string `validate:"required_without=Org"` + Org string `validate:"required_without=Owner"` + Repo string `validate:"required"` + Branch string `validate:"required"` BoardId string TodoListId string DoingListId string diff --git a/pkg/util/helm/param.go b/pkg/util/helm/param.go index cf8407fd6..da4b70b9a 100644 --- a/pkg/util/helm/param.go +++ b/pkg/util/helm/param.go @@ -9,14 +9,14 @@ type HelmParam struct { // Repo is the struct containing details of a git repository. // TODO(daniel-hutao): make the Repo equals to repo.Entry type Repo struct { - Name string - URL string + Name string `validate:"required"` + URL string `validate:"required"` } // Chart is the struct containing details of a helm chart. // TODO(daniel-hutao): make the Chart equals to helmclient.ChartSpec type Chart struct { - ChartName string `mapstructure:"chart_name"` + ChartName string `validate:"required" mapstructure:"chart_name"` Version string ReleaseName string `mapstructure:"release_name"` Namespace string diff --git a/pkg/util/helm/validation.go b/pkg/util/helm/validation.go index 722c6e254..7eeabf7f1 100644 --- a/pkg/util/helm/validation.go +++ b/pkg/util/helm/validation.go @@ -1,23 +1,10 @@ package helm -import "fmt" +import "github.com/devstream-io/devstream/pkg/util/validator" func Validate(param *HelmParam) []error { Defaults(param) - - retErrors := make([]error, 0) - - if param.Repo.Name == "" { - retErrors = append(retErrors, fmt.Errorf("repo.name is empty")) - } - if param.Repo.URL == "" { - retErrors = append(retErrors, fmt.Errorf("repo.url is empty")) - } - if param.Chart.ChartName == "" { - retErrors = append(retErrors, fmt.Errorf("chart.chart_name is empty")) - } - - return retErrors + return validator.Struct(param) } // Defaults set the default value with HelmParam. diff --git a/pkg/util/validator/validator.go b/pkg/util/validator/validator.go new file mode 100644 index 000000000..fcd39bbe1 --- /dev/null +++ b/pkg/util/validator/validator.go @@ -0,0 +1,34 @@ +package validator + +import ( + "errors" + "log" + + "github.com/go-playground/validator/v10" + "k8s.io/apimachinery/pkg/util/validation" +) + +var v *validator.Validate + +func init() { + v = validator.New() + + if err := v.RegisterValidation("dns1123subdomain", dns1123SubDomain); err != nil { + log.Fatal(err) + } +} + +func Struct(s interface{}) []error { + if err := v.Struct(s); err != nil { + errs := make([]error, 0) + for _, e := range err.(validator.ValidationErrors) { + errs = append(errs, errors.New(e.Error())) + } + return errs + } + return nil +} + +func dns1123SubDomain(fl validator.FieldLevel) bool { + return len(validation.IsDNS1123Subdomain(fl.Field().String())) == 0 +}