Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes multiple help issues #959

Open
wants to merge 78 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
01bcd67
fix help for 'atmos about non-existent'
samtholiya Jan 20, 2025
0470a32
Add invalid flag error handler
samtholiya Jan 21, 2025
3d3ebbc
Ignore update messages in tests
samtholiya Jan 21, 2025
1badbc8
Merge branch 'main' into feature/dev-2953-update-help-and-usageyaml-w…
samtholiya Jan 21, 2025
284db86
fix test cases
samtholiya Jan 22, 2025
d21e412
Add Markdown usage capability
samtholiya Jan 22, 2025
06353a5
usage markdowns
samtholiya Jan 22, 2025
bb9705b
example hint updated
samtholiya Jan 23, 2025
6317c43
fix tests
samtholiya Jan 23, 2025
5ddbc4d
Merge branch 'main' into feature/dev-2953-update-help-and-usageyaml-w…
samtholiya Jan 24, 2025
8b74647
Update usage display to markdown glamour
samtholiya Jan 26, 2025
7f9fbb3
Merge branch 'main' into feature/dev-2953-update-help-and-usageyaml-w…
samtholiya Jan 26, 2025
f0fcf3c
CheckTTYSupport should be in term package
samtholiya Jan 26, 2025
08153a5
atmost about usage and workflow usage
samtholiya Jan 26, 2025
b1cc112
Improved flag usage example
samtholiya Jan 26, 2025
370e982
update workflow error message
samtholiya Jan 26, 2025
045ab19
updated usage
samtholiya Jan 26, 2025
e8fbcaf
Add custom alias to help
samtholiya Jan 28, 2025
3e4563e
Fix asci render logic for error
samtholiya Jan 28, 2025
d65a24e
fix testcases
samtholiya Jan 28, 2025
dbb87ba
Merge branch 'main' of https://github.com/cloudposse/atmos into featu…
samtholiya Jan 28, 2025
9731347
test updated
samtholiya Jan 28, 2025
f801cff
Add --help suggestion to help
samtholiya Jan 28, 2025
2ecd83a
removed examples from md
samtholiya Jan 28, 2025
0087493
fix doubledash suggestion
samtholiya Jan 28, 2025
e9fce14
fix test case
samtholiya Jan 28, 2025
28a2f65
fix no valid subcommands
samtholiya Jan 28, 2025
0d0fb70
updated stack required and component errors to new error render format
samtholiya Jan 28, 2025
a3443fe
fix tests
samtholiya Jan 28, 2025
f1fc80f
fix logger
samtholiya Jan 28, 2025
6c7a191
test fixes
samtholiya Jan 29, 2025
0a0434e
added test for alias subcommand
samtholiya Jan 29, 2025
7fc52ed
Valid subcommands should be on next line
samtholiya Jan 29, 2025
b08659a
Update cmd/workflow.go
samtholiya Jan 30, 2025
f3decf2
Merge branch 'main' into feature/dev-2953-update-help-and-usageyaml-w…
samtholiya Jan 30, 2025
dc77e1f
added assertion back
samtholiya Jan 30, 2025
3972f82
update error messages for atlantis to markdown
samtholiya Jan 30, 2025
8013318
Merge branch 'main' of https://github.com/cloudposse/atmos into featu…
samtholiya Jan 31, 2025
f6d5a19
[autofix.ci] apply automated fixes
autofix-ci[bot] Jan 31, 2025
fbddec2
fix log level error
samtholiya Jan 31, 2025
d1ce9ef
Merge branch 'feature/dev-2953-update-help-and-usageyaml-with-snapsho…
samtholiya Jan 31, 2025
8f0e37c
fix rendering of examples
samtholiya Jan 31, 2025
78fbefc
Add markdown util
samtholiya Feb 2, 2025
a644478
Update pkg/ui/markdown/renderer.go
samtholiya Feb 2, 2025
f90abf5
remove unwanted renderError function
samtholiya Feb 2, 2025
7d6bff2
Merge branch 'feature/dev-2953-update-help-and-usageyaml-with-snapsho…
samtholiya Feb 2, 2025
6aede13
fix tests
samtholiya Feb 2, 2025
f5cba9a
added more error checks
samtholiya Feb 3, 2025
d714ab6
Merge branch 'main' into feature/dev-2953-update-help-and-usageyaml-w…
samtholiya Feb 3, 2025
68cec78
added description comment
samtholiya Feb 3, 2025
2954d72
Merge branch 'feature/dev-2953-update-help-and-usageyaml-with-snapsho…
samtholiya Feb 3, 2025
3c15f4c
fix ascii render logic and use consistent functions
samtholiya Feb 4, 2025
c0dd9ae
Merge branch 'main' into feature/dev-2953-update-help-and-usageyaml-w…
samtholiya Feb 4, 2025
2881ff4
fix manual render issue
samtholiya Feb 4, 2025
eeeb4cb
Update cmd/validate_component.go
samtholiya Feb 4, 2025
66116ab
Update internal/exec/atlantis_generate_repo_config.go
samtholiya Feb 4, 2025
f90e81e
Update cmd/docs.go
samtholiya Feb 4, 2025
cd58e7b
Update cmd/cmd_utils.go
samtholiya Feb 4, 2025
5a4068a
updated error message for stack and components missing
samtholiya Feb 4, 2025
faa862f
Merge branch 'main' into feature/dev-2953-update-help-and-usageyaml-w…
samtholiya Feb 4, 2025
e1fba8a
fix flag help
samtholiya Feb 4, 2025
f1f598f
Merge branch 'feature/dev-2953-update-help-and-usageyaml-with-snapsho…
samtholiya Feb 4, 2025
7b038f1
Merge branch 'main' into feature/dev-2953-update-help-and-usageyaml-w…
samtholiya Feb 4, 2025
1d95b24
Update pkg/ui/markdown/renderer.go
samtholiya Feb 4, 2025
5506308
[autofix.ci] apply automated fixes
autofix-ci[bot] Feb 4, 2025
5faaa7d
fix the snapshots
samtholiya Feb 4, 2025
de042b9
fixed workflow error message format
samtholiya Feb 4, 2025
8738804
workflow UX updated
samtholiya Feb 4, 2025
c66dbfa
Update cmd/cmd_utils.go
samtholiya Feb 5, 2025
652751b
Merge branch 'main' of https://github.com/cloudposse/atmos into featu…
samtholiya Feb 5, 2025
c0ab2d4
small updates
samtholiya Feb 5, 2025
316fc49
[autofix.ci] apply automated fixes
autofix-ci[bot] Feb 5, 2025
7dc5ec7
fix tests
samtholiya Feb 5, 2025
d3790d0
make error condition for custom command invalid config usefull
samtholiya Feb 5, 2025
ffc48a3
fix custom command invalid error sentence
samtholiya Feb 5, 2025
e71cb8d
Merge branch 'main' into feature/dev-2953-update-help-and-usageyaml-w…
aknysh Feb 6, 2025
d4a5a5c
Merge branch 'main' into feature/dev-2953-update-help-and-usageyaml-w…
samtholiya Feb 6, 2025
865db1e
update tests
samtholiya Feb 6, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 6 additions & 27 deletions cmd/about.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@ package cmd

