Added support for `-force-color` to the CLI. (#10975)
This commit is contained in:
parent
6ff0b6debc
commit
0fa60dae9d
|
@ -0,0 +1,3 @@
|
||||||
|
```release-note:improvement
|
||||||
|
cli: Added support for `-force-color` to the CLI to force colored output.
|
||||||
|
```
|
|
@ -13,6 +13,9 @@ import (
|
||||||
const (
|
const (
|
||||||
// EnvNomadCLINoColor is an env var that toggles colored UI output.
|
// EnvNomadCLINoColor is an env var that toggles colored UI output.
|
||||||
EnvNomadCLINoColor = `NOMAD_CLI_NO_COLOR`
|
EnvNomadCLINoColor = `NOMAD_CLI_NO_COLOR`
|
||||||
|
|
||||||
|
// EnvNomadCLIForceColor is an env var that forces colored UI output.
|
||||||
|
EnvNomadCLIForceColor = `NOMAD_CLI_FORCE_COLOR`
|
||||||
)
|
)
|
||||||
|
|
||||||
// DeprecatedCommand is a command that wraps an existing command and prints a
|
// DeprecatedCommand is a command that wraps an existing command and prints a
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/hashicorp/nomad/api"
|
"github.com/hashicorp/nomad/api"
|
||||||
|
colorable "github.com/mattn/go-colorable"
|
||||||
"github.com/mitchellh/cli"
|
"github.com/mitchellh/cli"
|
||||||
"github.com/mitchellh/colorstring"
|
"github.com/mitchellh/colorstring"
|
||||||
"github.com/posener/complete"
|
"github.com/posener/complete"
|
||||||
|
@ -39,6 +40,9 @@ type Meta struct {
|
||||||
// Whether to not-colorize output
|
// Whether to not-colorize output
|
||||||
noColor bool
|
noColor bool
|
||||||
|
|
||||||
|
// Whether to force colorized output
|
||||||
|
forceColor bool
|
||||||
|
|
||||||
// The region to send API requests
|
// The region to send API requests
|
||||||
region string
|
region string
|
||||||
|
|
||||||
|
@ -70,6 +74,7 @@ func (m *Meta) FlagSet(n string, fs FlagSetFlags) *flag.FlagSet {
|
||||||
f.StringVar(&m.region, "region", "", "")
|
f.StringVar(&m.region, "region", "", "")
|
||||||
f.StringVar(&m.namespace, "namespace", "", "")
|
f.StringVar(&m.namespace, "namespace", "", "")
|
||||||
f.BoolVar(&m.noColor, "no-color", false, "")
|
f.BoolVar(&m.noColor, "no-color", false, "")
|
||||||
|
f.BoolVar(&m.forceColor, "force-color", false, "")
|
||||||
f.StringVar(&m.caCert, "ca-cert", "", "")
|
f.StringVar(&m.caCert, "ca-cert", "", "")
|
||||||
f.StringVar(&m.caPath, "ca-path", "", "")
|
f.StringVar(&m.caPath, "ca-path", "", "")
|
||||||
f.StringVar(&m.clientCert, "client-cert", "", "")
|
f.StringVar(&m.clientCert, "client-cert", "", "")
|
||||||
|
@ -97,6 +102,7 @@ func (m *Meta) AutocompleteFlags(fs FlagSetFlags) complete.Flags {
|
||||||
"-region": complete.PredictAnything,
|
"-region": complete.PredictAnything,
|
||||||
"-namespace": NamespacePredictor(m.Client, nil),
|
"-namespace": NamespacePredictor(m.Client, nil),
|
||||||
"-no-color": complete.PredictNothing,
|
"-no-color": complete.PredictNothing,
|
||||||
|
"-force-color": complete.PredictNothing,
|
||||||
"-ca-cert": complete.PredictFiles("*"),
|
"-ca-cert": complete.PredictFiles("*"),
|
||||||
"-ca-path": complete.PredictDirs("*"),
|
"-ca-path": complete.PredictDirs("*"),
|
||||||
"-client-cert": complete.PredictFiles("*"),
|
"-client-cert": complete.PredictFiles("*"),
|
||||||
|
@ -155,15 +161,47 @@ func (m *Meta) allNamespaces() bool {
|
||||||
|
|
||||||
func (m *Meta) Colorize() *colorstring.Colorize {
|
func (m *Meta) Colorize() *colorstring.Colorize {
|
||||||
_, coloredUi := m.Ui.(*cli.ColoredUi)
|
_, coloredUi := m.Ui.(*cli.ColoredUi)
|
||||||
noColor := m.noColor || !coloredUi || !terminal.IsTerminal(int(os.Stdout.Fd()))
|
|
||||||
|
|
||||||
return &colorstring.Colorize{
|
return &colorstring.Colorize{
|
||||||
Colors: colorstring.DefaultColors,
|
Colors: colorstring.DefaultColors,
|
||||||
Disable: noColor,
|
Disable: !coloredUi,
|
||||||
Reset: true,
|
Reset: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Meta) SetupUi(args []string) {
|
||||||
|
noColor := os.Getenv(EnvNomadCLINoColor) != ""
|
||||||
|
forceColor := os.Getenv(EnvNomadCLIForceColor) != ""
|
||||||
|
|
||||||
|
for _, arg := range args {
|
||||||
|
// Check if color is set
|
||||||
|
if arg == "-no-color" || arg == "--no-color" {
|
||||||
|
noColor = true
|
||||||
|
} else if arg == "-force-color" || arg == "--force-color" {
|
||||||
|
forceColor = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m.Ui = &cli.BasicUi{
|
||||||
|
Reader: os.Stdin,
|
||||||
|
Writer: colorable.NewColorableStdout(),
|
||||||
|
ErrorWriter: colorable.NewColorableStderr(),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only use colored UI if not disabled and stdout is a tty or colors are
|
||||||
|
// forced.
|
||||||
|
isTerminal := terminal.IsTerminal(int(os.Stdout.Fd()))
|
||||||
|
useColor := !noColor && (isTerminal || forceColor)
|
||||||
|
if useColor {
|
||||||
|
m.Ui = &cli.ColoredUi{
|
||||||
|
ErrorColor: cli.UiColorRed,
|
||||||
|
WarnColor: cli.UiColorYellow,
|
||||||
|
InfoColor: cli.UiColorGreen,
|
||||||
|
Ui: m.Ui,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type usageOptsFlags uint8
|
type usageOptsFlags uint8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -196,12 +234,17 @@ func generalOptionsUsage(usageOpts usageOptsFlags) string {
|
||||||
`
|
`
|
||||||
|
|
||||||
// note: that although very few commands use color explicitly, all of them
|
// note: that although very few commands use color explicitly, all of them
|
||||||
// return red-colored text on error so we don't want to make this
|
// return red-colored text on error so we want the color flags to always be
|
||||||
// configurable
|
// present in the help messages.
|
||||||
remainingText := `
|
remainingText := `
|
||||||
-no-color
|
-no-color
|
||||||
Disables colored command output. Alternatively, NOMAD_CLI_NO_COLOR may be
|
Disables colored command output. Alternatively, NOMAD_CLI_NO_COLOR may be
|
||||||
set.
|
set. This option takes precedence over -force-color.
|
||||||
|
|
||||||
|
-force-color
|
||||||
|
Forces colored command output. This can be used in cases where the usual
|
||||||
|
terminal detection fails. Alternatively, NOMAD_CLI_FORCE_COLOR may be set.
|
||||||
|
This option has no effect if -no-color is also used.
|
||||||
|
|
||||||
-ca-cert=<path>
|
-ca-cert=<path>
|
||||||
Path to a PEM encoded CA cert file to use to verify the
|
Path to a PEM encoded CA cert file to use to verify the
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/kr/pty"
|
"github.com/kr/pty"
|
||||||
|
@ -27,6 +28,7 @@ func TestMeta_FlagSet(t *testing.T) {
|
||||||
[]string{
|
[]string{
|
||||||
"address",
|
"address",
|
||||||
"no-color",
|
"no-color",
|
||||||
|
"force-color",
|
||||||
"region",
|
"region",
|
||||||
"namespace",
|
"namespace",
|
||||||
"ca-cert",
|
"ca-cert",
|
||||||
|
@ -81,11 +83,45 @@ func TestMeta_Colorize(t *testing.T) {
|
||||||
{
|
{
|
||||||
Name: "disable colors via CLI flag",
|
Name: "disable colors via CLI flag",
|
||||||
SetupFn: func(t *testing.T, m *Meta) {
|
SetupFn: func(t *testing.T, m *Meta) {
|
||||||
m.Ui = &cli.ColoredUi{}
|
m.SetupUi([]string{"-no-color"})
|
||||||
|
},
|
||||||
fs := m.FlagSet("colorize_test", FlagSetDefault)
|
ExpectColor: false,
|
||||||
err := fs.Parse([]string{"-no-color"})
|
},
|
||||||
assert.NoError(t, err)
|
{
|
||||||
|
Name: "disable colors via env var",
|
||||||
|
SetupFn: func(t *testing.T, m *Meta) {
|
||||||
|
os.Setenv(EnvNomadCLINoColor, "1")
|
||||||
|
m.SetupUi([]string{})
|
||||||
|
},
|
||||||
|
ExpectColor: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "force colors via CLI flag",
|
||||||
|
SetupFn: func(t *testing.T, m *Meta) {
|
||||||
|
m.SetupUi([]string{"-force-color"})
|
||||||
|
},
|
||||||
|
ExpectColor: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "force colors via env var",
|
||||||
|
SetupFn: func(t *testing.T, m *Meta) {
|
||||||
|
os.Setenv(EnvNomadCLIForceColor, "1")
|
||||||
|
m.SetupUi([]string{})
|
||||||
|
},
|
||||||
|
ExpectColor: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "no color take predecence over force color via CLI flag",
|
||||||
|
SetupFn: func(t *testing.T, m *Meta) {
|
||||||
|
m.SetupUi([]string{"-no-color", "-force-color"})
|
||||||
|
},
|
||||||
|
ExpectColor: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "no color take predecence over force color via env var",
|
||||||
|
SetupFn: func(t *testing.T, m *Meta) {
|
||||||
|
os.Setenv(EnvNomadCLINoColor, "1")
|
||||||
|
m.SetupUi([]string{"-force-color"})
|
||||||
},
|
},
|
||||||
ExpectColor: false,
|
ExpectColor: false,
|
||||||
},
|
},
|
||||||
|
@ -104,6 +140,14 @@ func TestMeta_Colorize(t *testing.T) {
|
||||||
defer func() { os.Stdout = oldStdout }()
|
defer func() { os.Stdout = oldStdout }()
|
||||||
os.Stdout = tty
|
os.Stdout = tty
|
||||||
|
|
||||||
|
// Make sure Nomad environment variables are clean.
|
||||||
|
for _, envVar := range os.Environ() {
|
||||||
|
if strings.HasPrefix(envVar, "NOMAD") {
|
||||||
|
k := strings.SplitN(envVar, "=", 2)[0]
|
||||||
|
os.Unsetenv(k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Run test case.
|
// Run test case.
|
||||||
m := &Meta{}
|
m := &Meta{}
|
||||||
if tc.SetupFn != nil {
|
if tc.SetupFn != nil {
|
||||||
|
|
48
main.go
48
main.go
|
@ -19,10 +19,8 @@ import (
|
||||||
|
|
||||||
"github.com/hashicorp/nomad/command"
|
"github.com/hashicorp/nomad/command"
|
||||||
"github.com/hashicorp/nomad/version"
|
"github.com/hashicorp/nomad/version"
|
||||||
colorable "github.com/mattn/go-colorable"
|
|
||||||
"github.com/mitchellh/cli"
|
"github.com/mitchellh/cli"
|
||||||
"github.com/sean-/seed"
|
"github.com/sean-/seed"
|
||||||
"golang.org/x/crypto/ssh/terminal"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -88,24 +86,9 @@ func Run(args []string) int {
|
||||||
}
|
}
|
||||||
|
|
||||||
func RunCustom(args []string) int {
|
func RunCustom(args []string) int {
|
||||||
// Parse flags into env vars for global use
|
|
||||||
args = setupEnv(args)
|
|
||||||
|
|
||||||
// Create the meta object
|
// Create the meta object
|
||||||
metaPtr := new(command.Meta)
|
metaPtr := new(command.Meta)
|
||||||
|
metaPtr.SetupUi(args)
|
||||||
// Don't use color if disabled
|
|
||||||
color := true
|
|
||||||
if os.Getenv(command.EnvNomadCLINoColor) != "" {
|
|
||||||
color = false
|
|
||||||
}
|
|
||||||
|
|
||||||
isTerminal := terminal.IsTerminal(int(os.Stdout.Fd()))
|
|
||||||
metaPtr.Ui = &cli.BasicUi{
|
|
||||||
Reader: os.Stdin,
|
|
||||||
Writer: colorable.NewColorableStdout(),
|
|
||||||
ErrorWriter: colorable.NewColorableStderr(),
|
|
||||||
}
|
|
||||||
|
|
||||||
// The Nomad agent never outputs color
|
// The Nomad agent never outputs color
|
||||||
agentUi := &cli.BasicUi{
|
agentUi := &cli.BasicUi{
|
||||||
|
@ -114,16 +97,6 @@ func RunCustom(args []string) int {
|
||||||
ErrorWriter: os.Stderr,
|
ErrorWriter: os.Stderr,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only use colored UI if stdout is a tty, and not disabled
|
|
||||||
if isTerminal && color {
|
|
||||||
metaPtr.Ui = &cli.ColoredUi{
|
|
||||||
ErrorColor: cli.UiColorRed,
|
|
||||||
WarnColor: cli.UiColorYellow,
|
|
||||||
InfoColor: cli.UiColorGreen,
|
|
||||||
Ui: metaPtr.Ui,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
commands := command.Commands(metaPtr, agentUi)
|
commands := command.Commands(metaPtr, agentUi)
|
||||||
cli := &cli.CLI{
|
cli := &cli.CLI{
|
||||||
Name: "nomad",
|
Name: "nomad",
|
||||||
|
@ -203,22 +176,3 @@ func printCommand(w io.Writer, name string, cmdFn cli.CommandFactory) {
|
||||||
}
|
}
|
||||||
fmt.Fprintf(w, " %s\t%s\n", name, cmd.Synopsis())
|
fmt.Fprintf(w, " %s\t%s\n", name, cmd.Synopsis())
|
||||||
}
|
}
|
||||||
|
|
||||||
// setupEnv parses args and may replace them and sets some env vars to known
|
|
||||||
// values based on format options
|
|
||||||
func setupEnv(args []string) []string {
|
|
||||||
noColor := false
|
|
||||||
for _, arg := range args {
|
|
||||||
// Check if color is set
|
|
||||||
if arg == "-no-color" || arg == "--no-color" {
|
|
||||||
noColor = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Put back into the env for later
|
|
||||||
if noColor {
|
|
||||||
os.Setenv(command.EnvNomadCLINoColor, "true")
|
|
||||||
}
|
|
||||||
|
|
||||||
return args
|
|
||||||
}
|
|
||||||
|
|
|
@ -11,7 +11,12 @@
|
||||||
user. Defaults to the "default" namespace.
|
user. Defaults to the "default" namespace.
|
||||||
|
|
||||||
- `-no-color`: Disables colored command output. Alternatively,
|
- `-no-color`: Disables colored command output. Alternatively,
|
||||||
`NOMAD_CLI_NO_COLOR` may be set.
|
`NOMAD_CLI_NO_COLOR` may be set. This option takes precedence over
|
||||||
|
`-force-color`.
|
||||||
|
|
||||||
|
-`-force-color`: Forces colored command output. This can be used in cases where
|
||||||
|
the usual terminal detection fails. Alternatively, `NOMAD_CLI_FORCE_COLOR`
|
||||||
|
may be set. This option has no effect if `-no-color` is also used.
|
||||||
|
|
||||||
- `-ca-cert=<path>`: Path to a PEM encoded CA cert file to use to verify the
|
- `-ca-cert=<path>`: Path to a PEM encoded CA cert file to use to verify the
|
||||||
Nomad server SSL certificate. Overrides the `NOMAD_CACERT` environment
|
Nomad server SSL certificate. Overrides the `NOMAD_CACERT` environment
|
||||||
|
|
|
@ -6,7 +6,12 @@
|
||||||
Agent's local region.
|
Agent's local region.
|
||||||
|
|
||||||
- `-no-color`: Disables colored command output. Alternatively,
|
- `-no-color`: Disables colored command output. Alternatively,
|
||||||
`NOMAD_CLI_NO_COLOR` may be set.
|
`NOMAD_CLI_NO_COLOR` may be set. This option takes precedence over
|
||||||
|
`-force-color`.
|
||||||
|
|
||||||
|
-`-force-color`: Forces colored command output. This can be used in cases where
|
||||||
|
the usual terminal detection fails. Alternatively, `NOMAD_CLI_FORCE_COLOR`
|
||||||
|
may be set. This option has no effect if `-no-color` is also used.
|
||||||
|
|
||||||
- `-ca-cert=<path>`: Path to a PEM encoded CA cert file to use to verify the
|
- `-ca-cert=<path>`: Path to a PEM encoded CA cert file to use to verify the
|
||||||
Nomad server SSL certificate. Overrides the `NOMAD_CACERT` environment
|
Nomad server SSL certificate. Overrides the `NOMAD_CACERT` environment
|
||||||
|
|
Loading…
Reference in New Issue