Skip to content

Commit 161c074

Browse files
samtholiyacoderabbitai[bot]autofix-ci[bot]ostermanaknysh
authored
Improve Atmos help (#959)
* fix help for 'atmos about non-existent' * Add invalid flag error handler * Ignore update messages in tests * fix test cases * Add Markdown usage capability * usage markdowns * example hint updated * fix tests * Update usage display to markdown glamour * CheckTTYSupport should be in term package * atmost about usage and workflow usage * Improved flag usage example * update workflow error message * updated usage * Add custom alias to help * Fix asci render logic for error * fix testcases * test updated * Add --help suggestion to help * removed examples from md * fix doubledash suggestion * fix test case * fix no valid subcommands * updated stack required and component errors to new error render format * fix tests * fix logger * test fixes * added test for alias subcommand * Valid subcommands should be on next line * Update cmd/workflow.go Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * added assertion back * update error messages for atlantis to markdown * [autofix.ci] apply automated fixes * fix log level error * fix rendering of examples * Add markdown util * Update pkg/ui/markdown/renderer.go Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * remove unwanted renderError function * fix tests * added more error checks * added description comment * fix ascii render logic and use consistent functions * fix manual render issue * Update cmd/validate_component.go Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <erik@cloudposse.com> * Update internal/exec/atlantis_generate_repo_config.go Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <erik@cloudposse.com> * Update cmd/docs.go Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <erik@cloudposse.com> * Update cmd/cmd_utils.go Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <erik@cloudposse.com> * updated error message for stack and components missing * fix flag help * Update pkg/ui/markdown/renderer.go Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <erik@cloudposse.com> * [autofix.ci] apply automated fixes * fix the snapshots * fixed workflow error message format * workflow UX updated * Update cmd/cmd_utils.go Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * small updates * [autofix.ci] apply automated fixes * fix tests * make error condition for custom command invalid config usefull * fix custom command invalid error sentence * update tests * updated terraform help * readded this test * [autofix.ci] apply automated fixes * update tests * update tests * update tests --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <erik@cloudposse.com> Co-authored-by: Andriy Knysh <aknysh@users.noreply.github.com> Co-authored-by: aknysh <andriy.knysh@gmail.com>
1 parent 35727ce commit 161c074

File tree

149 files changed

+2355
-672
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

149 files changed

+2355
-672
lines changed

cmd/about.go

Lines changed: 6 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,8 @@ package cmd
22

33
import (
44
_ "embed"
5-
"fmt"
6-
"os"
75

8-
"github.com/charmbracelet/glamour"
6+
"github.com/cloudposse/atmos/pkg/utils"
97
"github.com/spf13/cobra"
108
)
119

@@ -14,31 +12,12 @@ var aboutMarkdown string
1412

1513
// aboutCmd represents the about command
1614
var aboutCmd = &cobra.Command{
17-
Use: "about",
18-
Short: "Learn about Atmos",
19-
Long: `Display information about Atmos, its features, and benefits.`,
20-
Args: cobra.NoArgs,
21-
DisableSuggestions: true,
22-
SilenceUsage: true,
23-
SilenceErrors: true,
15+
Use: "about",
16+
Short: "Learn about Atmos",
17+
Long: `Display information about Atmos, its features, and benefits.`,
18+
Args: cobra.NoArgs,
2419
RunE: func(cmd *cobra.Command, args []string) error {
25-
renderer, err := glamour.NewTermRenderer(
26-
glamour.WithAutoStyle(),
27-
glamour.WithWordWrap(80),
28-
)
29-
if err != nil {
30-
return fmt.Errorf("failed to create markdown renderer: %w", err)
31-
}
32-
33-
out, err := renderer.Render(aboutMarkdown)
34-
if err != nil {
35-
return fmt.Errorf("failed to render about documentation: %w", err)
36-
}
37-
38-
_, err = fmt.Fprint(os.Stdout, out)
39-
if err != nil {
40-
return err
41-
}
20+
utils.PrintfMarkdown(aboutMarkdown)
4221
return nil
4322
},
4423
}

cmd/atlantis.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,6 @@ var atlantisCmd = &cobra.Command{
1414
}
1515

1616
func init() {
17+
atlantisCmd.PersistentFlags().Bool("", false, doubleDashHint)
1718
RootCmd.AddCommand(atlantisCmd)
1819
}

cmd/atlantis_generate_repo_config.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import (
44
"github.com/spf13/cobra"
55

66
e "github.com/cloudposse/atmos/internal/exec"
7-
u "github.com/cloudposse/atmos/pkg/utils"
7+
"github.com/cloudposse/atmos/pkg/utils"
88
)
99

1010
// atlantisGenerateRepoConfigCmd generates repository configuration for Atlantis
@@ -22,7 +22,7 @@ var atlantisGenerateRepoConfigCmd = &cobra.Command{
2222
checkAtmosConfig()
2323
err := e.ExecuteAtlantisGenerateRepoConfigCmd(cmd, args)
2424
if err != nil {
25-
u.LogErrorAndExit(err)
25+
utils.PrintErrorMarkdownAndExit("", err, "")
2626
}
2727
},
2828
}

