Skip to content

Commit

Permalink
feat: add a quiet flag to suppress all output except for errors
Browse files Browse the repository at this point in the history
Signed-off-by: Brian McGee <brian@bmcgee.ie>

diff --git a/cmd/root.go b/cmd/root.go
index 72348f1..cd3f8f7 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -144,13 +144,19 @@ func runE(v *viper.Viper, statz *stats.Stats, cmd *cobra.Command, args []string)
 	log.SetOutput(os.Stderr)
 	log.SetReportTimestamp(false)

-	switch v.GetInt("verbose") {
-	case 0:
-		log.SetLevel(log.WarnLevel)
-	case 1:
-		log.SetLevel(log.InfoLevel)
-	default:
-		log.SetLevel(log.DebugLevel)
+	if v.GetBool("quiet") {
+		// if quiet, we only log errors
+		log.SetLevel(log.ErrorLevel)
+	} else {
+		// otherwise, the verbose flag controls the log level
+		switch v.GetInt("verbose") {
+		case 0:
+			log.SetLevel(log.WarnLevel)
+		case 1:
+			log.SetLevel(log.InfoLevel)
+		default:
+			log.SetLevel(log.DebugLevel)
+		}
 	}

 	// format
diff --git a/cmd/root_test.go b/cmd/root_test.go
index 2846504..628a69b 100644
--- a/cmd/root_test.go
+++ b/cmd/root_test.go
@@ -131,6 +131,33 @@ func TestOnUnmatched(t *testing.T) {
 	})
 }

