Skip to content

add pager to describe config command #1203

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

Open
wants to merge 29 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
2494cd1
add pager to describe config command
samtholiya Apr 14, 2025
0bd38c8
add model test
samtholiya Apr 14, 2025
eb55512
[autofix.ci] apply automated fixes
autofix-ci[bot] Apr 14, 2025
746b105
update structure
samtholiya Apr 15, 2025
bfa2e74
update with unit test cases
samtholiya Apr 15, 2025
b23ac9d
remove unwanted interface
samtholiya Apr 15, 2025
03fa985
[autofix.ci] apply automated fixes
autofix-ci[bot] Apr 15, 2025
a20d636
fix tests
samtholiya Apr 16, 2025
51bd717
updated describe config
samtholiya Apr 16, 2025
bb88f35
implement status with help
samtholiya Apr 20, 2025
b6752b9
error view added
samtholiya Apr 20, 2025
5119033
[autofix.ci] apply automated fixes
autofix-ci[bot] Apr 20, 2025
bc455d3
fix tests
samtholiya Apr 21, 2025
7ccc618
Update pkg/pager/model.go
samtholiya Apr 20, 2025
1fcf428
fix lint
samtholiya Apr 21, 2025
aa35f38
fix golangci-lint
samtholiya Apr 21, 2025
fda30bb
updated pager variable
samtholiya Apr 21, 2025
dfc53e7
fix tests
samtholiya Apr 21, 2025
dd1e6da
fixed test cases
samtholiya Apr 21, 2025
6b38f6b
update the pager variable logic
samtholiya Apr 22, 2025
151e8da
updated the pager logic
samtholiya Apr 22, 2025
29ff77a
updated test case
samtholiya Apr 22, 2025
e39721f
fix pager bool
samtholiya Apr 23, 2025
3fa3c1d
unit test case for isPagerEnabled
samtholiya Apr 24, 2025
1649fa7
fix error name
samtholiya Apr 24, 2025
c1de851
Merge branch 'main' into feature/dev-3131-add-pager-to-atmos-describe…
samtholiya Apr 24, 2025
91b348a
Merge branch 'main' into feature/dev-3131-add-pager-to-atmos-describe…
samtholiya Apr 27, 2025
f4ec621
Merge branch 'main' into feature/dev-3131-add-pager-to-atmos-describe…
osterman May 2, 2025
8feface
Merge branch 'main' into feature/dev-3131-add-pager-to-atmos-describe…
samtholiya May 8, 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
2 changes: 2 additions & 0 deletions cmd/describe.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,7 @@ var describeCmd = &cobra.Command{

func init() {
describeCmd.PersistentFlags().StringP("query", "q", "", "Query the results of an `atmos describe` command using `yq` expressions")
describeCmd.PersistentFlags().String("pager", "true", "Disable the paging user experience")

RootCmd.AddCommand(describeCmd)
}
32 changes: 30 additions & 2 deletions cmd/describe_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
"github.com/spf13/cobra"

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

Expand All @@ -14,11 +16,37 @@
Long: "This command displays the final, deep-merged CLI configuration after combining all relevant configuration files.",
FParseErrWhitelist: struct{ UnknownFlags bool }{UnknownFlags: false},
Args: cobra.NoArgs,
Run: func(cmd *cobra.Command, args []string) {
err := e.ExecuteDescribeConfigCmd(cmd, args)
RunE: func(cmd *cobra.Command, args []string) error {
flags := cmd.Flags()

format, err := flags.GetString("format")
if err != nil {
return err
}

Check warning on line 25 in cmd/describe_config.go

View check run for this annotation

Codecov / codecov/patch

cmd/describe_config.go#L19-L25

Added lines #L19 - L25 were not covered by tests

query, err := flags.GetString("query")
if err != nil {
return err
}

Check warning on line 30 in cmd/describe_config.go

View check run for this annotation

Codecov / codecov/patch

cmd/describe_config.go#L27-L30

Added lines #L27 - L30 were not covered by tests

atmosConfig, err := cfg.InitCliConfig(schema.ConfigAndStacksInfo{}, false)
if err != nil {
return err
}

Check warning on line 35 in cmd/describe_config.go

View check run for this annotation

Codecov / codecov/patch

cmd/describe_config.go#L32-L35

Added lines #L32 - L35 were not covered by tests

if cmd.Flags().Changed("pager") {
// TODO: update this post pr:https://github.com/cloudposse/atmos/pull/1174 is merged
atmosConfig.Settings.Terminal.Pager, err = cmd.Flags().GetString("pager")
if err != nil {
return err
}

Check warning on line 42 in cmd/describe_config.go

View check run for this annotation

Codecov / codecov/patch

cmd/describe_config.go#L37-L42

Added lines #L37 - L42 were not covered by tests
}

err = e.NewDescribeConfig(&atmosConfig).ExecuteDescribeConfigCmd(query, format, "")

Check warning on line 45 in cmd/describe_config.go

View check run for this annotation

Codecov / codecov/patch

cmd/describe_config.go#L45

Added line #L45 was not covered by tests
if err != nil {
u.PrintErrorMarkdown("", err, "")
}
return nil

Check warning on line 49 in cmd/describe_config.go

View check run for this annotation

Codecov / codecov/patch

cmd/describe_config.go#L49

Added line #L49 was not covered by tests
},
}

Expand Down
2 changes: 1 addition & 1 deletion cmd/docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@
u.LogErrorAndExit(err)
}