cmd/aws.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,6 @@ var awsCmd = &cobra.Command{
1414
}
1515

1616
func init() {
17+
awsCmd.PersistentFlags().Bool("", false, doubleDashHint)
1718
RootCmd.AddCommand(awsCmd)
1819
}

cmd/aws_eks_update_kubeconfig.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ See https://docs.aws.amazon.com/cli/latest/reference/eks/update-kubeconfig.html
3535
Run: func(cmd *cobra.Command, args []string) {
3636
err := e.ExecuteAwsEksUpdateKubeconfigCommand(cmd, args)
3737
if err != nil {
38-
u.LogErrorAndExit(err)
38+
u.PrintErrorMarkdownAndExit("", err, "")
3939
}
4040
},
4141
ValidArgsFunction: ComponentsArgCompletion,

cmd/cmd_utils.go

Lines changed: 95 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ func processCustomCommands(
4646
var command *cobra.Command
4747
existingTopLevelCommands := make(map[string]*cobra.Command)
4848

49+
// Build commands and their hierarchy from the alias map
50+
for alias, fullCmd := range atmosConfig.CommandAliases {
51+
parts := strings.Fields(fullCmd)
52+
addCommandWithAlias(RootCmd, alias, parts)
53+
}
54+
4955
if topLevel {
5056
existingTopLevelCommands = getTopLevelCommands()
5157
}
@@ -113,6 +119,35 @@ func processCustomCommands(
113119
return nil
114120
}
115121

122+
// addCommandWithAlias adds a command hierarchy based on the full command
123+
func addCommandWithAlias(parentCmd *cobra.Command, alias string, parts []string) {
124+
if len(parts) == 0 {
125+
return
126+
}
127+
128+
// Check if a command with the current part already exists
129+
var cmd *cobra.Command
130+
for _, c := range parentCmd.Commands() {
131+
if c.Use == parts[0] {
132+
cmd = c
133+
break
134+
}
135+
}
136+
137+
// If the command doesn't exist, create it
138+
if cmd == nil {
139+
u.LogErrorAndExit(fmt.Errorf("subcommand `%s` not found for alias `%s`", parts[0], alias))
140+
}
141+
142+
// If there are more parts, recurse for the next level
143+
if len(parts) > 1 {
144+
addCommandWithAlias(cmd, alias, parts[1:])
145+
} else if !Contains(cmd.Aliases, alias) {
146+
// This is the last part of the command, add the alias
147+
cmd.Aliases = append(cmd.Aliases, alias)
148+
}
149+
}
150+
116151
// processCommandAliases processes the command aliases
117152
func processCommandAliases(
118153
atmosConfig schema.AtmosConfiguration,
@@ -187,10 +222,9 @@ func preCustomCommand(
187222
os.Exit(1)
188223
} else {
189224
// truly invalid, nothing to do
190-
u.LogError(errors.New(
191-
"invalid command: no args, no steps, no sub-commands",
192-
))
193-
os.Exit(1)
225+
u.PrintErrorMarkdownAndExit("Invalid command", errors.New(
226+
fmt.Sprintf("The `%s` command has no steps or subcommands configured.", cmd.CommandPath()),
227+
), "https://atmos.tools/cli/configuration/commands")
194228
}
195229
}
196230

@@ -583,37 +617,31 @@ func handleHelpRequest(cmd *cobra.Command, args []string) {
583617
}
584618
}
585619