+func TestQuiet(t *testing.T) {
+	as := require.New(t)
+	tempDir := test.TempExamples(t)
+
+	test.ChangeWorkDir(t, tempDir)
+
+	// allow missing formatter
+	t.Setenv("TREEFMT_ALLOW_MISSING_FORMATTER", "true")
+
+	noOutput := func(out []byte) {
+		as.Empty(out)
+	}
+
+	treefmt(t, withArgs("-q"), withNoError(t), withStdout(noOutput), withStderr(noOutput))
+	treefmt(t, withArgs("--quiet"), withNoError(t), withStdout(noOutput), withStderr(noOutput))
+
+	t.Setenv("TREEFMT_QUIET", "true")
+	treefmt(t, withNoError(t), withStdout(noOutput), withStderr(noOutput))
+
+	t.Setenv("TREEFMT_ALLOW_MISSING_FORMATTER", "false")
+
+	// check it doesn't suppress errors
+	treefmt(t, withError(func(err error) {
+		as.ErrorContains(err, "error looking up 'foo-fmt'")
+	}))
+}
+
 func TestCpuProfile(t *testing.T) {
 	as := require.New(t)
 	tempDir := test.TempExamples(t)
diff --git a/config/config.go b/config/config.go
index f5d8e7a..c10df3f 100644
--- a/config/config.go
+++ b/config/config.go
@@ -22,6 +22,7 @@ type Config struct {
 	Formatters            []string `mapstructure:"formatters"              toml:"formatters,omitempty"`
 	NoCache               bool     `mapstructure:"no-cache"                toml:"-"` // not allowed in config
 	OnUnmatched           string   `mapstructure:"on-unmatched"            toml:"on-unmatched,omitempty"`
+	Quiet                 bool     `mapstructure:"quiet"                   toml:"-"` // not allowed in config
 	TreeRoot              string   `mapstructure:"tree-root"               toml:"tree-root,omitempty"`
 	TreeRootFile          string   `mapstructure:"tree-root-file"          toml:"tree-root-file,omitempty"`
 	Verbose               uint8    `mapstructure:"verbose"                 toml:"verbose,omitempty"`
@@ -110,6 +111,9 @@ func SetFlags(fs *pflag.FlagSet) {
 		"verbose", "v",
 		"Set the verbosity of logs e.g. -vv. (env $TREEFMT_VERBOSE)",
 	)
+	fs.BoolP(
+		"quiet", "q", false, "Disable all logs except errors. (env $TREEFMT_QUIET)",
+	)
 	fs.String(
 		"walk", "auto",
 		"The method used to traverse the files within the tree root. Currently supports "+
diff --git a/config/config_test.go b/config/config_test.go
index 281e18a..ae6dc83 100644
--- a/config/config_test.go
+++ b/config/config_test.go
@@ -323,6 +323,36 @@ func TestNoCache(t *testing.T) {
 	checkValue(true)
 }

+func TestQuiet(t *testing.T) {
+	as := require.New(t)
+
+	cfg := &config.Config{}
+	v, flags := newViper(t)
+
+	checkValue := func(expected bool) {
+		readValue(t, v, cfg, func(cfg *config.Config) {
+			as.Equal(expected, cfg.Quiet)
+		})
+	}
+
+	// default with no flag, env or config
+	checkValue(false)
+
+	// set config value and check that it has no effect
+	// you are not allowed to set no-cache in config
+	cfg.Quiet = true
+
+	checkValue(false)
+
+	// env override
+	t.Setenv("TREEFMT_QUIET", "false")
+	checkValue(false)
+
+	// flag override
+	as.NoError(flags.Set("quiet", "true"))
+	checkValue(true)
+}
+
 func TestOnUnmatched(t *testing.T) {
 	as := require.New(t)

diff --git a/docs/content/getting-started/configure.md b/docs/content/getting-started/configure.md
index efc8f2b..623077e 100644
--- a/docs/content/getting-started/configure.md
+++ b/docs/content/getting-started/configure.md
@@ -254,6 +254,22 @@ Possible values are `<debug|info|warn|error|fatal>`.
     on-unmatched = "debug"
     ```

+### `quiet`
+
+Suppress all output except for errors.
+
+=== "Flag"
+
+    ```console
+    treefmt --quiet
+    ```
+
+=== "Env"
+
+    ```console
+    TREEFMT_QUIET=true treefmt
+    ```
+
 ### `stdin`

 Format the context passed in via stdin.
  • Loading branch information
brianmcgee committed Jan 8, 2025
1 parent b76d839 commit 10c9a72
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 7 deletions.
20 changes: 13 additions & 7 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,13 +144,19 @@ func runE(v *viper.Viper, statz *stats.Stats, cmd *cobra.Command, args []string)
log.SetOutput(os.Stderr)
log.SetReportTimestamp(false)

switch v.GetInt("verbose") {
case 0:
log.SetLevel(log.WarnLevel)
case 1:
log.SetLevel(log.InfoLevel)
default:
log.SetLevel(log.DebugLevel)
if v.GetBool("quiet") {
// if quiet, we only log errors
log.SetLevel(log.ErrorLevel)
} else {
// otherwise, the verbose flag controls the log level
switch v.GetInt("verbose") {
case 0:
log.SetLevel(log.WarnLevel)
case 1:
log.SetLevel(log.InfoLevel)
default:
log.SetLevel(log.DebugLevel)
}
}

// format
Expand Down
27 changes: 27 additions & 0 deletions cmd/root_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,33 @@ func TestOnUnmatched(t *testing.T) {
})
}

func TestQuiet(t *testing.T) {
as := require.New(t)
tempDir := test.TempExamples(t)

test.ChangeWorkDir(t, tempDir)

// allow missing formatter
t.Setenv("TREEFMT_ALLOW_MISSING_FORMATTER", "true")

noOutput := func(out []byte) {
as.Empty(out)
}

treefmt(t, withArgs("-q"), withNoError(t), withStdout(noOutput), withStderr(noOutput))
treefmt(t, withArgs("--quiet"), withNoError(t), withStdout(noOutput), withStderr(noOutput))

t.Setenv("TREEFMT_QUIET", "true")
treefmt(t, withNoError(t), withStdout(noOutput), withStderr(noOutput))

t.Setenv("TREEFMT_ALLOW_MISSING_FORMATTER", "false")

// check it doesn't suppress errors
treefmt(t, withError(func(err error) {
as.ErrorContains(err, "error looking up 'foo-fmt'")
}))
}

func TestCpuProfile(t *testing.T) {
as := require.New(t)
tempDir := test.TempExamples(t)
Expand Down
4 changes: 4 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type Config struct {
Formatters []string `mapstructure:"formatters" toml:"formatters,omitempty"`
NoCache bool `mapstructure:"no-cache" toml:"-"` // not allowed in config
OnUnmatched string `mapstructure:"on-unmatched" toml:"on-unmatched,omitempty"`
Quiet bool `mapstructure:"quiet" toml:"-"` // not allowed in config
TreeRoot string `mapstructure:"tree-root" toml:"tree-root,omitempty"`
TreeRootFile string `mapstructure:"tree-root-file" toml:"tree-root-file,omitempty"`
Verbose uint8 `mapstructure:"verbose" toml:"verbose,omitempty"`
Expand Down Expand Up @@ -110,6 +111,9 @@ func SetFlags(fs *pflag.FlagSet) {
"verbose", "v",
"Set the verbosity of logs e.g. -vv. (env $TREEFMT_VERBOSE)",
)
fs.BoolP(
"quiet", "q", false, "Disable all logs except errors. (env $TREEFMT_QUIET)",
)
fs.String(
"walk", "auto",
"The method used to traverse the files within the tree root. Currently supports "+
Expand Down
30 changes: 30 additions & 0 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,36 @@ func TestNoCache(t *testing.T) {
checkValue(true)
}

func TestQuiet(t *testing.T) {
as := require.New(t)

cfg := &config.Config{}
v, flags := newViper(t)

checkValue := func(expected bool) {
readValue(t, v, cfg, func(cfg *config.Config) {
as.Equal(expected, cfg.Quiet)
})
}

// default with no flag, env or config
checkValue(false)

// set config value and check that it has no effect
// you are not allowed to set no-cache in config
cfg.Quiet = true

checkValue(false)

// env override
t.Setenv("TREEFMT_QUIET", "false")
checkValue(false)

// flag override
as.NoError(flags.Set("quiet", "true"))
checkValue(true)
}

func TestOnUnmatched(t *testing.T) {
as := require.New(t)

Expand Down
16 changes: 16 additions & 0 deletions docs/content/getting-started/configure.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,22 @@ Possible values are `<debug|info|warn|error|fatal>`.
on-unmatched = "debug"
```

### `quiet`

Suppress all output except for errors.

=== "Flag"

```console
treefmt --quiet
```

=== "Env"

```console
TREEFMT_QUIET=true treefmt
```

### `stdin`

Format the context passed in via stdin.
Expand Down

0 comments on commit 10c9a72

Please # to comment.