import (
_ "embed"
"fmt"
"os"

"github.com/charmbracelet/glamour"
"github.com/cloudposse/atmos/pkg/utils"
"github.com/spf13/cobra"
)

Expand All @@ -14,31 +12,12 @@ var aboutMarkdown string

// aboutCmd represents the about command
var aboutCmd = &cobra.Command{
Use: "about",
Short: "Learn about Atmos",
Long: `Display information about Atmos, its features, and benefits.`,
Args: cobra.NoArgs,
DisableSuggestions: true,
SilenceUsage: true,
SilenceErrors: true,
Use: "about",
Short: "Learn about Atmos",
Long: `Display information about Atmos, its features, and benefits.`,
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
renderer, err := glamour.NewTermRenderer(
glamour.WithAutoStyle(),
glamour.WithWordWrap(80),
)
if err != nil {
return fmt.Errorf("failed to create markdown renderer: %w", err)
}

out, err := renderer.Render(aboutMarkdown)
if err != nil {
return fmt.Errorf("failed to render about documentation: %w", err)
}

_, err = fmt.Fprint(os.Stdout, out)
if err != nil {
return err
}
utils.PrintfMarkdown(aboutMarkdown)
return nil
},
}
Expand Down
1 change: 1 addition & 0 deletions cmd/atlantis.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@ var atlantisCmd = &cobra.Command{
}

func init() {
atlantisCmd.PersistentFlags().Bool("", false, doubleDashHint)
RootCmd.AddCommand(atlantisCmd)
}
4 changes: 2 additions & 2 deletions cmd/atlantis_generate_repo_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"github.com/spf13/cobra"

e "github.com/cloudposse/atmos/internal/exec"
u "github.com/cloudposse/atmos/pkg/utils"
"github.com/cloudposse/atmos/pkg/utils"
)