pager := atmosConfig.Settings.Terminal.Pager
pager := atmosConfig.Settings.Terminal.IsPagerEnabled()

Check warning on line 105 in cmd/docs.go

View check run for this annotation

Codecov / codecov/patch

cmd/docs.go#L105

Added line #L105 was not covered by tests
if !pager && atmosConfig.Settings.Docs.Pagination {
pager = atmosConfig.Settings.Docs.Pagination
u.LogWarning("'settings.docs.pagination' is deprecated and will be removed in a future version. Please use 'settings.terminal.pager' instead")
Expand Down
8 changes: 4 additions & 4 deletions go.mod

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

100 changes: 72 additions & 28 deletions internal/exec/describe_config.go
Original file line number Diff line number Diff line change
@@ -1,51 +1,95 @@
package exec

import (
u "github.com/cloudposse/atmos/pkg/utils"
"github.com/spf13/cobra"
"fmt"

log "github.com/charmbracelet/log"
"github.com/cloudposse/atmos/internal/tui/templates/term"
"github.com/cloudposse/atmos/pkg/pager"
"github.com/cloudposse/atmos/pkg/schema"

cfg "github.com/cloudposse/atmos/pkg/config"
u "github.com/cloudposse/atmos/pkg/utils"
)

// ExecuteDescribeConfigCmd executes `describe config` command
func ExecuteDescribeConfigCmd(cmd *cobra.Command, args []string) error {
flags := cmd.Flags()
const describeConfigTitle = "Atmos Config"

format, err := flags.GetString("format")
if err != nil {
return err
}
type DescribeConfigFormatError struct {
format string
}

query, err := flags.GetString("query")
if err != nil {
return err
}
func (e DescribeConfigFormatError) Error() string {
return fmt.Sprintf("invalid 'format': %s", e.format)
}

info, err := ProcessCommandLineArgs("", cmd, args, nil)
if err != nil {
return err
}
var ErrTTYNotSupported = fmt.Errorf("tty not supported for this command")

atmosConfig, err := cfg.InitCliConfig(info, false)
if err != nil {
return err
}
type describeConfigExec struct {
atmosConfig *schema.AtmosConfiguration
pageCreator pager.PageCreator
printOrWriteToFile func(format string, file string, data any) error
IsTTYSupportForStdout func() bool
}

var res any
func NewDescribeConfig(atmosConfig *schema.AtmosConfiguration) *describeConfigExec {
return &describeConfigExec{
atmosConfig: atmosConfig,
pageCreator: pager.New(),
printOrWriteToFile: printOrWriteToFile,
IsTTYSupportForStdout: term.IsTTYSupportForStdout,
}
}

// ExecuteDescribeConfigCmd executes `describe config` command.
func (d *describeConfigExec) ExecuteDescribeConfigCmd(query, format, output string) error {
var res *schema.AtmosConfiguration
var err error
if query != "" {
res, err = u.EvaluateYqExpression(&atmosConfig, atmosConfig, query)
res, err = u.EvaluateYqExpressionWithType[schema.AtmosConfiguration](d.atmosConfig, *d.atmosConfig, query)
if err != nil {
return err
}
} else {
res = atmosConfig
res = d.atmosConfig
}

err = printOrWriteToFile(format, "", res)
if err != nil {
return err
if d.atmosConfig.Settings.Terminal.IsPagerEnabled() {
err = d.viewConfig(format, res)
switch err.(type) {
case DescribeConfigFormatError:
return err
case nil:
return nil
default:
log.Debug("Failed to use pager")
}
}
return d.printOrWriteToFile(format, output, res)
}

func (d *describeConfigExec) viewConfig(format string, data *schema.AtmosConfiguration) error {
if !d.IsTTYSupportForStdout() {
return ErrTTYNotSupported
}
var content string
var err error
switch format {
case "yaml":
content, err = u.GetAtmosConfigYAML(data)
if err != nil {
return err
}

Check warning on line 80 in internal/exec/describe_config.go

View check run for this annotation

Codecov / codecov/patch

internal/exec/describe_config.go#L79-L80

Added lines #L79 - L80 were not covered by tests
case "json":
content, err = u.GetAtmosConfigJSON(data)
if err != nil {
return err
}

Check warning on line 85 in internal/exec/describe_config.go

View check run for this annotation

Codecov / codecov/patch

internal/exec/describe_config.go#L84-L85

Added lines #L84 - L85 were not covered by tests
default:
return DescribeConfigFormatError{
format,
}
}
if err := d.pageCreator.Run(describeConfigTitle, content); err != nil {
return err
}

Check warning on line 93 in internal/exec/describe_config.go

View check run for this annotation

Codecov / codecov/patch

internal/exec/describe_config.go#L92-L93

Added lines #L92 - L93 were not covered by tests
return nil
}
128 changes: 128 additions & 0 deletions internal/exec/describe_config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package exec

import (
"testing"

"github.com/cloudposse/atmos/pkg/pager"
"github.com/cloudposse/atmos/pkg/schema"
gomock "github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
)

func TestErrInvalidFormat_Error(t *testing.T) {
err := DescribeConfigFormatError{format: "invalid"}
assert.Equal(t, "invalid 'format': invalid", err.Error())
}

func TestDescribeConfig(t *testing.T) {
// Setup test data
config := &schema.AtmosConfiguration{
Components: schema.Components{
Terraform: schema.Terraform{
BasePath: "something",
},
},
Settings: schema.AtmosSettings{
Terminal: schema.Terminal{
Pager: "less",
},
},
}

t.Run("NewDescribeConfig", func(t *testing.T) {
dc := NewDescribeConfig(config)
assert.Equal(t, config, dc.atmosConfig)
assert.NotNil(t, dc.pageCreator)
assert.NotNil(t, dc.printOrWriteToFile)
})

t.Run("ExecuteDescribeConfigCmd_NoQuery_YAML_TTY", func(t *testing.T) {
// Mock dependencies
ctrl := gomock.NewController(t)
defer ctrl.Finish()

mockPager := pager.NewMockPageCreator(ctrl)
mockPager.EXPECT().Run(describeConfigTitle, gomock.Any()).Return(nil)
dc := &describeConfigExec{
atmosConfig: config,
pageCreator: mockPager,
IsTTYSupportForStdout: func() bool { return true },
}

err := dc.ExecuteDescribeConfigCmd("", "yaml", "")
assert.NoError(t, err)
})

t.Run("ExecuteDescribeConfigCmd_NoQuery_JSON_TTY", func(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
mockPager := pager.NewMockPageCreator(ctrl)
mockPager.EXPECT().Run(describeConfigTitle, gomock.Any()).Return(nil)

dc := &describeConfigExec{
atmosConfig: config,
pageCreator: mockPager,
IsTTYSupportForStdout: func() bool { return true },
}

err := dc.ExecuteDescribeConfigCmd("", "json", "")
assert.NoError(t, err)
})

t.Run("ExecuteDescribeConfigCmd_NoQuery_InvalidFormat_TTY", func(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

dc := &describeConfigExec{
atmosConfig: config,
IsTTYSupportForStdout: func() bool { return true },
}

err := dc.ExecuteDescribeConfigCmd("", "invalid", "")
assert.Error(t, err)
assert.Equal(t, DescribeConfigFormatError{format: "invalid"}, err)
})

t.Run("ExecuteDescribeConfigCmd_NoQuery_NoTTY", func(t *testing.T) {
dc := &describeConfigExec{
atmosConfig: config,
IsTTYSupportForStdout: func() bool { return false },
printOrWriteToFile: func(format, file string, data any) error {
assert.Equal(t, "yaml", format)
assert.Equal(t, "", file)
assert.Equal(t, config, data)
return nil
},
}

err := dc.ExecuteDescribeConfigCmd("", "yaml", "")
assert.NoError(t, err)
})

t.Run("ExecuteDescribeConfigCmd_WithQuery", func(t *testing.T) {
dc := &describeConfigExec{
atmosConfig: config,
IsTTYSupportForStdout: func() bool { return false },
printOrWriteToFile: func(format, file string, data any) error {
assert.Equal(t, "yaml", format)
assert.Equal(t, "", file)
assert.Equal(t, config, data)
return nil
},
}

err := dc.ExecuteDescribeConfigCmd("", "yaml", "")
assert.NoError(t, err)
})

t.Run("ExecuteDescribeConfigCmd_WithQuery_EvalError", func(t *testing.T) {
dc := &describeConfigExec{
atmosConfig: config,
IsTTYSupportForStdout: func() bool { return false },
}

err := dc.ExecuteDescribeConfigCmd(".component.terraform[", "yaml", "")
assert.Error(t, err)
assert.Equal(t, "EvaluateYqExpressionWithType: failed to evaluate YQ expression '.component.terraform[': bad expression, could not find matching `]`", err.Error())
})
}
2 changes: 1 addition & 1 deletion pkg/config/default.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ var (
ListMergeStrategy: "replace",
Terminal: schema.Terminal{
MaxWidth: templates.GetTerminalWidth(),
Pager: true,
Pager: "less",
Colors: true,
Unicode: true,
SyntaxHighlighting: schema.SyntaxHighlighting{
Expand Down
4 changes: 4 additions & 0 deletions pkg/config/load.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ func setEnv(v *viper.Viper) {
bindEnv(v, "settings.gitlab_token", "GITLAB_TOKEN")
bindEnv(v, "settings.inject_gitlab_token", "ATMOS_INJECT_GITLAB_TOKEN")
bindEnv(v, "settings.atmos_gitlab_token", "ATMOS_GITLAB_TOKEN")

bindEnv(v, "settings.terminal.pager", "ATMOS_PAGER", "PAGER")
}

func bindEnv(v *viper.Viper, key ...string) {
Expand All @@ -115,6 +117,8 @@ func setDefaultConfiguration(v *viper.Viper) {
v.SetDefault("settings.inject_github_token", true)
v.SetDefault("logs.file", "/dev/stderr")
v.SetDefault("logs.level", "Info")

v.SetDefault("settings.terminal.pager", true)
v.SetDefault("docs.generate.readme.output", "./README.md")
}

Expand Down
Loading
Loading