620+
// showUsageAndExit we display the markdown usage or fallback to our custom usage
621+
// Markdown usage is not compatible with all outputs. We should therefore have fallback option.
586622
func showUsageAndExit(cmd *cobra.Command, args []string) {
587-
var suggestions []string
588-
unknownCommand := fmt.Sprintf("Error: Unknown command: %q\n\n", cmd.CommandPath())
589-
623+
if len(args) == 0 {
624+
showErrorExampleFromMarkdown(cmd, "")
625+
}
590626
if len(args) > 0 {
591-
suggestions = cmd.SuggestionsFor(args[0])
592-
unknownCommand = fmt.Sprintf("Error: Unknown command %q for %q\n\n", args[0], cmd.CommandPath())
627+
showErrorExampleFromMarkdown(cmd, args[0])
593628
}
594-
u.PrintErrorInColor(unknownCommand)
595-
if len(suggestions) > 0 {
596-
u.PrintMessage("Did you mean this?")
597-
for _, suggestion := range suggestions {
598-
u.PrintMessage(fmt.Sprintf(" %s\n", suggestion))
599-
}
600-
} else {
601-
// Retrieve valid subcommands dynamically
602-
validSubcommands := []string{}
603-
for _, subCmd := range cmd.Commands() {
604-
validSubcommands = append(validSubcommands, subCmd.Name())
605-
}
606-
if len(validSubcommands) > 0 {
607-
u.PrintMessage("Valid subcommands are:")
608-
for _, sub := range validSubcommands {
609-
u.PrintMessage(fmt.Sprintf(" %s", sub))
610-
}
629+
os.Exit(1)
630+
}
631+
632+
func showFlagUsageAndExit(cmd *cobra.Command, err error) error {
633+
unknownCommand := fmt.Sprintf("%v for command `%s`\n\n", err.Error(), cmd.CommandPath())
634+
args := strings.Split(err.Error(), ": ")
635+
if len(args) == 2 {
636+
if strings.Contains(args[0], "flag needs an argument") {
637+
unknownCommand = fmt.Sprintf("`%s` %s for command `%s`\n\n", args[1], args[0], cmd.CommandPath())
611638
} else {
612-
u.PrintMessage("No valid subcommands found")
639+
unknownCommand = fmt.Sprintf("%s `%s` for command `%s`\n\n", args[0], args[1], cmd.CommandPath())
613640
}
614641
}
615-
u.PrintMessage(fmt.Sprintf("\nRun '%s --help' for usage", cmd.CommandPath()))
642+
showUsageExample(cmd, unknownCommand)
616643
os.Exit(1)
644+
return nil
617645
}
618646

619647
// getConfigAndStacksInfo processes the CLI config and stacks
@@ -637,6 +665,45 @@ func getConfigAndStacksInfo(commandName string, cmd *cobra.Command, args []strin
637665
return info
638666
}
639667

668+
func showErrorExampleFromMarkdown(cmd *cobra.Command, arg string) {
669+
commandPath := cmd.CommandPath()
670+
suggestions := []string{}
671+
details := fmt.Sprintf("The command `%s` is not valid usage\n", commandPath)
672+
if len(arg) > 0 {
673+
details = fmt.Sprintf("Unknown command `%s` for `%s`\n", arg, commandPath)
674+
} else if len(cmd.Commands()) != 0 && arg == "" {
675+
details = fmt.Sprintf("The command `%s` requires a subcommand\n", commandPath)
676+
}
677+
if len(arg) > 0 {
678+
suggestions = cmd.SuggestionsFor(arg)
679+
}
680+
if len(suggestions) > 0 {
681+
details = details + "Did you mean this?\n"
682+
for _, suggestion := range suggestions {
683+
details += "* " + suggestion + "\n"
684+
}
685+
} else {
686+
if len(cmd.Commands()) > 0 {
687+
details += "\nValid subcommands are:\n"
688+
}
689+
// Retrieve valid subcommands dynamically
690+
for _, subCmd := range cmd.Commands() {
691+
details = details + "* " + subCmd.Name() + "\n"
692+
}
693+
}
694+
showUsageExample(cmd, details)
695+
}
696+
697+
func showUsageExample(cmd *cobra.Command, details string) {
698+
contentName := strings.ReplaceAll(cmd.CommandPath(), " ", "_")
699+
suggestion := fmt.Sprintf("\n\nRun `%s --help` for usage", cmd.CommandPath())
700+
if exampleContent, ok := examples[contentName]; ok {
701+
suggestion = exampleContent.Suggestion
702+
details += "\n## Usage Examples:\n" + exampleContent.Content
703+
}
704+
u.PrintInvalidUsageErrorAndExit(errors.New(details), suggestion)
705+
}
706+
640707
func stackFlagCompletion(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
641708
output, err := listStacks(cmd)
642709
if err != nil {

cmd/describe_affected.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ var describeAffectedCmd = &cobra.Command{
2020

2121
err := e.ExecuteDescribeAffectedCmd(cmd, args)
2222
if err != nil {
23-
u.LogErrorAndExit(err)
23+
u.PrintErrorMarkdownAndExit("", err, "")
2424
}
2525
},
2626
}

cmd/describe_component.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ var describeComponentCmd = &cobra.Command{
1919

2020
err := e.ExecuteDescribeComponentCmd(cmd, args)
2121
if err != nil {
22-
u.LogErrorAndExit(err)
22+
u.PrintErrorMarkdownAndExit("", err, "")
2323
}
2424
},
2525
ValidArgsFunction: ComponentsArgCompletion,

cmd/describe_config.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ var describeConfigCmd = &cobra.Command{
1717
Run: func(cmd *cobra.Command, args []string) {
1818
err := e.ExecuteDescribeConfigCmd(cmd, args)
1919
if err != nil {
20-
u.LogErrorAndExit(err)
20+
u.PrintErrorMarkdown("", err, "")
2121
}
2222
},
2323
}

cmd/describe_dependents.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ var describeDependentsCmd = &cobra.Command{
2121

2222
err := e.ExecuteDescribeDependentsCmd(cmd, args)
2323
if err != nil {
24-
u.LogErrorAndExit(err)
24+
u.PrintErrorMarkdownAndExit("", err, "")
2525
}
2626
},
2727
}

0 commit comments

Comments
 (0)