// atlantisGenerateRepoConfigCmd generates repository configuration for Atlantis
Expand All @@ -22,7 +22,7 @@ var atlantisGenerateRepoConfigCmd = &cobra.Command{
checkAtmosConfig()
err := e.ExecuteAtlantisGenerateRepoConfigCmd(cmd, args)
if err != nil {
u.LogErrorAndExit(err)
utils.PrintErrorMarkdownAndExit("", err, "")
}
},
}
Expand Down
1 change: 1 addition & 0 deletions cmd/aws.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@ var awsCmd = &cobra.Command{
}

func init() {
awsCmd.PersistentFlags().Bool("", false, doubleDashHint)
RootCmd.AddCommand(awsCmd)
}
2 changes: 1 addition & 1 deletion cmd/aws_eks_update_kubeconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ See https://docs.aws.amazon.com/cli/latest/reference/eks/update-kubeconfig.html
Run: func(cmd *cobra.Command, args []string) {
err := e.ExecuteAwsEksUpdateKubeconfigCommand(cmd, args)
if err != nil {
u.LogErrorAndExit(err)
u.PrintErrorMarkdownAndExit("", err, "")
}
},
}
Expand Down
123 changes: 95 additions & 28 deletions cmd/cmd_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ func processCustomCommands(
var command *cobra.Command
existingTopLevelCommands := make(map[string]*cobra.Command)

// Build commands and their hierarchy from the alias map
for alias, fullCmd := range atmosConfig.CommandAliases {
parts := strings.Fields(fullCmd)
addCommandWithAlias(RootCmd, alias, parts)
}

if topLevel {
existingTopLevelCommands = getTopLevelCommands()
}
Expand Down Expand Up @@ -112,6 +118,35 @@ func processCustomCommands(
return nil
}

// addCommandWithAlias adds a command hierarchy based on the full command
func addCommandWithAlias(parentCmd *cobra.Command, alias string, parts []string) {
if len(parts) == 0 {
return
}

// Check if a command with the current part already exists
var cmd *cobra.Command
for _, c := range parentCmd.Commands() {
if c.Use == parts[0] {
cmd = c
break
}
}

// If the command doesn't exist, create it
if cmd == nil {
u.LogErrorAndExit(fmt.Errorf("subcommand `%s` not found for alias `%s`", parts[0], alias))
}

// If there are more parts, recurse for the next level
if len(parts) > 1 {
addCommandWithAlias(cmd, alias, parts[1:])
} else if !Contains(cmd.Aliases, alias) {
// This is the last part of the command, add the alias
cmd.Aliases = append(cmd.Aliases, alias)
}
}

// processCommandAliases processes the command aliases
func processCommandAliases(
atmosConfig schema.AtmosConfiguration,
Expand Down Expand Up @@ -186,10 +221,9 @@ func preCustomCommand(
os.Exit(1)
} else {
// truly invalid, nothing to do
u.LogError(errors.New(
"invalid command: no args, no steps, no sub-commands",
))
os.Exit(1)
u.PrintErrorMarkdownAndExit("Invalid command", errors.New(
fmt.Sprintf("The `%s` command has no steps or subcommands configured.", cmd.CommandPath()),
), "https://atmos.tools/cli/configuration/commands")
}
}

Expand Down Expand Up @@ -552,37 +586,31 @@ func handleHelpRequest(cmd *cobra.Command, args []string) {
}
}

