Skip to content

Commit

Permalink
Refactor list commands, add common flags & error handling
Browse files Browse the repository at this point in the history
- Updated `listCmd` to include subcommands for various resources
- Refactored metadata and settings listing logic into separate functions
- Introduced common flag handling across list commands
- Enhanced error handling with specific error types
- Added support for different output formats (table, json, yaml, csv)
- Improved code structure by organizing formatters and utilities
  • Loading branch information
Cerebrovinny committed Feb 24, 2025
1 parent de1e0e2 commit 5fbaeef
Show file tree
Hide file tree
Showing 22 changed files with 1,586 additions and 663 deletions.
9 changes: 5 additions & 4 deletions cmd/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import (
"github.com/spf13/cobra"
)

// listCmd commands list stacks and components
// listCmd represents the base list command that provides subcommands for listing
// various Atmos resources like stacks, components, settings, metadata, etc.
var listCmd = &cobra.Command{
Use: "list",
Short: "List available stacks and components",
Long: `Display a list of all available stacks and components defined in your project.`,
Use: "list [command]",
Short: "List Atmos resources and configurations",
Long: "List and display Atmos resources and configurations",
FParseErrWhitelist: struct{ UnknownFlags bool }{UnknownFlags: false},
Args: cobra.NoArgs,
}
Expand Down
123 changes: 49 additions & 74 deletions cmd/list_metadata.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
package cmd

import (
"fmt"

log "github.com/charmbracelet/log"
"github.com/spf13/cobra"

e "github.com/cloudposse/atmos/internal/exec"
"github.com/cloudposse/atmos/pkg/config"
list "github.com/cloudposse/atmos/pkg/list"
l "github.com/cloudposse/atmos/pkg/list"
fl "github.com/cloudposse/atmos/pkg/list/flags"
f "github.com/cloudposse/atmos/pkg/list/format"
u "github.com/cloudposse/atmos/pkg/list/utils"
"github.com/cloudposse/atmos/pkg/schema"
)

Expand All @@ -23,89 +28,59 @@ var listMetadataCmd = &cobra.Command{
Run: func(cmd *cobra.Command, args []string) {
// Check Atmos configuration
checkAtmosConfig()

configAndStacksInfo := schema.ConfigAndStacksInfo{}
atmosConfig, err := config.InitCliConfig(configAndStacksInfo, true)
if err != nil {
log.Error("failed to initialize CLI config", "error", err)
return
}

flags := cmd.Flags()

queryFlag, err := flags.GetString("query")
if err != nil {
log.Error("failed to get query flag", "error", err)
return
}

maxColumnsFlag, err := flags.GetInt("max-columns")
if err != nil {
log.Error("failed to get max-columns flag", "error", err)
return
}

formatFlag, err := flags.GetString("format")
output, err := listMetadata(cmd)
if err != nil {
log.Error("failed to get format flag", "error", err)
log.Error("failed to list metadata", "error", err)
return
}

delimiterFlag, err := flags.GetString("delimiter")
if err != nil {
log.Error("failed to get delimiter flag", "error", err)
return
}

stackPattern, err := flags.GetString("stack")
if err != nil {
log.Error("failed to get stack pattern flag", "error", err)
return
}

// Set appropriate default delimiter based on format
if formatFlag == list.FormatCSV && delimiterFlag == list.DefaultTSVDelimiter {
delimiterFlag = list.DefaultCSVDelimiter
}

// Get all stacks
stacksMap, err := e.ExecuteDescribeStacks(atmosConfig, "", nil, nil, nil, false, false, false, false, nil)
if err != nil {
log.Error("failed to describe stacks", "error", err)
return
}

// Use .metadata as the default query if none provided
if queryFlag == "" {
queryFlag = ".metadata"
}

output, err := list.FilterAndListValues(stacksMap, "", queryFlag, false, maxColumnsFlag, formatFlag, delimiterFlag, stackPattern)
if err != nil {
// Check if this is a 'no values found' error
if list.IsNoValuesFoundError(err) {
log.Error("no values found", "error", err)
} else {
log.Warn("failed to filter and list metadata", "error", err)
}
return
}

log.Info(output)
fmt.Println(output)

Check failure on line 37 in cmd/list_metadata.go

View workflow job for this annotation

GitHub Actions / golangci

[golangci] cmd/list_metadata.go#L37

use of `fmt.Println` forbidden by pattern `^(fmt\.Print(|f|ln)|print|println)$` (forbidigo)
Raw output
cmd/list_metadata.go:37:3: use of `fmt.Println` forbidden by pattern `^(fmt\.Print(|f|ln)|print|println)$` (forbidigo)
		fmt.Println(output)
		^
},
}

