diff --git a/README.md b/README.md index 5c1b59d..045626e 100644 --- a/README.md +++ b/README.md @@ -98,6 +98,8 @@ To run in provided directory Process will exit with code `1` if architecture is not valid, otherwise it will exit with `0`. +### -ignore-tests + If you need to ignore `*_test.go` files in `go-cleanarch` check you can pass `-ignore-tests` go-cleanarch -ignore-tests @@ -105,6 +107,16 @@ If you need to ignore `*_test.go` files in `go-cleanarch` check you can pass `-i It is useful when you have memory implementation in infrastructure layer and you need to test application service which depends of it. +### -ignore-package + +If for some reason you need to allow to make forbidden import, for example + +`github.com/roblaszczak/go-cleanarch/examples/ignore-package/app` to `github.com/roblaszczak/go-cleanarch/examples/ignore-package/domain`. + +you can use + + go-cleanarch -ignore-package=github.com/roblaszczak/go-cleanarch/examples/ignore-package/app + ## Running the tests make test diff --git a/cleanarch/cleanarch.go b/cleanarch/cleanarch.go index c1c2f29..efdb423 100644 --- a/cleanarch/cleanarch.go +++ b/cleanarch/cleanarch.go @@ -80,7 +80,7 @@ type Validator struct { } // Validate validates provided path for Clean Architecture rules. -func (v *Validator) Validate(root string, ignoreTests bool) (bool, []ValidationError, error) { +func (v *Validator) Validate(root string, ignoreTests bool, ignoredPackages []string) (bool, []ValidationError, error) { errors := []ValidationError{} err := filepath.Walk(root, func(path string, fi os.FileInfo, err error) error { @@ -122,7 +122,14 @@ func (v *Validator) Validate(root string, ignoreTests bool) (bool, []ValidationE return nil } + ImportsLoop: for _, imp := range f.Imports { + for _, ignoredPackage := range ignoredPackages { + if strings.Contains(imp.Path.Value, ignoredPackage) { + continue ImportsLoop + } + } + validationErrors := v.validateImport(imp, importerMeta, path) errors = append(errors, validationErrors...) } diff --git a/cleanarch/cleanarch_test.go b/cleanarch/cleanarch_test.go index 9bbaa3c..4e21597 100644 --- a/cleanarch/cleanarch_test.go +++ b/cleanarch/cleanarch_test.go @@ -15,28 +15,34 @@ func init() { func TestValidator_Validate(t *testing.T) { testCases := []struct { - Path string - IsValid bool - IgnoreTests bool + Path string + IsValid bool + IgnoreTests bool + IgnoredPackages []string }{ - {"../examples/valid-simple", true, false}, - {"../examples/invalid-infra-in-domain-import", false, false}, - {"../examples/invalid-app-to-domain-import", false, false}, - {"../examples/invalid-cross-module-deps", false, false}, - {"../examples/valid-cross-module-deps", true, false}, - {"../examples/valid-imports-inside-module", true, false}, - {"../examples/invalid-imports-between-submodules", false, false}, - {"../examples/invalid-imports-between-submodules-2", false, false}, - {"../examples/ignored-dirs", true, false}, - {"../examples/ignored-dirs", true, false}, - {"../examples/invalid-infrastructure-to-app-import-in-tests", true, true}, - {"../examples/invalid-infrastructure-to-app-import-in-tests", false, false}, + {Path: "../examples/valid-simple", IsValid: true}, + {Path: "../examples/invalid-infra-in-domain-import", IsValid: false}, + {Path: "../examples/invalid-app-to-domain-import", IsValid: false}, + {Path: "../examples/invalid-cross-module-deps", IsValid: false}, + {Path: "../examples/valid-cross-module-deps", IsValid: true}, + {Path: "../examples/valid-imports-inside-module", IsValid: true}, + {Path: "../examples/invalid-imports-between-submodules", IsValid: false}, + {Path: "../examples/invalid-imports-between-submodules-2", IsValid: false}, + {Path: "../examples/ignored-dirs", IsValid: true}, + {Path: "../examples/ignored-dirs", IsValid: true}, + {Path: "../examples/invalid-infrastructure-to-app-import-in-tests", IsValid: true, IgnoreTests: true}, + {Path: "../examples/invalid-infrastructure-to-app-import-in-tests", IsValid: false}, + { + Path: "../examples/ignore-package", + IsValid: true, + IgnoredPackages: []string{"github.com/roblaszczak/go-cleanarch/examples/ignore-package/app"}, + }, } for _, c := range testCases { t.Run(c.Path, func(t *testing.T) { validator := cleanarch.NewValidator() - valid, errors, err := validator.Validate(c.Path, c.IgnoreTests) + valid, errors, err := validator.Validate(c.Path, c.IgnoreTests, c.IgnoredPackages) if err != nil { t.Fatal(err) } diff --git a/examples/ignore-package/README.md b/examples/ignore-package/README.md new file mode 100644 index 0000000..a9324ba --- /dev/null +++ b/examples/ignore-package/README.md @@ -0,0 +1 @@ +Invalid `app` to `domain` import can be ignored with "import-package" flag set to `github.com/roblaszczak/go-cleanarch/examples/ignore-package/app`. \ No newline at end of file diff --git a/examples/ignore-package/app/price.go b/examples/ignore-package/app/price.go new file mode 100644 index 0000000..58bac36 --- /dev/null +++ b/examples/ignore-package/app/price.go @@ -0,0 +1,3 @@ +package app + +type Price float64 diff --git a/examples/ignore-package/domain/product.go b/examples/ignore-package/domain/product.go new file mode 100644 index 0000000..73bb16c --- /dev/null +++ b/examples/ignore-package/domain/product.go @@ -0,0 +1,8 @@ +package domain + +import "github.com/roblaszczak/go-cleanarch/examples/ignore-package/app" + +// Product imports app.Price, with breaks Dependency Rule. +type Product struct { + Price app.Price +} diff --git a/flag.go b/flag.go new file mode 100644 index 0000000..472c609 --- /dev/null +++ b/flag.go @@ -0,0 +1,14 @@ +package main + +import "fmt" + +type sliceFlag []string + +func (s *sliceFlag) String() string { + return fmt.Sprintf("%s", s) +} + +func (s *sliceFlag) Set(v string) error { + *s = append(*s, v) + return nil +} diff --git a/main.go b/main.go index 7b5dfd2..f62184b 100644 --- a/main.go +++ b/main.go @@ -9,12 +9,19 @@ import ( "github.com/roblaszczak/go-cleanarch/cleanarch" ) -var ( - ignoreTests = flag.Bool("ignore-tests", false, "if flag is passed *_test.go files will be not checked") - debug = flag.Bool("debug", false, "debug mode") -) - func main() { + ignoredPackages := sliceFlag{} + + ignoreTests := flag.Bool("ignore-tests", false, "if flag is passed *_test.go files will be not checked") + debug := flag.Bool("debug", false, "debug mode") + flag.Var( + &ignoredPackages, + "ignore-package", + "provided packages can be imported to any layer, "+ + "for example you can use`-ignore-package github.com/roblaszczak/go-cleanarch/infrastructure` to import "+ + "this package to the domain", + ) + flag.Parse() var path string @@ -35,7 +42,7 @@ func main() { fmt.Printf("[cleanarch] checking %s\n", path) validator := cleanarch.NewValidator() - isValid, errors, err := validator.Validate(path, *ignoreTests) + isValid, errors, err := validator.Validate(path, *ignoreTests, ignoredPackages) if err != nil { panic(err) }