// showUsageAndExit we display the markdown usage or fallback to our custom usage
// Markdown usage is not compatible with all outputs. We should therefore have fallback option.
func showUsageAndExit(cmd *cobra.Command, args []string) {
var suggestions []string
unknownCommand := fmt.Sprintf("Error: Unknown command: %q\n\n", cmd.CommandPath())

if len(args) == 0 {
showErrorExampleFromMarkdown(cmd, "")
}
if len(args) > 0 {
suggestions = cmd.SuggestionsFor(args[0])
unknownCommand = fmt.Sprintf("Error: Unknown command %q for %q\n\n", args[0], cmd.CommandPath())
showErrorExampleFromMarkdown(cmd, args[0])
}
u.PrintErrorInColor(unknownCommand)
if len(suggestions) > 0 {
u.PrintMessage("Did you mean this?")
for _, suggestion := range suggestions {
u.PrintMessage(fmt.Sprintf(" %s\n", suggestion))
}
} else {
// Retrieve valid subcommands dynamically
validSubcommands := []string{}
for _, subCmd := range cmd.Commands() {
validSubcommands = append(validSubcommands, subCmd.Name())
}
if len(validSubcommands) > 0 {
u.PrintMessage("Valid subcommands are:")
for _, sub := range validSubcommands {
u.PrintMessage(fmt.Sprintf(" %s", sub))
}
os.Exit(1)
}

func showFlagUsageAndExit(cmd *cobra.Command, err error) error {
unknownCommand := fmt.Sprintf("%v for command `%s`\n\n", err.Error(), cmd.CommandPath())
args := strings.Split(err.Error(), ": ")
if len(args) == 2 {
if strings.Contains(args[0], "flag needs an argument") {
unknownCommand = fmt.Sprintf("`%s` %s for command `%s`\n\n", args[1], args[0], cmd.CommandPath())
} else {
u.PrintMessage("No valid subcommands found")
unknownCommand = fmt.Sprintf("%s `%s` for command `%s`\n\n", args[0], args[1], cmd.CommandPath())
}
}
u.PrintMessage(fmt.Sprintf("\nRun '%s --help' for usage", cmd.CommandPath()))
showUsageExample(cmd, unknownCommand)
os.Exit(1)
return nil
}
samtholiya marked this conversation as resolved.
Show resolved Hide resolved

// getConfigAndStacksInfo processes the CLI config and stacks
Expand All @@ -606,6 +634,45 @@ func getConfigAndStacksInfo(commandName string, cmd *cobra.Command, args []strin
return info
}

func showErrorExampleFromMarkdown(cmd *cobra.Command, arg string) {
commandPath := cmd.CommandPath()
suggestions := []string{}
details := fmt.Sprintf("The command `%s` is not valid usage\n", commandPath)
if len(arg) > 0 {
details = fmt.Sprintf("Unknown command `%s` for `%s`\n", arg, commandPath)
} else if len(cmd.Commands()) != 0 && arg == "" {
details = fmt.Sprintf("The command `%s` requires a subcommand\n", commandPath)
}
if len(arg) > 0 {
suggestions = cmd.SuggestionsFor(arg)
}
if len(suggestions) > 0 {
details = details + "Did you mean this?\n"
for _, suggestion := range suggestions {
details += "* " + suggestion + "\n"
}
} else {
if len(cmd.Commands()) > 0 {
details += "\nValid subcommands are:\n"
}
// Retrieve valid subcommands dynamically
for _, subCmd := range cmd.Commands() {
details = details + "* " + subCmd.Name() + "\n"
}
}
showUsageExample(cmd, details)
}

func showUsageExample(cmd *cobra.Command, details string) {
contentName := strings.ReplaceAll(cmd.CommandPath(), " ", "_")
suggestion := fmt.Sprintf("\n\nRun `%s --help` for usage", cmd.CommandPath())
if exampleContent, ok := examples[contentName]; ok {
suggestion = exampleContent.Suggestion
details += "\n## Usage Examples:\n" + exampleContent.Content
}
u.PrintInvalidUsageErrorAndExit(errors.New(details), suggestion)
}

// Contains checks if a slice of strings contains an exact match for the target string.
func Contains(slice []string, target string) bool {
for _, item := range slice {
Expand Down
2 changes: 1 addition & 1 deletion cmd/describe_affected.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ var describeAffectedCmd = &cobra.Command{

err := e.ExecuteDescribeAffectedCmd(cmd, args)
if err != nil {
u.LogErrorAndExit(err)
u.PrintErrorMarkdownAndExit("", err, "")
}
},
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/describe_component.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ var describeComponentCmd = &cobra.Command{

err := e.ExecuteDescribeComponentCmd(cmd, args)
if err != nil {
u.LogErrorAndExit(err)
u.PrintErrorMarkdownAndExit("", err, "")
}
},
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/describe_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ var describeConfigCmd = &cobra.Command{
Run: func(cmd *cobra.Command, args []string) {
err := e.ExecuteDescribeConfigCmd(cmd, args)
if err != nil {
u.LogErrorAndExit(err)
u.PrintErrorMarkdown("", err, "")
samtholiya marked this conversation as resolved.
Show resolved Hide resolved
}
},
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/describe_dependents.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ var describeDependentsCmd = &cobra.Command{

err := e.ExecuteDescribeDependentsCmd(cmd, args)
if err != nil {
u.LogErrorAndExit(err)
u.PrintErrorMarkdownAndExit("", err, "")
}
},
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/describe_stacks.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ var describeStacksCmd = &cobra.Command{

err := e.ExecuteDescribeStacksCmd(cmd, args)
if err != nil {
u.LogErrorAndExit(err)
u.PrintErrorMarkdownAndExit("", err, "")
}
},
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/describe_workflows.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ var describeWorkflowsCmd = &cobra.Command{
Run: func(cmd *cobra.Command, args []string) {
err := e.ExecuteDescribeWorkflowsCmd(cmd, args)
if err != nil {
u.LogErrorAndExit(err)
u.PrintErrorMarkdownAndExit("", err, "")
}
},
}
Expand Down
6 changes: 3 additions & 3 deletions cmd/docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ var docsCmd = &cobra.Command{

atmosConfig, err := cfg.InitCliConfig(info, true)
if err != nil {
u.LogErrorAndExit(err)
u.PrintErrorMarkdownAndExit("", err, "")
}

// Detect terminal width if not specified in `atmos.yaml`
Expand Down Expand Up @@ -66,10 +66,10 @@ var docsCmd = &cobra.Command{
componentPath := filepath.Join(atmosConfig.BasePath, atmosConfig.Components.Terraform.BasePath, info.Component)
componentPathExists, err := u.IsDirectory(componentPath)
if err != nil {
u.LogErrorAndExit(err)
u.PrintErrorMarkdownAndExit("", err, "")
}
if !componentPathExists {
u.LogErrorAndExit(fmt.Errorf("Component '%s' not found in path: '%s'", info.Component, componentPath))
u.PrintErrorMarkdownAndExit("", fmt.Errorf("Component `%s` not found in path: `%s`", info.Component, componentPath), "")
}

readmePath := filepath.Join(componentPath, "README.md")
Expand Down
3 changes: 2 additions & 1 deletion cmd/helmfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ func init() {
// https://github.com/spf13/cobra/issues/739
helmfileCmd.DisableFlagParsing = true
helmfileCmd.PersistentFlags().StringP("stack", "s", "", "atmos helmfile <helmfile_command> <component> -s <stack>")
helmfileCmd.PersistentFlags().Bool("", false, doubleDashHint)
RootCmd.AddCommand(helmfileCmd)
}

Expand All @@ -30,6 +31,6 @@ func helmfileRun(cmd *cobra.Command, commandName string, args []string) {
info := getConfigAndStacksInfo("helmfile", cmd, diffArgs)
err := e.ExecuteHelmfile(info)
if err != nil {
u.LogErrorAndExit(err)
u.PrintErrorMarkdownAndExit("", err, "")
}
}
2 changes: 1 addition & 1 deletion cmd/helmfile_generate_varfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func init() {

err := helmfileGenerateVarfileCmd.MarkPersistentFlagRequired("stack")
if err != nil {
u.LogErrorAndExit(err)
u.PrintErrorMarkdownAndExit("", err, "")
}

helmfileGenerateCmd.AddCommand(helmfileGenerateVarfileCmd)
Expand Down
9 changes: 4 additions & 5 deletions cmd/list_components.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package cmd
import (
"fmt"

"github.com/fatih/color"
"github.com/spf13/cobra"

e "github.com/cloudposse/atmos/internal/exec"
Expand All @@ -30,26 +29,26 @@ var listComponentsCmd = &cobra.Command{

stackFlag, err := flags.GetString("stack")
if err != nil {
u.PrintMessageInColor(fmt.Sprintf("Error getting the 'stack' flag: %v", err), color.New(color.FgRed))
u.PrintInvalidUsageErrorAndExit(fmt.Errorf("Error getting the `stack` flag: `%v`", err), "")
return
}

configAndStacksInfo := schema.ConfigAndStacksInfo{}
atmosConfig, err := config.InitCliConfig(configAndStacksInfo, true)
if err != nil {
u.PrintMessageInColor(fmt.Sprintf("Error initializing CLI config: %v", err), theme.Colors.Error)
u.PrintErrorMarkdownAndExit("Error Initializing CLI config", err, "")
return
}

stacksMap, err := e.ExecuteDescribeStacks(atmosConfig, "", nil, nil, nil, false, false, false, false, nil)
if err != nil {
u.PrintMessageInColor(fmt.Sprintf("Error describing stacks: %v", err), theme.Colors.Error)
u.PrintErrorMarkdownAndExit("Error describing stacks", err, "")
return
}

output, err := l.FilterAndListComponents(stackFlag, stacksMap)
if err != nil {
u.PrintMessageInColor(fmt.Sprintf("Error: %v"+"\n", err), theme.Colors.Warning)
u.PrintErrorMarkdownAndExit("", err, "")
return
}

Expand Down
Loading