github.com/Jeffail/benthos/v3@v3.65.0/internal/cli/template/lint.go (about) 1 package template 2 3 import ( 4 "fmt" 5 "os" 6 "path/filepath" 7 "strings" 8 9 "github.com/Jeffail/benthos/v3/internal/template" 10 "github.com/fatih/color" 11 "github.com/urfave/cli/v2" 12 ) 13 14 var red = color.New(color.FgRed).SprintFunc() 15 var yellow = color.New(color.FgYellow).SprintFunc() 16 17 func resolveLintPath(path string) []string { 18 recurse := false 19 if path == "./..." || path == "..." { 20 recurse = true 21 path = "." 22 } 23 if strings.HasSuffix(path, "/...") { 24 recurse = true 25 path = strings.TrimSuffix(path, "/...") 26 } 27 if recurse { 28 var targets []string 29 if err := filepath.Walk(path, func(path string, info os.FileInfo, werr error) error { 30 if werr != nil { 31 return werr 32 } 33 if info.IsDir() { 34 return nil 35 } 36 if strings.HasSuffix(path, ".yaml") || 37 strings.HasSuffix(path, ".yml") { 38 targets = append(targets, path) 39 } 40 return nil 41 }); err != nil { 42 fmt.Fprintf(os.Stderr, "Filesystem walk error: %v\n", err) 43 os.Exit(1) 44 } 45 return targets 46 } 47 return []string{path} 48 } 49 50 type pathLint struct { 51 source string 52 lint string 53 err string 54 } 55 56 func lintFile(path string) (pathLints []pathLint) { 57 conf, lints, err := template.ReadConfig(path) 58 if err != nil { 59 pathLints = append(pathLints, pathLint{ 60 source: path, 61 err: red(err.Error()), 62 }) 63 return 64 } 65 66 for _, l := range lints { 67 pathLints = append(pathLints, pathLint{ 68 source: path, 69 lint: l, 70 }) 71 } 72 73 testErrors, err := conf.Test() 74 if err != nil { 75 pathLints = append(pathLints, pathLint{ 76 source: path, 77 err: err.Error(), 78 }) 79 return 80 } 81 82 for _, tErr := range testErrors { 83 pathLints = append(pathLints, pathLint{ 84 source: path, 85 err: tErr, 86 }) 87 } 88 return 89 } 90 91 func lintCliCommand() *cli.Command { 92 return &cli.Command{ 93 Name: "lint", 94 Usage: "Parse Benthos templates and report any linting errors", 95 Description: ` 96 Exits with a status code 1 if any linting errors are detected: 97 98 benthos template lint 99 benthos template lint ./templates/*.yaml 100 benthos template lint ./foo.yaml ./bar.yaml 101 benthos template lint ./templates/... 102 103 If a path ends with '...' then Benthos will walk the target and lint any 104 files with the .yaml or .yml extension.`[1:], 105 Action: func(c *cli.Context) error { 106 var targets []string 107 for _, p := range c.Args().Slice() { 108 targets = append(targets, resolveLintPath(p)...) 109 } 110 111 var pathLints []pathLint 112 for _, target := range targets { 113 if target == "" { 114 continue 115 } 116 lints := lintFile(target) 117 if len(lints) > 0 { 118 pathLints = append(pathLints, lints...) 119 } 120 } 121 if len(pathLints) == 0 { 122 os.Exit(0) 123 } 124 for _, lint := range pathLints { 125 message := yellow(lint.lint) 126 if len(lint.err) > 0 { 127 message = red(lint.err) 128 } 129 fmt.Fprintf(os.Stderr, "%v: %v\n", lint.source, message) 130 } 131 os.Exit(1) 132 return nil 133 }, 134 } 135 }