func init() {
// Add flags
listMetadataCmd.PersistentFlags().String("query", "", "JMESPath query to filter metadata (default: .metadata)")
listMetadataCmd.PersistentFlags().Int("max-columns", 10, "Maximum number of columns to display")
listMetadataCmd.PersistentFlags().String("format", "", "Output format (table, json, yaml, csv, tsv)")
listMetadataCmd.PersistentFlags().String("delimiter", "\t", "Delimiter for csv/tsv output (default: tab for tsv, comma for csv)")
listMetadataCmd.PersistentFlags().String("stack", "", "Stack pattern to filter (supports glob patterns, e.g., '*-dev-*', 'prod-*')")
fl.AddCommonListFlags(listMetadataCmd)

// Add stack pattern completion
AddStackCompletion(listMetadataCmd)

// Add command to list command
listCmd.AddCommand(listMetadataCmd)
}

func listMetadata(cmd *cobra.Command) (string, error) {
commonFlags, err := fl.GetCommonListFlags(cmd)
if err != nil {
return "", fmt.Errorf("error getting common flags: %v", err)

Check failure on line 52 in cmd/list_metadata.go

View workflow job for this annotation

GitHub Actions / golangci

[golangci] cmd/list_metadata.go#L52

do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf(\"error getting common flags: %v\", err)" (err113)
Raw output
cmd/list_metadata.go:52:14: do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf(\"error getting common flags: %v\", err)" (err113)
		return "", fmt.Errorf("error getting common flags: %v", err)
		           ^
}

if f.Format(commonFlags.Format) == f.FormatCSV && commonFlags.Delimiter == f.DefaultTSVDelimiter {
commonFlags.Delimiter = f.DefaultCSVDelimiter
}

// Initialize CLI config
configAndStacksInfo := schema.ConfigAndStacksInfo{}
atmosConfig, err := config.InitCliConfig(configAndStacksInfo, true)
if err != nil {
return "", fmt.Errorf("error initializing CLI config: %v", err)

Check failure on line 63 in cmd/list_metadata.go

View workflow job for this annotation

GitHub Actions / golangci

[golangci] cmd/list_metadata.go#L63

do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf(\"error initializing CLI config: %v\", err)" (err113)
Raw output
cmd/list_metadata.go:63:14: do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf(\"error initializing CLI config: %v\", err)" (err113)
		return "", fmt.Errorf("error initializing CLI config: %v", err)
		           ^
}

// Get all stacks
stacksMap, err := e.ExecuteDescribeStacks(atmosConfig, "", nil, nil, nil, false, false, false, false, nil)
if err != nil {
return "", fmt.Errorf("error describing stacks: %v", err)

Check failure on line 69 in cmd/list_metadata.go

View workflow job for this annotation

GitHub Actions / golangci

[golangci] cmd/list_metadata.go#L69

do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf(\"error describing stacks: %v\", err)" (err113)
Raw output
cmd/list_metadata.go:69:14: do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf(\"error describing stacks: %v\", err)" (err113)
		return "", fmt.Errorf("error describing stacks: %v", err)
		           ^
}

// Use .metadata as the default query if none provided
if commonFlags.Query == "" {
commonFlags.Query = ".metadata"
}

output, err := l.FilterAndListValues(stacksMap, "", commonFlags.Query, false, commonFlags.MaxColumns, commonFlags.Format, commonFlags.Delimiter, commonFlags.Stack)
if err != nil {
if u.IsNoValuesFoundError(err) {
return "", fmt.Errorf("no metadata found in any stacks with query '%s'", commonFlags.Query)

Check failure on line 80 in cmd/list_metadata.go

View workflow job for this annotation

GitHub Actions / golangci

[golangci] cmd/list_metadata.go#L80

do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf(\"no metadata found in any stacks with query '%s'\", commonFlags.Query)" (err113)
Raw output
cmd/list_metadata.go:80:15: do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf(\"no metadata found in any stacks with query '%s'\", commonFlags.Query)" (err113)
			return "", fmt.Errorf("no metadata found in any stacks with query '%s'", commonFlags.Query)
			           ^
}
return "", fmt.Errorf("error filtering and listing metadata: %v", err)

Check failure on line 82 in cmd/list_metadata.go

View workflow job for this annotation

GitHub Actions / golangci

[golangci] cmd/list_metadata.go#L82

do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf(\"error filtering and listing metadata: %v\", err)" (err113)
Raw output
cmd/list_metadata.go:82:14: do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf(\"error filtering and listing metadata: %v\", err)" (err113)
		return "", fmt.Errorf("error filtering and listing metadata: %v", err)
		           ^
}

return output, nil
}
125 changes: 50 additions & 75 deletions cmd/list_settings.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
package cmd

import (
"fmt"

log "github.com/charmbracelet/log"
"github.com/spf13/cobra"

e "github.com/cloudposse/atmos/internal/exec"
"github.com/cloudposse/atmos/pkg/config"
list "github.com/cloudposse/atmos/pkg/list"
l "github.com/cloudposse/atmos/pkg/list"
fl "github.com/cloudposse/atmos/pkg/list/flags"
f "github.com/cloudposse/atmos/pkg/list/format"
u "github.com/cloudposse/atmos/pkg/list/utils"
"github.com/cloudposse/atmos/pkg/schema"
)

Expand All @@ -23,90 +28,60 @@ var listSettingsCmd = &cobra.Command{
Run: func(cmd *cobra.Command, args []string) {
// Check Atmos configuration
checkAtmosConfig()

// Initialize logger from CLI config
configAndStacksInfo := schema.ConfigAndStacksInfo{}
atmosConfig, err := config.InitCliConfig(configAndStacksInfo, true)
if err != nil {
log.Error("failed to initialize CLI config", "error", err)
return
}

flags := cmd.Flags()

queryFlag, err := flags.GetString("query")
if err != nil {
log.Error("failed to get query flag", "error", err)
return
}

maxColumnsFlag, err := flags.GetInt("max-columns")
if err != nil {
log.Error("failed to get max-columns flag", "error", err)
return
}

formatFlag, err := flags.GetString("format")
output, err := listSettings(cmd)
if err != nil {
log.Error("failed to get format flag", "error", err)
log.Error("failed to list settings", "error", err)
return
}

delimiterFlag, err := flags.GetString("delimiter")
if err != nil {
log.Error("failed to get delimiter flag", "error", err)
return
}

stackPattern, err := flags.GetString("stack")
if err != nil {
log.Error("failed to get stack pattern flag", "error", err)
return
}

// Set appropriate default delimiter based on format
if formatFlag == list.FormatCSV && delimiterFlag == list.DefaultTSVDelimiter {
delimiterFlag = list.DefaultCSVDelimiter
}

// Get all stacks
stacksMap, err := e.ExecuteDescribeStacks(atmosConfig, "", nil, nil, nil, false, false, false, false, nil)
if err != nil {
log.Error("failed to describe stacks", "error", err)
return
}

// Use .settings as the default query if none provided
if queryFlag == "" {
queryFlag = ".settings"
}

output, err := list.FilterAndListValues(stacksMap, "", queryFlag, false, maxColumnsFlag, formatFlag, delimiterFlag, stackPattern)
if err != nil {
// Check if this is a 'no values found' error
if list.IsNoValuesFoundError(err) {
log.Error("no values found", "error", err)
} else {
log.Warn("failed to filter and list settings", "error", err)
}
return
}

log.Info(output)
fmt.Println(output)

Check failure on line 37 in cmd/list_settings.go

View workflow job for this annotation

GitHub Actions / golangci

[golangci] cmd/list_settings.go#L37

use of `fmt.Println` forbidden by pattern `^(fmt\.Print(|f|ln)|print|println)$` (forbidigo)
Raw output
cmd/list_settings.go:37:3: use of `fmt.Println` forbidden by pattern `^(fmt\.Print(|f|ln)|print|println)$` (forbidigo)
		fmt.Println(output)
		^
},
}

func init() {
// Add flags
listSettingsCmd.PersistentFlags().String("query", "", "JMESPath query to filter settings (default: .settings)")
listSettingsCmd.PersistentFlags().Int("max-columns", 10, "Maximum number of columns to display")
listSettingsCmd.PersistentFlags().String("format", "", "Output format (table, json, yaml, csv, tsv)")
listSettingsCmd.PersistentFlags().String("delimiter", "\t", "Delimiter for csv/tsv output (default: tab for tsv, comma for csv)")
listSettingsCmd.PersistentFlags().String("stack", "", "Stack pattern to filter (supports glob patterns, e.g., '*-dev-*', 'prod-*')")
fl.AddCommonListFlags(listSettingsCmd)

// Add stack pattern completion
AddStackCompletion(listSettingsCmd)

// Add command to list command
listCmd.AddCommand(listSettingsCmd)
}

func listSettings(cmd *cobra.Command) (string, error) {
// Get common flags
commonFlags, err := fl.GetCommonListFlags(cmd)
if err != nil {
return "", fmt.Errorf("error getting common flags: %v", err)

Check failure on line 53 in cmd/list_settings.go

View workflow job for this annotation

GitHub Actions / golangci

[golangci] cmd/list_settings.go#L53

do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf(\"error getting common flags: %v\", err)" (err113)
Raw output
cmd/list_settings.go:53:14: do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf(\"error getting common flags: %v\", err)" (err113)
		return "", fmt.Errorf("error getting common flags: %v", err)
		           ^
}

if f.Format(commonFlags.Format) == f.FormatCSV && commonFlags.Delimiter == f.DefaultTSVDelimiter {
commonFlags.Delimiter = f.DefaultCSVDelimiter
}

// Initialize CLI config
configAndStacksInfo := schema.ConfigAndStacksInfo{}
atmosConfig, err := config.InitCliConfig(configAndStacksInfo, true)
if err != nil {
return "", fmt.Errorf("error initializing CLI config: %v", err)

Check failure on line 64 in cmd/list_settings.go

View workflow job for this annotation

GitHub Actions / golangci

[golangci] cmd/list_settings.go#L64

do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf(\"error initializing CLI config: %v\", err)" (err113)
Raw output
cmd/list_settings.go:64:14: do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf(\"error initializing CLI config: %v\", err)" (err113)
		return "", fmt.Errorf("error initializing CLI config: %v", err)
		           ^
}

// Get all stacks
stacksMap, err := e.ExecuteDescribeStacks(atmosConfig, "", nil, nil, nil, false, false, false, false, nil)
if err != nil {
return "", fmt.Errorf("error describing stacks: %v", err)

Check failure on line 70 in cmd/list_settings.go

View workflow job for this annotation

GitHub Actions / golangci

[golangci] cmd/list_settings.go#L70

do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf(\"error describing stacks: %v\", err)" (err113)
Raw output
cmd/list_settings.go:70:14: do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf(\"error describing stacks: %v\", err)" (err113)
		return "", fmt.Errorf("error describing stacks: %v", err)
		           ^
}

// Use .settings as the default query if none provided
if commonFlags.Query == "" {
commonFlags.Query = ".settings"
}

output, err := l.FilterAndListValues(stacksMap, "", commonFlags.Query, false, commonFlags.MaxColumns, commonFlags.Format, commonFlags.Delimiter, commonFlags.Stack)
if err != nil {
if u.IsNoValuesFoundError(err) {
return "", fmt.Errorf("no settings found in any stacks with query '%s'", commonFlags.Query)

Check failure on line 81 in cmd/list_settings.go

View workflow job for this annotation

GitHub Actions / golangci

[golangci] cmd/list_settings.go#L81

do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf(\"no settings found in any stacks with query '%s'\", commonFlags.Query)" (err113)
Raw output
cmd/list_settings.go:81:15: do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf(\"no settings found in any stacks with query '%s'\", commonFlags.Query)" (err113)
			return "", fmt.Errorf("no settings found in any stacks with query '%s'", commonFlags.Query)
			           ^
}
return "", fmt.Errorf("error filtering and listing settings: %v", err)

Check failure on line 83 in cmd/list_settings.go

View workflow job for this annotation

GitHub Actions / golangci

[golangci] cmd/list_settings.go#L83

do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf(\"error filtering and listing settings: %v\", err)" (err113)
Raw output
cmd/list_settings.go:83:14: do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf(\"error filtering and listing settings: %v\", err)" (err113)
		return "", fmt.Errorf("error filtering and listing settings: %v", err)
		           ^
}

return output, nil
}
Loading

0 comments on commit 5fbaeef

Please sign in to comment.