diff --git a/cmd/cli.go b/cmd/cli.go index 4a72471f4..6904d23e7 100644 --- a/cmd/cli.go +++ b/cmd/cli.go @@ -14,6 +14,7 @@ import ( "github.com/fatih/color" "github.com/hashicorp/logutils" flags "github.com/jessevdk/go-flags" + "github.com/spf13/afero" "github.com/terraform-linters/tflint/formatter" "github.com/terraform-linters/tflint/terraform" "github.com/terraform-linters/tflint/tflint" @@ -54,19 +55,46 @@ func NewCLI(outStream io.Writer, errStream io.Writer) (*CLI, error) { // Run invokes the CLI with the given arguments. func (cli *CLI) Run(args []string) int { + cli.formatter = &formatter.Formatter{ + Stdout: cli.outStream, + Stderr: cli.errStream, + } + + // Configure options parser var opts Options parser := flags.NewParser(&opts, flags.HelpFlag) parser.Usage = "--chdir=DIR/--recursive [OPTIONS]" parser.UnknownOptionHandler = unknownOptionHandler - // Parse commandline flag + + // Parse command line options args, err := parser.ParseArgs(args) - // Set up output formatter - cli.formatter = &formatter.Formatter{ - Stdout: cli.outStream, - Stderr: cli.errStream, - Format: opts.Format, - Fix: opts.Fix, + if err != nil { + if flagsErr, ok := err.(*flags.Error); ok && flagsErr.Type == flags.ErrHelp { + fmt.Fprintln(cli.outStream, err) + return ExitCodeOK + } + cli.formatter.Print(tflint.Issues{}, fmt.Errorf("Failed to parse CLI options; %w", err), map[string][]byte{}) + return ExitCodeError + } + + if len(args) > 1 { + cli.formatter.Print(tflint.Issues{}, fmt.Errorf("Command line arguments support was dropped in v0.47. Use --chdir or --filter instead."), map[string][]byte{}) + return ExitCodeError + } + + // Setup config + cfg, err := tflint.LoadConfig(afero.Afero{Fs: afero.NewOsFs()}, opts.Config) + if err != nil { + fmt.Fprintf(cli.errStream, "Failed to load TFLint config; %s\n", err) + return ExitCodeError } + + cfg.Merge(opts.toConfig()) + + // Set formatter fields from options/config + cli.formatter.Format = cfg.Format + cli.formatter.Fix = opts.Fix + if opts.Color { color.NoColor = false cli.formatter.NoColor = false @@ -83,18 +111,6 @@ func (cli *CLI) Run(args []string) int { }) log.SetFlags(log.Ltime | log.Lshortfile) - if err != nil { - if flagsErr, ok := err.(*flags.Error); ok && flagsErr.Type == flags.ErrHelp { - fmt.Fprintln(cli.outStream, err) - return ExitCodeOK - } - cli.formatter.Print(tflint.Issues{}, fmt.Errorf("Failed to parse CLI options; %w", err), map[string][]byte{}) - return ExitCodeError - } - if len(args) > 1 { - cli.formatter.Print(tflint.Issues{}, fmt.Errorf("Command line arguments support was dropped in v0.47. Use --chdir or --filter instead."), map[string][]byte{}) - return ExitCodeError - } if opts.MaxWorkers != nil && *opts.MaxWorkers <= 0 { cli.formatter.Print(tflint.Issues{}, fmt.Errorf("Max workers should be greater than 0"), map[string][]byte{}) return ExitCodeError diff --git a/integrationtest/cli/cli_test.go b/integrationtest/cli/cli_test.go index 55224169a..5061807a6 100644 --- a/integrationtest/cli/cli_test.go +++ b/integrationtest/cli/cli_test.go @@ -50,11 +50,25 @@ func TestIntegration(t *testing.T) { stdout: "", }, { - name: "specify format", + name: "format flag", command: "./tflint --format json", dir: "no_issues", status: cmd.ExitCodeOK, - stdout: "[]", + stdout: `{"issues":[],"errors":[]}`, + }, + { + name: "format config", + command: "./tflint", + dir: "format_config", + status: cmd.ExitCodeIssuesFound, + stdout: `main.tf:2:19: Error - instance type is t2.micro (aws_instance_example_type)`, + }, + { + name: "format flag overrides config", + command: "./tflint --format json", + dir: "format_config", + status: cmd.ExitCodeIssuesFound, + stdout: `{"issues":[{"rule":{"name":"aws_instance_example_type","severity":"error","link":""},"message":"instance type is t2.micro","range":{"filename":"main.tf","start":{"line":2,"column":19},"end":{"line":2,"column":29}},"callers":[]}],"errors":[]}`, }, { name: "`--force` option with no issues", diff --git a/integrationtest/cli/format_config/.tflint.hcl b/integrationtest/cli/format_config/.tflint.hcl new file mode 100644 index 000000000..d60d16448 --- /dev/null +++ b/integrationtest/cli/format_config/.tflint.hcl @@ -0,0 +1,7 @@ +config { + format = "compact" +} + +plugin "testing" { + enabled = true +} diff --git a/integrationtest/cli/format_config/main.tf b/integrationtest/cli/format_config/main.tf new file mode 100644 index 000000000..b73b98751 --- /dev/null +++ b/integrationtest/cli/format_config/main.tf @@ -0,0 +1,3 @@ +resource "aws_instance" "main" { + instance_type = "t2.micro" +} diff --git a/integrationtest/recursive/recursive_test.go b/integrationtest/recursive/recursive_test.go index f83e634ef..aaa50ea9a 100644 --- a/integrationtest/recursive/recursive_test.go +++ b/integrationtest/recursive/recursive_test.go @@ -92,6 +92,14 @@ func TestIntegration(t *testing.T) { opts := []cmp.Option{ cmpopts.IgnoreFields(formatter.JSONRule{}, "Link"), + // Only compare error messages up to the double new line. + // After this, stderr will be printed which is verbose. + cmp.Transformer("TruncateMessage", func(e formatter.JSONError) formatter.JSONError { + if parts := strings.Split(e.Message, "\n\n"); len(parts) > 1 { + e.Message = parts[0] + } + return e + }), } if test.ignoreOrder { opts = append(opts, cmpopts.SortSlices(func(a, b formatter.JSONError) bool {