main: Emphasize only the primary workflow commands in our help

A long time ago we introduced this separation between "common commands"
and "all other commands", but over the intervening years we've not really
done a good job of classifying new commands we've added and so by default
most of them ended up being classified as "common".

In the interests of making this output more useful for those getting
started, this switches the two categories so that "Main commands" is now
the curated list, and "all other commands" is the bucket for everything
else.

The intent here is that the "main commands" are the ones users are likely
to try as part of their initial learning of Terraform, while the other
commands are for less common situations where the user is more likely to
learn about a specific command in some other context, like a tutorial
about a special situation.

The "main commands" are also now ordered by the sequence users will
typically run them in, rather than alphabetical order. That's a subjective
readability tradeoff, but I think as long as the list stays relatively
short (which it should) it's still relatively easy to scan and find a
particular command in the shortlist.
This commit is contained in:
Martin Atkins 2020-10-23 16:13:41 -07:00
parent f44265e59e
commit 9665901e8e
2 changed files with 62 additions and 46 deletions

View File

@ -25,7 +25,22 @@ const runningInAutomationEnvName = "TF_IN_AUTOMATION"
// Commands is the mapping of all the available Terraform commands.
var Commands map[string]cli.CommandFactory
var PlumbingCommands map[string]struct{}
// PrimaryCommands is an ordered sequence of the top-level commands (not
// subcommands) that we emphasize at the top of our help output. This is
// ordered so that we can show them in the typical workflow order, rather
// than in alphabetical order. Anything not in this sequence or in the
// HiddenCommands set appears under "all other commands".
var PrimaryCommands []string
// HiddenCommands is a set of top-level commands (not subcommands) that are
// not advertised in the top-level help at all. This is typically because
// they are either just stubs that return an error message about something
// no longer being supported or backward-compatibility aliases for other
// commands.
//
// No commands in the PrimaryCommands sequence should also appear in the
// HiddenCommands set, because that would be rather silly.
var HiddenCommands map[string]struct{}
// Ui is the cli.Ui used for communicating to the outside world.
@ -94,19 +109,6 @@ func initCommands(
// add, remove or reclassify commands then consider updating
// that to match.
PlumbingCommands = map[string]struct{}{
"state": struct{}{}, // includes all subcommands
"force-unlock": struct{}{},
}
HiddenCommands = map[string]struct{}{
"0.12upgrade": struct{}{},
"0.13upgrade": struct{}{},
"env": struct{}{},
"internal-plugin": struct{}{},
"push": struct{}{},
}
Commands = map[string]cli.CommandFactory{
"apply": func() (cli.Command, error) {
return &command.ApplyCommand{
@ -402,6 +404,23 @@ func initCommands(
}, nil
},
}
PrimaryCommands = []string{
"init",
"validate",
"plan",
"apply",
"destroy",
}
HiddenCommands = map[string]struct{}{
"0.12upgrade": struct{}{},
"0.13upgrade": struct{}{},
"env": struct{}{},
"internal-plugin": struct{}{},
"push": struct{}{},
}
}
// makeShutdownCh creates an interrupt listener and returns a channel.

61
help.go
View File

@ -13,10 +13,10 @@ import (
// helpFunc is a cli.HelpFunc that can is used to output the help for Terraform.
func helpFunc(commands map[string]cli.CommandFactory) string {
// Determine the maximum key length, and classify based on type
porcelain := make(map[string]cli.CommandFactory)
plumbing := make(map[string]cli.CommandFactory)
var otherCommands []string
maxKeyLen := 0
for key, f := range commands {
for key := range commands {
if _, ok := HiddenCommands[key]; ok {
// We don't consider hidden commands when deciding the
// maximum command length.
@ -27,12 +27,18 @@ func helpFunc(commands map[string]cli.CommandFactory) string {
maxKeyLen = len(key)
}
if _, ok := PlumbingCommands[key]; ok {
plumbing[key] = f
} else {
porcelain[key] = f
isOther := true
for _, candidate := range PrimaryCommands {
if candidate == key {
isOther = false
break
}
}
if isOther {
otherCommands = append(otherCommands, key)
}
}
sort.Strings(otherCommands)
// The output produced by this is included in the docs at
// website/source/docs/commands/index.html.markdown; if you
@ -41,50 +47,41 @@ func helpFunc(commands map[string]cli.CommandFactory) string {
Usage: terraform [global options] <subcommand> [args]
The available commands for execution are listed below.
The most common, useful commands are shown first, followed by
less common or more advanced commands. If you're just getting
started with Terraform, stick with the common commands. For the
other commands, please read the help and docs before usage.
The primary workflow commands are given first, followed by
less common or more advanced commands.
Common commands:
Main commands:
%s
All other commands:
%s
Global options (use these before the subcommand, if any):
-chdir=DIR Switch to a different working directory before executing
the given subcommand.
-help Show this help output, or the help for a specified
subcommand.
-version An alias for the "version" subcommand.
`, listCommands(porcelain, maxKeyLen), listCommands(plumbing, maxKeyLen))
-chdir=DIR Switch to a different working directory before executing
the given subcommand.
-help Show this help output, or the help for a specified
subcommand.
-version An alias for the "version" subcommand.
`, listCommands(commands, PrimaryCommands, maxKeyLen), listCommands(commands, otherCommands, maxKeyLen))
return strings.TrimSpace(helpText)
}
// listCommands just lists the commands in the map with the
// given maximum key length.
func listCommands(commands map[string]cli.CommandFactory, maxKeyLen int) string {
func listCommands(allCommands map[string]cli.CommandFactory, order []string, maxKeyLen int) string {
var buf bytes.Buffer
// Get the list of keys so we can sort them, and also get the maximum
// key length so they can be aligned properly.
keys := make([]string, 0, len(commands))
for key, _ := range commands {
keys = append(keys, key)
}
sort.Strings(keys)
for _, key := range keys {
commandFunc, ok := commands[key]
for _, key := range order {
commandFunc, ok := allCommands[key]
if !ok {
// This should never happen since we JUST built the list of
// keys.
// This suggests an inconsistency in the command table definitions
// in commands.go .
panic("command not found: " + key)
}
command, err := commandFunc()
if err != nil {
// This would be really weird since there's no good reason for
// any of our command factories to fail.
log.Printf("[ERR] cli: Command '%s' failed to load: %s",
key, err)
continue