Skip to content

Add pager to atmos describe affected command #1236

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

Conversation

samtholiya
Copy link
Collaborator

@samtholiya samtholiya commented May 4, 2025

what

  • Updated describe affected with pager

image

why

  • Makes easier for users to view the content

references

Summary by CodeRabbit

  • New Features
    • Introduced an interactive terminal pager with scrolling, clipboard support, and syntax highlighting for command outputs.
    • Added a --pager flag to CLI commands to enable or disable paged output.
    • Enhanced terminal paging configuration with environment variable support and customizable pager settings.
  • Bug Fixes
    • Improved output formatting and paging reliability for large or complex command outputs.
  • Documentation
    • Updated CLI documentation to include the --pager flag usage and examples.
  • Tests
    • Added extensive tests covering pager behavior, configuration parsing, output formatting, and CLI command execution.
  • Style/Refactor
    • Refactored command execution and output handling to modularize paging, formatting, and argument parsing for better maintainability.

@samtholiya samtholiya added minor New features that do not break anything and removed size/xl labels May 4, 2025
Copy link

mergify bot commented May 4, 2025

Important

Cloud Posse Engineering Team Review Required

This pull request modifies files that require Cloud Posse's review. Please be patient, and a core maintainer will review your changes.

To expedite this process, reach out to us on Slack in the #pr-reviews channel.

@mergify mergify bot added the needs-cloudposse Needs Cloud Posse assistance label May 4, 2025
Copy link

mergify bot commented May 4, 2025

Warning

This PR exceeds the recommended limit of 1,000 lines.

Large PRs are difficult to review and may be rejected due to their size.

Please verify that this PR does not address multiple issues.
Consider refactoring it into smaller, more focused PRs to facilitate a smoother review process.

Copy link

codecov bot commented May 4, 2025

Codecov Report

Attention: Patch coverage is 49.18415% with 654 lines in your changes missing coverage. Please review.

Project coverage is 48.38%. Comparing base (c87cd51) to head (70871f0).
Report is 80 commits behind head on feature/dev-3162-add-pager-to-atmos-describe-component-command.

Files with missing lines Patch % Lines
internal/exec/docs_generate.go 41.15% 112 Missing and 21 partials ⚠️
pkg/pager/model.go 50.94% 103 Missing and 1 partial ⚠️
cmd/describe_affected.go 0.00% 79 Missing ⚠️
internal/exec/template_utils.go 52.38% 34 Missing and 16 partials ⚠️
internal/exec/describe_affected.go 54.83% 38 Missing and 4 partials ⚠️
internal/exec/terraform_outputs.go 59.77% 35 Missing ⚠️
pkg/utils/highlight_utils.go 7.89% 35 Missing ⚠️
internal/exec/terraform.go 45.65% 25 Missing ⚠️
cmd/describe_config.go 0.00% 23 Missing ⚠️
internal/exec/file_utils.go 30.00% 17 Missing and 4 partials ⚠️
... and 16 more
Additional details and impacted files
@@                                         Coverage Diff                                         @@
##           feature/dev-3162-add-pager-to-atmos-describe-component-command    #1236       +/-   ##
===================================================================================================
+ Coverage                                                           32.89%   48.38%   +15.48%     
===================================================================================================
  Files                                                                 223      229        +6     
  Lines                                                               23791    24602      +811     
===================================================================================================
+ Hits                                                                 7827    11904     +4077     
+ Misses                                                              14798    11111     -3687     
- Partials                                                             1166     1587      +421     
Flag Coverage Δ
unittests 48.38% <49.18%> (+15.48%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@samtholiya samtholiya changed the title Feature/dev 3182 add pager to atmos describe affected command Add pager to atmos describe affected command May 5, 2025
Copy link

mergify bot commented May 7, 2025

💥 This pull request now has conflicts. Could you fix it @samtholiya? 🙏

@mergify mergify bot added the conflict This PR has conflicts label May 7, 2025
@Benbentwo Benbentwo marked this pull request as ready for review May 8, 2025 20:24
@Benbentwo Benbentwo requested a review from a team as a code owner May 8, 2025 20:24
Copy link
Contributor

coderabbitai bot commented May 8, 2025

📝 Walkthrough
## Walkthrough

This change introduces a terminal pager to several `describe` commands in the CLI, notably `describe affected`, `describe component`, and `describe config`. It adds a pager flag, updates configuration and environment variable support for paging, refactors command execution for modularity, and implements a Bubble Tea-based pager with syntax highlighting. Tests and documentation are updated accordingly.

## Changes

| File(s) / Path(s)                                                                                 | Change Summary                                                                                                                                                                                                                           |
|---------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `cmd/describe.go`, `cmd/describe_component.go`, `cmd/describe_config.go`                           | Added/updated `--pager` flag and refactored command execution to support paging and explicit flag parsing.                                                                                                                              |
| `cmd/describe_affected.go`                                                                        | Refactored CLI argument parsing, added pager flag, and improved validation.                                                                                                                       |
| `internal/exec/describe_affected.go`, `internal/exec/describe_component.go`, `internal/exec/describe_config.go` | Refactored execution logic into new structs with pager integration, modularized output handling, added syntax highlighting, and improved TTY detection.                                           |
| `internal/exec/file_utils.go`, `pkg/utils/highlight_utils.go`, `pkg/utils/json_utils.go`, `pkg/utils/yaml_utils.go` | Updated function signatures to accept config pointers and support syntax highlighting for paged output.                                                                                           |
| `pkg/pager/pager.go`, `pkg/pager/model.go`, `pkg/pager/mock_pager.go`, `pkg/pager/model_test.go`  | Introduced Bubble Tea-based pager implementation, interface, and tests.                                                                                                                           |
| `pkg/schema/schema.go`, `pkg/schema/schema_test.go`                                               | Changed pager config type to string, added `IsPagerEnabled()` method, and tested pager enablement logic.                                                                                         |
| `pkg/config/default.go`, `pkg/config/load.go`                                                     | Updated default pager config to string, added environment variable binding for pager.                                                                                                             |
| `website/docs/cli/commands/describe/describe-component.mdx`                                       | Documented new `--pager` flag and usage example.                                                                                                                                                  |
| `go.mod`                                                                                          | Updated dependencies to support new pager and highlighting features.                                                                                                                              |
| `tests/cli_describe_component_test.go`, `tests/describe_test.go`, `internal/exec/describe_component_test.go`, `internal/exec/describe_config_test.go` | Added/updated tests for pager functionality, flag support, and output validation.                                                                          |
| `tests/snapshots/…`, `tests/test-cases/demo-stacks.yaml`                                          | Updated test snapshots and cases to reflect pager config changes and output.                                                                                                                      |
| Other files (`internal/exec/atlantis_generate_repo_config.go`, `internal/exec/describe_dependents.go`, etc.) | Updated print/output functions to accept config pointers for highlighting and paging.                                                                                                             |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant User
    participant CLI
    participant Config
    participant Pager
    participant Output

    User->>CLI: Run describe affected/component/config
    CLI->>Config: Load configuration
    CLI->>CLI: Parse flags (including --pager)
    CLI->>CLI: Execute describe logic (gather data)
    CLI->>Output: Format output (YAML/JSON)
    alt Pager enabled and TTY supports
        CLI->>Pager: Display output with syntax highlighting
        Pager->>User: Interactive paged view
    else
        CLI->>Output: Print or write output directly
        Output->>User: Output to stdout or file
    end

Assessment against linked issues

Objective Addressed Explanation
Add pager to atmos describe affected command (DEV-3182)
Pager loads only if content exceeds TTY max height (DEV-3182)
Pager supports forcing color via config or ENV (DEV-3182)
Pager implementation uses Bubble Tea (DEV-3182)

Suggested reviewers

  • aknysh
  • osterman

</details>

<!-- walkthrough_end -->
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details open="true">
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=cloudposse/atmos&utm_content=1236):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

### Support

Need help? Create a ticket on our [support page](https://www.coderabbit.ai/contact-us/support) for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` or `@auto-summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` or `@auto-title` anywhere in the PR title to generate the title automatically.

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🔭 Outside diff range comments (1)
cmd/describe_affected.go (1)

22-149: 💡 Verification agent

❓ Verification inconclusive

Add test coverage for new functions

The static analysis indicates that the new code lacks test coverage. Consider adding unit tests for:

  1. parseDescribeAffectedCliArgs
  2. setFlagValueInCliArgs
  3. checkFlagError

This will improve code reliability and help catch regressions.

Let's check if there are any existing tests for similar functions:


🏁 Script executed:

#!/bin/bash
# Look for test files that might contain related tests
find . -name "*_test.go" | xargs grep -l "parseDescribeAffectedCliArgs\|setFlagValueInCliArgs\|checkFlagError"

Length of output: 112


Add unit tests for CLI argument parsing helpers

The following functions in cmd/describe_affected.go currently have no test coverage. Please add unit tests that cover both success and error scenarios:

  • parseDescribeAffectedCliArgs
  • setFlagValueInCliArgs
  • checkFlagError

A new test file (e.g. cmd/describe_affected_test.go) in the same package should include cases for:

  • Valid flag combinations
  • Invalid --format values
  • Conflicting flags (e.g. --repo-path with --ref)
  • Error propagation from checkFlagError

This will help catch regressions in CLI flag handling.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 25-29: cmd/describe_affected.go#L25-L29
Added lines #L25 - L29 were not covered by tests


[warning] 62-66: cmd/describe_affected.go#L62-L66
Added lines #L62 - L66 were not covered by tests


[warning] 68-71: cmd/describe_affected.go#L68-L71
Added lines #L68 - L71 were not covered by tests


[warning] 73-76: cmd/describe_affected.go#L73-L76
Added lines #L73 - L76 were not covered by tests


[warning] 79-90: cmd/describe_affected.go#L79-L90
Added lines #L79 - L90 were not covered by tests


[warning] 92-100: cmd/describe_affected.go#L92-L100
Added lines #L92 - L100 were not covered by tests


[warning] 102-102: cmd/describe_affected.go#L102
Added line #L102 was not covered by tests


[warning] 105-131: cmd/describe_affected.go#L105-L131
Added lines #L105 - L131 were not covered by tests


[warning] 133-139: cmd/describe_affected.go#L133-L139
Added lines #L133 - L139 were not covered by tests


[warning] 141-141: cmd/describe_affected.go#L141
Added line #L141 was not covered by tests


[warning] 145-147: cmd/describe_affected.go#L145-L147
Added lines #L145 - L147 were not covered by tests

🪛 GitHub Check: golangci-lint

[failure] 62-62:
cyclomatic: function parseDescribeAffectedCliArgs has cyclomatic complexity 14 (> max enabled 10)


[failure] 93-93:
do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf("invalid '--format' flag '%s'. Valid values are 'json' (default) and 'yaml'", result.Format)"


[failure] 99-99:
do not define dynamic errors, use wrapped static errors instead: "errors.New("if the '--repo-path' flag is specified, the '--ref', '--sha', '--ssh-key' and '--ssh-key-password' flags can't be used")"

♻️ Duplicate comments (5)
internal/exec/describe_component_test.go (1)

13-13: Fix comment formatting: Add a period at the end.

The comment doesn't end with a period, which violates code style conventions.

-// --- Test ---
+// --- Test ---
🧰 Tools
🪛 GitHub Check: golangci-lint

[failure] 13-13:
Comment should end in a period

cmd/describe_component.go (1)

86-92: Sentence should start with a capital letter

The comment should start with a capital letter to follow code style conventions.

-// we prefer to panic because this is a developer error.
+// We prefer to panic because this is a developer error.
🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 90-90: cmd/describe_component.go#L90
Added line #L90 was not covered by tests

🪛 GitHub Check: golangci-lint

[failure] 86-86:
Sentence should start with a capital letter

pkg/utils/json_utils.go (1)

24-40: ⚠️ Potential issue

Improved error handling needed in GetHighlightedJSON

The function ignores highlighting errors and returns success even when highlighting fails.

highlighted, err := HighlightCodeWithConfig(atmosConfig, prettyJSON.String())
if err != nil {
-	return prettyJSON.String(), nil
+	return prettyJSON.String(), err
}
🧰 Tools
🪛 GitHub Check: golangci-lint

[failure] 37-37:
error is not nil (line 35) but it returns nil

cmd/describe_affected.go (2)

98-100: 🛠️ Refactor suggestion

Use predefined errors instead of dynamic errors

Replace the dynamic error with a predefined error constant as flagged by static analysis.

+var ErrIncompatibleRepoPathFlags = errors.New("if the '--repo-path' flag is specified, the '--ref', '--sha', '--ssh-key' and '--ssh-key-password' flags can't be used")

+// In validateRepoPathFlags function
if result.RepoPath != "" && (result.Ref != "" || result.SHA != "" || result.SSHKeyPath != "" || result.SSHKeyPassword != "") {
-	return errors.New("if the '--repo-path' flag is specified, the '--ref', '--sha', '--ssh-key' and '--ssh-key-password' flags can't be used")
+	return ErrIncompatibleRepoPathFlags
}
🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 92-100: cmd/describe_affected.go#L92-L100
Added lines #L92 - L100 were not covered by tests

🪛 GitHub Check: golangci-lint

[failure] 99-99:
do not define dynamic errors, use wrapped static errors instead: "errors.New("if the '--repo-path' flag is specified, the '--ref', '--sha', '--ssh-key' and '--ssh-key-password' flags can't be used")"


92-94: 🛠️ Refactor suggestion

Use predefined errors instead of dynamic errors

Replace the dynamic error with a predefined error constant to follow best practices as flagged by static analysis.

+var ErrInvalidFormat = func(format string) error {
+	return fmt.Errorf("invalid '--format' flag '%s'. Valid values are 'json' (default) and 'yaml'", format)
+}

+// In validateFormatFlag function
if result.Format != "" && result.Format != "yaml" && result.Format != "json" {
-	return fmt.Errorf("invalid '--format' flag '%s'. Valid values are 'json' (default) and 'yaml'", result.Format)
+	return ErrInvalidFormat(result.Format)
}
🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 92-100: cmd/describe_affected.go#L92-L100
Added lines #L92 - L100 were not covered by tests

🪛 GitHub Check: golangci-lint

[failure] 93-93:
do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf("invalid '--format' flag '%s'. Valid values are 'json' (default) and 'yaml'", result.Format)"

🧹 Nitpick comments (13)
cmd/describe.go (1)

18-18: Pager flag has unclear string parameter usage.

The flag uses a string type with "true" as default, but the description only mentions enable/disable functionality. Consider clarifying that the parameter can accept program names like "less" or "more" in addition to boolean values.

-	describeCmd.PersistentFlags().String("pager", "true", "Disable / Enable the paging user experience")
+	describeCmd.PersistentFlags().String("pager", "true", "Control paging: 'true'/'false' to enable/disable, or specify pager name (e.g., 'less', 'more')")
internal/exec/atmos.go (1)

111-111: Consider adding tests for these modified lines

The static analysis flagged that these modified lines aren't covered by tests.

Consider adding test cases that verify the correct behavior when calling these functions with the atmosConfig parameter.

Also applies to: 123-123

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 111-111: internal/exec/atmos.go#L111
Added line #L111 was not covered by tests

tests/describe_test.go (2)

18-32: Good JSON format test with pager configuration

This test properly sets up the test environment, configures the pager via environment variable, and verifies that the command executes successfully with JSON output format.

Consider adding assertions to verify the actual output content matches expectations, not just that the command doesn't error.

 err := cmd.Execute()
 if err != nil {
     t.Fatalf("Failed to execute command: %v", err)
 }
+// Consider capturing output and verifying content
+// output := captureOutput(func() {
+//     cmd.Execute()
+// })
+// assert.Contains(t, output, "expected-json-content")

34-48: Good YAML format test with pager configuration

This test properly sets up the test environment, configures the pager via environment variable, and verifies that the command executes successfully with YAML output format.

As with the JSON test, consider adding assertions to verify the actual output content, not just that the command doesn't error.

 err := cmd.Execute()
 if err != nil {
     t.Fatalf("Failed to execute command: %v", err)
 }
+// Consider capturing output and verifying content
+// output := captureOutput(func() {
+//     cmd.Execute()
+// })
+// assert.Contains(t, output, "expected-yaml-content")
cmd/describe_config.go (1)

38-39: Remove TODO comment once PR #1174 is merged.

The TODO comment references another PR that might be merged by now. Check if this can be cleaned up.

-			// TODO: update this post pr:https://github.com/cloudposse/atmos/pull/1174 is merged
			atmosConfig.Settings.Terminal.Pager, err = cmd.Flags().GetString("pager")
cmd/describe_component.go (2)

25-27: Remove redundant argument validation

This manual argument check is redundant since you're already using cobra.ExactArgs(1) on line 20, which enforces the same constraint.

-if len(args) != 1 {
-	return errInvalidFlag
-}
🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 26-27: cmd/describe_component.go#L26-L27
Added lines #L26 - L27 were not covered by tests


87-92: Consider a less drastic error handling approach

While panic makes sense for developer errors, consider returning errors instead of crashing the program, especially in a CLI tool. This would make the code more robust and user-friendly.

-// checkFlagNotPresentError checks if the error is nil.
-func checkFlagNotPresentError(err error) {
-	if err != nil {
-		panic(err)
-	}
+// checkFlagNotPresentError checks if the error is nil and returns it.
+func checkFlagNotPresentError(err error) error {
+	if err != nil {
+		return fmt.Errorf("flag error: %w", err)
+	}
+	return nil
}
🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 90-90: cmd/describe_component.go#L90
Added line #L90 was not covered by tests

cmd/describe_affected.go (1)

145-149: Consider less abrupt error handling

Using panic for flag errors could be harsh in a CLI context. Consider returning errors instead for more graceful failure handling.

-func checkFlagError(err error) {
-	if err != nil {
-		panic(err)
-	}
-}

+func checkFlagError(err error) error {
+	return err
+}

+// Then in setFlagValueInCliArgs:
+var err error
+for k := range flagsKeyValue {
+	if !flags.Changed(k) {
+		continue
+	}
+	switch v := flagsKeyValue[k].(type) {
+	case *string:
+		*v, err = flags.GetString(k)
+	case *bool:
+		*v, err = flags.GetBool(k)
+	default:
+		return fmt.Errorf("unsupported type %T for flag %s", v, k)
+	}
+	if err != nil {
+		return fmt.Errorf("error getting flag %s: %w", k, err)
+	}
+}
+return nil
🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 145-147: cmd/describe_affected.go#L145-L147
Added lines #L145 - L147 were not covered by tests

internal/exec/describe_component.go (1)

46-111: Reduce function length

The function exceeds the 60-line limit (it's 64 lines). Consider extracting some logic into helper methods for better readability and to satisfy the linter.

You could extract the parameter initialization or the query evaluation logic into separate methods:

func (d *DescribeComponentExec) ExecuteDescribeComponentCmd(describeComponentParams DescribeComponentParams) error {
-	component := describeComponentParams.Component
-	stack := describeComponentParams.Stack
-	processTemplates := describeComponentParams.ProcessTemplates
-	processYamlFunctions := describeComponentParams.ProcessYamlFunctions
-	skip := describeComponentParams.Skip
-	query := describeComponentParams.Query
-	pager := describeComponentParams.Pager
-	format := describeComponentParams.Format
-	file := describeComponentParams.File
+	params := d.extractParams(describeComponentParams)

	var err error
	var atmosConfig schema.AtmosConfiguration

	atmosConfig, err = d.initCliConfig(schema.ConfigAndStacksInfo{
-		ComponentFromArg: component,
-		Stack:            stack,
+		ComponentFromArg: params.component,
+		Stack:            params.stack,
	}, true)
	if err != nil {
		return err
	}

	componentSection, err := d.executeDescribeComponent(
-		component,
-		stack,
-		processTemplates,
-		processYamlFunctions,
-		skip,
+		params.component,
+		params.stack,
+		params.processTemplates,
+		params.processYamlFunctions,
+		params.skip,
	)
	if err != nil {
		return err
	}

	var res any
-	if pager != "" {
-		atmosConfig.Settings.Terminal.Pager = pager
+	if params.pager != "" {
+		atmosConfig.Settings.Terminal.Pager = params.pager
	}

-	if query != "" {
-		res, err = d.evaluateYqExpression(&atmosConfig, componentSection, query)
-		if err != nil {
-			return err
-		}
-	} else {
-		res = componentSection
-	}
+	res, err = d.evaluateQueryIfPresent(&atmosConfig, componentSection, params.query)
+	if err != nil {
+		return err
+	}

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

-	err = d.printOrWriteToFile(&atmosConfig, format, file, res)
+	err = d.printOrWriteToFile(&atmosConfig, params.format, params.file, res)
	if err != nil {
		return err
	}

	return nil
}

+type extractedParams struct {
+	component            string
+	stack                string
+	processTemplates     bool
+	processYamlFunctions bool
+	skip                 []string
+	query                string
+	pager                string
+	format               string
+	file                 string
+}
+
+func (d *DescribeComponentExec) extractParams(params DescribeComponentParams) extractedParams {
+	return extractedParams{
+		component:            params.Component,
+		stack:                params.Stack,
+		processTemplates:     params.ProcessTemplates,
+		processYamlFunctions: params.ProcessYamlFunctions,
+		skip:                 params.Skip,
+		query:                params.Query,
+		pager:                params.Pager,
+		format:               params.Format,
+		file:                 params.File,
+	}
+}
+
+func (d *DescribeComponentExec) evaluateQueryIfPresent(atmosConfig *schema.AtmosConfiguration, data any, query string) (any, error) {
+	if query == "" {
+		return data, nil
+	}
+	return d.evaluateYqExpression(atmosConfig, data, query)
+}
🧰 Tools
🪛 GitHub Check: golangci-lint

[failure] 46-46:
function-length: maximum number of lines per function exceeded; max 60 but got 64

pkg/utils/highlight_utils.go (1)

75-147: Consider breaking down the HighlightCodeWithConfig function

This function exceeds the 60-line limit (as flagged by static analysis). Consider extracting some of the lexer determination logic into a separate function.

func HighlightCodeWithConfig(config *schema.AtmosConfiguration, code string, format ...string) (string, error) {
	if !term.IsTerminal(int(os.Stdout.Fd())) {
		return code, nil
	}
	settings := GetHighlightSettings(config)
	if !settings.Enabled {
		return code, nil
	}

	// Get terminal width
	config.Settings.Terminal.MaxWidth = templates.GetTerminalWidth()

-	// Determine lexer based on format flag or content format
-	var lexerName string
-	if len(format) > 0 && format[0] != "" {
-		// Use format flag if provided
-		lexerName = strings.ToLower(format[0])
-	} else {
-		// This is just a fallback
-		trimmed := strings.TrimSpace(code)
-
-		// Try to parse as JSON first
-		if json.Valid([]byte(trimmed)) {
-			lexerName = "json"
-		} else {
-			// Check for common YAML indicators
-			// 1. Contains key-value pairs with colons
-			// 2. Does not start with a curly brace (which could indicate malformed JSON)
-			// 3. Contains indentation or list markers
-			if (strings.Contains(trimmed, ":") && !strings.HasPrefix(trimmed, "{")) ||
-				strings.Contains(trimmed, "\n  ") ||
-				strings.Contains(trimmed, "\n- ") {
-				lexerName = "yaml"
-			} else {
-				// Fallback to plaintext if format is unclear
-				lexerName = "plaintext"
-			}
-		}
-	}
+	lexerName := determineLexer(code, format...)

	// Get lexer
	lexer := lexers.Get(lexerName)
	if lexer == nil {
		lexer = lexers.Fallback
	}
	// Get style
	s := styles.Get(settings.Theme)
	if s == nil {
		s = styles.Fallback
	}
	// Get formatter
	var formatter chroma.Formatter
	if settings.LineNumbers {
		formatter = formatters.TTY256
	} else {
		formatter = formatters.Get(settings.Formatter)
		if formatter == nil {
			formatter = formatters.Fallback
		}
	}
	// Create buffer for output
	var buf bytes.Buffer
	// Format the code
	iterator, err := lexer.Tokenise(nil, code)
	if err != nil {
		return code, err
	}
	err = formatter.Format(&buf, s, iterator)
	if err != nil {
		return code, err
	}
	return buf.String(), nil
}

+// determineLexer determines the appropriate lexer name based on format or content
+func determineLexer(code string, format ...string) string {
+	// Use format flag if provided
+	if len(format) > 0 && format[0] != "" {
+		return strings.ToLower(format[0])
+	}
+
+	// Otherwise, try to determine from content
+	trimmed := strings.TrimSpace(code)
+
+	// Try to parse as JSON first
+	if json.Valid([]byte(trimmed)) {
+		return "json"
+	}
+
+	// Check for common YAML indicators
+	// 1. Contains key-value pairs with colons
+	// 2. Does not start with a curly brace (which could indicate malformed JSON)
+	// 3. Contains indentation or list markers
+	if (strings.Contains(trimmed, ":") && !strings.HasPrefix(trimmed, "{")) ||
+		strings.Contains(trimmed, "\n  ") ||
+		strings.Contains(trimmed, "\n- ") {
+		return "yaml"
+	}
+
+	// Fallback to plaintext if format is unclear
+	return "plaintext"
+}
🧰 Tools
🪛 GitHub Check: golangci-lint

[failure] 75-75:
function-length: maximum number of lines per function exceeded; max 60 but got 71

internal/exec/describe_config.go (1)

55-66: Pager skips --output writing

When the pager is enabled and there’s no error, the function returns early (line 61).
This means output is silently ignored when the user asks for both paged viewing and file export.

A small tweak lets us keep today’s UX while still honoring --output:

-        case nil:
-            return nil
+        case nil:
+            if output == "" {
+                return nil          // pure pager workflow
+            }
+            // fall-through to file writer

This keeps behaviour unchanged for the majority case yet still writes the file when requested.

pkg/pager/model.go (1)

80-82: Forward reference is safe but can be sharper

statusBarMessageHelpStyle relies on the green constant declared later.
Go handles this at compile-time, yet grouping the colour declarations first would improve readability and avoid head-scratchers for new contributors.

internal/exec/describe_affected.go (1)

219-328: Duplicate legacy function

ExecuteDescribeAffectedCmd duplicates the new struct’s logic almost verbatim.
Unless kept for backwards compatibility, this increases maintenance cost and test surface.
Evaluate deprecating it or delegating to DescribeAffectedExec.Execute.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f26dfe1 and 94cc2ce.

📒 Files selected for processing (43)
  • Makefile (1 hunks)
  • cmd/describe.go (1 hunks)
  • cmd/describe_affected.go (3 hunks)
  • cmd/describe_component.go (2 hunks)
  • cmd/describe_config.go (2 hunks)
  • cmd/docs.go (1 hunks)
  • go.mod (2 hunks)
  • internal/exec/atlantis_generate_repo_config.go (1 hunks)
  • internal/exec/atmos.go (2 hunks)
  • internal/exec/describe_affected.go (5 hunks)
  • internal/exec/describe_component.go (2 hunks)
  • internal/exec/describe_component_test.go (1 hunks)
  • internal/exec/describe_config.go (1 hunks)
  • internal/exec/describe_config_test.go (1 hunks)
  • internal/exec/describe_dependents.go (1 hunks)
  • internal/exec/describe_stacks.go (1 hunks)
  • internal/exec/describe_workflows.go (1 hunks)
  • internal/exec/file_utils.go (2 hunks)
  • pkg/atlantis/atlantis_generate_repo_config_test.go (2 hunks)
  • pkg/config/default.go (1 hunks)
  • pkg/config/load.go (2 hunks)
  • pkg/pager/mock_pager.go (1 hunks)
  • pkg/pager/model.go (1 hunks)
  • pkg/pager/model_test.go (1 hunks)
  • pkg/pager/pager.go (1 hunks)
  • pkg/schema/schema.go (1 hunks)
  • pkg/schema/schema_test.go (1 hunks)
  • pkg/utils/highlight_utils.go (3 hunks)
  • pkg/utils/json_utils.go (1 hunks)
  • pkg/utils/json_utils_test.go (1 hunks)
  • pkg/utils/yaml_utils.go (1 hunks)
  • pkg/utils/yq_test.go (5 hunks)
  • pkg/utils/yq_utils.go (1 hunks)
  • tests/cli_describe_component_test.go (1 hunks)
  • tests/describe_test.go (1 hunks)
  • tests/snapshots/TestCLICommands_atmos_describe_config.stdout.golden (1 hunks)
  • tests/snapshots/TestCLICommands_atmos_describe_config_-f_yaml.stdout.golden (1 hunks)
  • tests/snapshots/TestCLICommands_atmos_describe_config_imports.stderr.golden (1 hunks)
  • tests/snapshots/TestCLICommands_atmos_describe_config_imports.stdout.golden (1 hunks)
  • tests/snapshots/TestCLICommands_atmos_describe_configuration.stderr.golden (1 hunks)
  • tests/snapshots/TestCLICommands_atmos_describe_configuration.stdout.golden (1 hunks)
  • tests/test-cases/demo-stacks.yaml (3 hunks)
  • website/docs/cli/commands/describe/describe-component.mdx (2 hunks)
🧰 Additional context used
🧠 Learnings (2)
internal/exec/describe_stacks.go (1)
Learnt from: Cerebrovinny
PR: cloudposse/atmos#764
File: internal/exec/describe_stacks.go:289-295
Timestamp: 2024-11-13T21:37:07.852Z
Learning: In the `internal/exec/describe_stacks.go` file of the `atmos` project written in Go, avoid extracting the stack name handling logic into a helper function within the `ExecuteDescribeStacks` method, even if the logic appears duplicated.
internal/exec/describe_affected.go (1)
Learnt from: samtholiya
PR: cloudposse/atmos#1147
File: internal/exec/validate_schema.go:42-57
Timestamp: 2025-04-11T22:06:46.999Z
Learning: The "ExecuteAtmosValidateSchemaCmd" function in internal/exec/validate_schema.go has been reviewed and confirmed to have acceptable cognitive complexity despite static analysis warnings. The function uses a clean structure with only three if statements for error handling and delegates complex operations to helper methods.
🧬 Code Graph Analysis (10)
pkg/atlantis/atlantis_generate_repo_config_test.go (1)
pkg/utils/yaml_utils.go (1)
  • PrintAsYAML (44-51)
internal/exec/atmos.go (1)
pkg/utils/yaml_utils.go (1)
  • PrintAsYAML (44-51)
pkg/utils/yq_test.go (2)
pkg/utils/yaml_utils.go (1)
  • PrintAsYAML (44-51)
pkg/utils/yq_utils.go (1)
  • EvaluateYqExpression (46-80)
pkg/utils/yq_utils.go (2)
pkg/schema/schema.go (1)
  • AtmosConfiguration (13-46)
pkg/utils/yaml_utils.go (2)
  • ConvertToYAML (89-95)
  • UnmarshalYAML (222-224)
internal/exec/file_utils.go (3)
pkg/schema/schema.go (1)
  • AtmosConfiguration (13-46)
pkg/utils/yaml_utils.go (1)
  • PrintAsYAML (44-51)
pkg/utils/json_utils.go (1)
  • PrintAsJSON (15-22)
internal/exec/atlantis_generate_repo_config.go (1)
pkg/utils/yaml_utils.go (1)
  • PrintAsYAML (44-51)
pkg/schema/schema_test.go (1)
pkg/schema/schema.go (1)
  • Terminal (190-196)
internal/exec/describe_config_test.go (4)
internal/exec/describe_config.go (2)
  • DescribeConfigFormatError (16-18)
  • NewDescribeConfig (33-40)
pkg/schema/schema.go (6)
  • AtmosConfiguration (13-46)
  • Components (296-299)
  • Terraform (266-277)
  • Settings (746-750)
  • AtmosSettings (212-228)
  • Terminal (190-196)
pkg/pager/mock_pager.go (1)
  • NewMockPageCreator (25-29)
internal/tui/templates/term/term_writer.go (1)
  • IsTTYSupportForStdout (83-87)
cmd/describe_component.go (3)
internal/exec/describe_component.go (2)
  • NewDescribeComponentExec (35-44)
  • DescribeComponentParams (14-24)
pkg/utils/markdown_utils.go (1)
  • PrintErrorMarkdownAndExit (87-89)
cmd/cmd_utils.go (2)
  • ComponentsArgCompletion (755-777)
  • AddStackCompletion (748-753)
pkg/utils/highlight_utils.go (1)
pkg/schema/schema.go (2)
  • AtmosConfiguration (13-46)
  • SyntaxHighlighting (202-210)
🪛 GitHub Check: codecov/patch
cmd/docs.go

[warning] 105-105: cmd/docs.go#L105
Added line #L105 was not covered by tests

internal/exec/atmos.go

[warning] 111-111: internal/exec/atmos.go#L111
Added line #L111 was not covered by tests


[warning] 123-123: internal/exec/atmos.go#L123
Added line #L123 was not covered by tests

internal/exec/atlantis_generate_repo_config.go

[warning] 528-528: internal/exec/atlantis_generate_repo_config.go#L528
Added line #L528 was not covered by tests

cmd/describe_component.go

[warning] 26-27: cmd/describe_component.go#L26-L27
Added lines #L26 - L27 were not covered by tests


[warning] 90-90: cmd/describe_component.go#L90
Added line #L90 was not covered by tests

cmd/describe_config.go

[warning] 19-25: cmd/describe_config.go#L19-L25
Added lines #L19 - L25 were not covered by tests


[warning] 27-30: cmd/describe_config.go#L27-L30
Added lines #L27 - L30 were not covered by tests


[warning] 32-35: cmd/describe_config.go#L32-L35
Added lines #L32 - L35 were not covered by tests


[warning] 37-42: cmd/describe_config.go#L37-L42
Added lines #L37 - L42 were not covered by tests


[warning] 45-45: cmd/describe_config.go#L45
Added line #L45 was not covered by tests


[warning] 49-49: cmd/describe_config.go#L49
Added line #L49 was not covered by tests

cmd/describe_affected.go

[warning] 25-29: cmd/describe_affected.go#L25-L29
Added lines #L25 - L29 were not covered by tests


[warning] 62-66: cmd/describe_affected.go#L62-L66
Added lines #L62 - L66 were not covered by tests


[warning] 68-71: cmd/describe_affected.go#L68-L71
Added lines #L68 - L71 were not covered by tests


[warning] 73-76: cmd/describe_affected.go#L73-L76
Added lines #L73 - L76 were not covered by tests


[warning] 79-90: cmd/describe_affected.go#L79-L90
Added lines #L79 - L90 were not covered by tests


[warning] 92-100: cmd/describe_affected.go#L92-L100
Added lines #L92 - L100 were not covered by tests


[warning] 102-102: cmd/describe_affected.go#L102
Added line #L102 was not covered by tests


[warning] 105-131: cmd/describe_affected.go#L105-L131
Added lines #L105 - L131 were not covered by tests


[warning] 133-139: cmd/describe_affected.go#L133-L139
Added lines #L133 - L139 were not covered by tests


[warning] 141-141: cmd/describe_affected.go#L141
Added line #L141 was not covered by tests


[warning] 145-147: cmd/describe_affected.go#L145-L147
Added lines #L145 - L147 were not covered by tests

internal/exec/describe_affected.go

[warning] 94-103: internal/exec/describe_affected.go#L94-L103
Added lines #L94 - L103 were not covered by tests


[warning] 107-151: internal/exec/describe_affected.go#L107-L151
Added lines #L107 - L151 were not covered by tests


[warning] 155-155: internal/exec/describe_affected.go#L155
Added line #L155 was not covered by tests


[warning] 159-160: internal/exec/describe_affected.go#L159-L160
Added lines #L159 - L160 were not covered by tests


[warning] 162-162: internal/exec/describe_affected.go#L162
Added line #L162 was not covered by tests


[warning] 166-171: internal/exec/describe_affected.go#L166-L171
Added lines #L166 - L171 were not covered by tests


[warning] 173-186: internal/exec/describe_affected.go#L173-L186
Added lines #L173 - L186 were not covered by tests

🪛 GitHub Check: golangci-lint
internal/exec/describe_component_test.go

[failure] 13-13:
Comment should end in a period

cmd/describe_component.go

[failure] 86-86:
Sentence should start with a capital letter

cmd/describe_affected.go

[failure] 62-62:
cyclomatic: function parseDescribeAffectedCliArgs has cyclomatic complexity 14 (> max enabled 10)


[failure] 93-93:
do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf("invalid '--format' flag '%s'. Valid values are 'json' (default) and 'yaml'", result.Format)"


[failure] 99-99:
do not define dynamic errors, use wrapped static errors instead: "errors.New("if the '--repo-path' flag is specified, the '--ref', '--sha', '--ssh-key' and '--ssh-key-password' flags can't be used")"

internal/exec/describe_component.go

[failure] 46-46:
function-length: maximum number of lines per function exceeded; max 60 but got 64

pkg/utils/json_utils.go

[failure] 37-37:
error is not nil (line 35) but it returns nil

pkg/utils/highlight_utils.go

[failure] 75-75:
function-length: maximum number of lines per function exceeded; max 60 but got 71

internal/exec/describe_affected.go

[failure] 107-107:
cognitive complexity 28 of func (*DescribeAffectedExec).Execute is high (> 20)


[failure] 112-112:
ifElseChain: rewrite if-else to switch statement


[warning] 166-166:
if a.Query == "" has complex nested blocks (complexity: 13)


[failure] 218-218:
cognitive complexity 28 of func ExecuteDescribeAffectedCmd is high (> 20)


[failure] 341-341:
hugeParam: v is heavy (104 bytes); consider passing it by pointer


[failure] 370-370:
hugeParam: v is heavy (80 bytes); consider passing it by pointer

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Acceptance Tests (windows-latest, windows)
  • GitHub Check: Summary
🔇 Additional comments (73)
Makefile (1)

64-64: Good improvement to test coverage.

Adding the -coverpkg=./... flag ensures coverage measurement across all packages in the module, not just those being directly tested. This will give you better visibility into how the new pager functionality is being exercised throughout the codebase.

tests/snapshots/TestCLICommands_atmos_describe_config_imports.stdout.golden (1)

113-113: Properly updated pager configuration.

The change from boolean true to string "1" for the pager setting aligns with the broader refactoring to support multiple pager options. This is the right update to reflect the new string-based configuration approach.

tests/snapshots/TestCLICommands_atmos_describe_config.stdout.golden (1)

70-70: Correctly updated pager configuration format.

The change from boolean false to string "0" properly reflects the new string-based approach for pager configuration. This is consistent with the implementation changes in the codebase.

tests/snapshots/TestCLICommands_atmos_describe_configuration.stderr.golden (1)

16-16: Added appropriate debug message for pager failures.

This new debug log line provides visibility into pager usage failures, which is important for troubleshooting. This aligns well with the implementation of the new pager functionality.

pkg/config/default.go (1)

58-58: Good update from boolean to string for pager configuration.

Changing Pager from a boolean to string value "less" makes the configuration more flexible, allowing users to specify their preferred pager. This is a common pattern in CLI tools that aligns with standard Unix practices.

tests/snapshots/TestCLICommands_atmos_describe_config_imports.stderr.golden (1)

20-20: Appropriate debug message for pager failure.

The added debug message provides helpful diagnostic information when the pager fails to initialize or run, which improves troubleshooting.

tests/snapshots/TestCLICommands_atmos_describe_configuration.stdout.golden (2)

48-60: Well-structured terminal configuration section.

The terminal configuration section is comprehensive and includes all necessary options for controlling the terminal display, paging, and syntax highlighting capabilities.


50-50:

✅ Verification successful

Verify pager value in test snapshot.

The pager is set to "1" in this test snapshot. Ensure this value is intentional and documented, as it differs from both the default "less" in the code and typical boolean strings like "true"/"false".


🏁 Script executed:

#!/bin/bash
# Check if "1" is a valid pager setting according to the codebase
# Look for code that handles pager values
rg -A 5 -B 5 "IsPagerEnabled|pager.*value"

Length of output: 5222


Pager value validated
The snapshot’s pager: "1" maps to an enabled pager—IsPagerEnabled() explicitly treats "1" as valid. No changes required.

go.mod (1)

13-13: Direct dependency declaration for terminal pager components.

The change correctly reclassifies these dependencies from indirect to direct. This aligns with the new pager functionality implementation that directly uses these libraries for terminal UI rendering, ANSI styling, and clipboard support.

Also applies to: 51-51, 55-56

tests/snapshots/TestCLICommands_atmos_describe_config_-f_yaml.stdout.golden (1)

35-47: Added terminal configuration section with pager settings.

The new terminal configuration section properly defines default values for the pager and related settings. This snapshot update reflects the newly added terminal pager functionality.

tests/test-cases/demo-stacks.yaml (3)

77-78: Disabled pager in docs command test.

Setting ATMOS_PAGER: false ensures consistent test output by preventing the pager from activating during test execution.


96-97: Disabled pager in describe config YAML test.

Setting ATMOS_PAGER: false ensures consistent test output by preventing the pager from activating during test execution.


118-119: Disabled pager in describe config JSON test.

Setting ATMOS_PAGER: false ensures consistent test output by preventing the pager from activating during test execution.

pkg/atlantis/atlantis_generate_repo_config_test.go (1)

18-18: Updated PrintAsYAML calls with configuration parameter.

The function calls have been properly updated to include the atmosConfig parameter, matching the refactored PrintAsYAML function signature. This change is necessary to support the new terminal pager functionality.

Also applies to: 40-40

website/docs/cli/commands/describe/describe-component.mdx (2)

58-59: Good addition of pager example.

This example clearly shows users how to invoke the component description with pager functionality using the more pager tool.


79-79: Documentation for pager flag is complete.

The added --pager flag documentation clearly explains its purpose to enable/disable paging.

internal/exec/describe_stacks.go (1)

139-139: Updated function call to support configuration-aware output.

The change properly integrates the describe stacks command with the new pager functionality by passing the configuration pointer to the printOrWriteToFile function.

cmd/docs.go (1)

105-109: Good usage of new pager configuration logic.

The code now uses the new IsPagerEnabled() method while maintaining backward compatibility with the deprecated settings.docs.pagination setting.

Since this line was flagged as not covered by tests, consider adding a test case that verifies the pager enablement logic for the docs command:

#!/bin/bash
# Verify test coverage for the IsPagerEnabled method in docs.go

# Look for existing tests that might be testing related functionality
rg -t go "Test.*Docs.*Pager" --glob "**/*_test.go"

# Check if the IsPagerEnabled method has test coverage elsewhere
rg -t go "Test.*IsPagerEnabled" --glob "**/*_test.go"
🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 105-105: cmd/docs.go#L105
Added line #L105 was not covered by tests

pkg/schema/schema.go (2)

192-192: Good improvement from bool to string type for pager setting.

Changing the Pager field type from boolean to string adds flexibility, allowing users to specify which pager program to use instead of just turning it on or off.


198-200: Well-implemented pager enablement logic.

The IsPagerEnabled() method provides a clean interface to determine if paging should be enabled based on string values, supporting multiple affirmative options ("on", "less", "true", etc.). This maintains backward compatibility while adding new capabilities.

internal/exec/atmos.go (2)

123-123: Added atmosConfig parameter correctly to PrintAsYAML

The change passes the configuration to the YAML printer, enabling syntax highlighting and paging functionality for the describe dependents command output.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 123-123: internal/exec/atmos.go#L123
Added line #L123 was not covered by tests


111-111:

✅ Verification successful

Added atmosConfig parameter correctly to PrintAsYAML

The change passes the configuration to the YAML printer, aligning with the new pager feature. This enables syntax highlighting and paging functionality for the describe component command output.


🏁 Script executed:

#!/bin/bash
# Verify if all PrintAsYAML calls have been updated with the new parameter
rg "PrintAsYAML\([^&]" --type=go

Length of output: 962


All PrintAsYAML calls include the new atmosConfig parameter
Verified that every invocation of PrintAsYAML now accepts the atmosConfig argument:

  • internal/exec/atmos.go (uses u.PrintAsYAML(&atmosConfig, data))
  • internal/exec/file_utils.go (passes the pointer variable)
  • All tests in pkg/utils/yq_test.go
    No further updates required.
🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 111-111: internal/exec/atmos.go#L111
Added line #L111 was not covered by tests

internal/exec/describe_workflows.go (1)

79-79: Added atmosConfig parameter correctly to printOrWriteToFile

The change passes the configuration to the file utility function, allowing the describe workflows command to use the new pager functionality.

internal/exec/file_utils.go (3)

35-35: Added atmosConfig parameter correctly to printOrWriteToFile function signature

The function signature has been updated to accept the atmosConfig parameter, which is necessary for the pager functionality.


43-43: Properly passes atmosConfig to PrintAsYAML

The change correctly forwards the atmosConfig to the YAML printer when outputting to console.


56-56: Properly passes atmosConfig to PrintAsJSON

The change correctly forwards the atmosConfig to the JSON printer when outputting to console.

pkg/utils/json_utils_test.go (1)

9-33: Good test coverage for PrintAsJSON with the new parameter

The test verifies that PrintAsJSON works correctly with the new atmosConfig parameter, including terminal syntax highlighting settings.

pkg/config/load.go (2)

103-103: Configuration environment variable binding for pager looks good

Binding the terminal pager setting to both ATMOS_PAGER and system PAGER environment variables provides flexible configuration options for users.


121-121: Default configuration for pager is appropriate

Setting the default pager value to true ensures users get a good out-of-box experience with the new pager feature without requiring explicit configuration.

pkg/schema/schema_test.go (1)

40-71: Comprehensive test coverage for IsPagerEnabled method

The test covers all relevant string values that users might provide for the pager setting. It properly tests both enabling and disabling scenarios, including case sensitivity and empty string handling. This provides good confidence in the string interpretation logic.

pkg/utils/yq_test.go (1)

68-68: Function signature update consistently applied

All calls to PrintAsYAML have been correctly updated to include the atmosConfig parameter, aligning with the function signature change in pkg/utils/yaml_utils.go.

Also applies to: 76-76, 84-84, 92-92, 100-100, 108-108, 120-120, 132-132, 144-144, 155-155, 161-161, 167-167, 173-173

tests/cli_describe_component_test.go (1)

1-19: Good test case for the new pager feature

This test verifies that the describe component command works correctly with the new --pager flag set to "more" and YAML output format. The test properly sets up the CLI arguments and verifies successful execution.

internal/exec/atlantis_generate_repo_config.go (1)

528-528:

✅ Verification successful

Function signature change to PrintAsYAML now requires atmosConfig parameter

The signature change to u.PrintAsYAML adds the ability to use CLI configuration for output formatting and paging support. This is consistent with broader changes throughout the codebase.


🏁 Script executed:

#!/bin/bash
# Verify test coverage for this line
grep -n "PrintAsYAML.*&atmosConfig" internal/exec/atlantis_generate_repo_config_test.go || echo "Test coverage needed for PrintAsYAML with atmosConfig parameter at line 528"

Length of output: 335


🏁 Script executed:

#!/bin/bash
# Search for PrintAsYAML usage in any test files across the repo
rg -n 'PrintAsYAML' -g '*_test.go' || echo "No PrintAsYAML coverage found in any test"

Length of output: 1045


Verified test coverage for PrintAsYAML signature change

Confirmed that the updated call is covered in the existing tests:

  • pkg/atlantis/atlantis_generate_repo_config_test.go:40
    err = u.PrintAsYAML(&atmosConfig, atlantisYaml)

All set—no additional test needed.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 528-528: internal/exec/atlantis_generate_repo_config.go#L528
Added line #L528 was not covered by tests

internal/exec/describe_dependents.go (1)

79-79: Update to printOrWriteToFile now requires configuration parameter

The change adds a pointer to atmosConfig as the first parameter to printOrWriteToFile. This aligns with the refactoring work to provide CLI configuration context for output formatting and paging support.

pkg/utils/yq_utils.go (1)

82-116: Good addition of type-safe YQ expression evaluator

The new EvaluateYqExpressionWithType function adds type safety by using generics, providing a cleaner API for callers that know the expected return type. This is a solid improvement over the untyped version.

A few observations:

  1. The function properly returns a pointer to the evaluated result
  2. Error messages are clearly labeled with the function name
  3. Implementation closely follows the existing pattern for consistency
tests/describe_test.go (1)

10-16: Useful helper function for CLI command testing

The ExecuteCommand function provides a clean way to test CLI commands by setting command line arguments and executing the main CLI command.

internal/exec/describe_component_test.go (1)

14-125: Test implementation looks good!

The test function provides thorough coverage of the pager functionality with various formats and configurations. Good use of mocks and assertions to verify behavior.

pkg/utils/yaml_utils.go (2)

44-51: Good refactoring of YAML output handling.

The function now cleanly delegates YAML conversion and highlighting to the new helper function while maintaining the same interface. This separation of concerns improves maintainability.


53-63: Well-structured new helper function.

The GetHighlightedYAML function properly handles the conversion and highlighting process with appropriate error handling, returning plain YAML as a fallback if highlighting fails.

pkg/pager/pager.go (3)

8-11: Good use of interface for testability.

Defining a PageCreator interface and generating mocks with gomock enables thorough testing of code that depends on this functionality.


13-21: Solid implementation with dependency injection.

The pageCreator struct and constructor allow for dependency injection of the tea.Program creator, which improves testability.


23-37: Well-implemented pager with good terminal UI options.

The Run method creates a Bubble Tea program with appropriate options for alternate screen buffer and mouse tracking, enhancing the user experience.

cmd/describe_config.go (1)

19-49:

❌ Incorrect review comment

Improved error handling and integration of pager functionality.

The command now properly handles errors and integrates with the pager functionality. Good use of explicit flag parsing and config initialization.

The implementation is missing test coverage. Consider adding tests for this command to ensure reliability:


🏁 Script executed:

#!/bin/bash
# Check for test files that might test this code
fd -t f "test.*describe_config" -x cat {} \; | grep -i "test.*describeconfig"

Length of output: 82


Let’s dig deeper to confirm test coverage for the describe_config command:


🏁 Script executed:

#!/bin/bash
# Search for any Go test files referencing describe_config or NewDescribeConfig
rg -n "describe_config" -g "*_test.go" || true
rg -n "DescribeConfig" -g "*_test.go" || true
rg -n "NewDescribeConfig" -g "*_test.go" || true

Length of output: 2017


Tests for describe_config already exist
The CLI behavior in cmd/describe_config.go is exercised by existing unit tests in internal/exec/describe_config_test.go (e.g. TestDescribeConfig, NewDescribeConfig, ExecuteDescribeConfigCmd in various scenarios). No additional test files are required for core logic coverage.

• internal/exec/describe_config_test.go

Likely an incorrect or invalid review comment.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 19-25: cmd/describe_config.go#L19-L25
Added lines #L19 - L25 were not covered by tests


[warning] 27-30: cmd/describe_config.go#L27-L30
Added lines #L27 - L30 were not covered by tests


[warning] 32-35: cmd/describe_config.go#L32-L35
Added lines #L32 - L35 were not covered by tests


[warning] 37-42: cmd/describe_config.go#L37-L42
Added lines #L37 - L42 were not covered by tests


[warning] 45-45: cmd/describe_config.go#L45
Added line #L45 was not covered by tests


[warning] 49-49: cmd/describe_config.go#L49
Added line #L49 was not covered by tests

pkg/pager/mock_pager.go (1)

1-49: Mock implementation meets requirements for testing

This mock implementation of the PageCreator interface provides the necessary functionality for testing components that depend on paging behavior.

pkg/pager/model_test.go (5)

14-18: Good test coverage for model initialization

The Init test verifies the model initialization works as expected.


20-81: Comprehensive test suite for model updates

Test cases effectively cover key press handling, window sizing (both when ready and not ready), and viewport updates. This ensures the pager's core interaction logic works properly.


83-106: View rendering tests cover all states

Tests appropriately check both the not-ready initialization state and ready state with content rendering.


108-132: Footer view tests validate scroll percentage display

Tests verify the footer displays correct scroll information in different viewport scenarios.


134-163: Helper function tests ensure utility functions work properly

The max function and styling tests confirm the supporting UI elements function correctly.

cmd/describe_component.go (4)

12-12: Create a defined static error

Good approach using a defined static error rather than creating dynamic errors in the function.


20-21: Improved command argument validation

Using cobra.ExactArgs(1) enhances the command's validation by enforcing exactly one argument.


29-60: Structured parameter handling improves maintainability

The refactored approach of explicitly retrieving all flags and passing them as a structured params object makes the code more maintainable and testable.


72-72: Simplified flag description

The format flag description is now cleaner.

pkg/utils/json_utils.go (2)

15-22: Configuration-aware JSON printing

Good update to accept configuration for customizing the output format.


42-60: Consistent fallback approach for AtmosConfig JSON

This function correctly implements a fallback mechanism by checking for nil error explicitly.

internal/exec/describe_config_test.go (8)

1-15: Well-structured test setup for DescribeConfigFormatError

The test case effectively validates the error message format for the DescribeConfigFormatError type. This is a good practice to ensure consistent error messaging.


17-37: Good test coverage for constructor and initialization

The test cases validate both the correct creation of the describeConfigExec object and the proper initialization of its fields.

Tests ensure that:

  1. The config is properly assigned
  2. The pageCreator is initialized
  3. The printOrWriteToFile function is set

This provides good verification of the initialization logic.


39-54: Solid mocking approach for pager testing with TTY support

This test case effectively:

  1. Mocks the pager dependency
  2. Sets expectations for the pager's behavior
  3. Simulates TTY availability
  4. Tests the YAML format output path

The use of gomock provides clear validation of the pager interaction.


56-70: JSON format test ensures format flexibility

This test complements the YAML test by ensuring the JSON output format is properly handled with TTY support. The consistent pattern between test cases enhances readability.


72-84: Error handling validation for invalid formats

The test properly validates that the function correctly returns a DescribeConfigFormatError when an invalid format is provided, ensuring robust input validation.


86-100: Thorough testing of non-TTY output path

This test verifies the fallback behavior when TTY is not supported, using a mock function for printOrWriteToFile that validates the expected arguments and returns the expected value. The assertions confirm that the right format, file, and data are passed to the output function.


102-116: Test case for query execution

This test verifies the execution path when a query parameter is provided. The test structure follows the established pattern seen in other test cases.


118-127: Edge case validation for YQ expression evaluation errors

This test ensures proper error handling when a malformed query is provided, verifying that the appropriate error message is returned, which helps with debugging.

cmd/describe_affected.go (1)

22-30: Improved separation of concerns

The refactoring separates CLI argument parsing from execution logic, which improves code modularity. This aligns with the broader changes in the PR to introduce a pager feature.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 25-29: cmd/describe_affected.go#L25-L29
Added lines #L25 - L29 were not covered by tests

internal/exec/describe_component.go (4)

14-24: Well-structured parameter struct

Creating a dedicated DescribeComponentParams struct is a clear improvement over passing numerous individual parameters. This makes the code more maintainable and the function signature cleaner.


26-33: Good dependency management with struct fields

The DescribeComponentExec struct encapsulates dependencies effectively, making testing easier through dependency injection and improving the overall architecture.


35-44: Clean constructor with sensible defaults

The constructor provides appropriate default implementations while still allowing for dependency injection during testing.


113-139: Well-implemented viewConfig method

This method effectively:

  1. Checks for TTY support
  2. Formats the output based on the specified format
  3. Uses the pager to display the output

The error handling is comprehensive and the method follows a clear logical flow.

pkg/utils/highlight_utils.go (3)

33-33: Appropriate parameter type change

Changing from value to pointer parameter for AtmosConfiguration is a good performance improvement, avoiding unnecessary struct copying for this potentially large struct.


75-75: Consistent parameter order in HighlightCodeWithConfig

Moving the configuration parameter to the first position is a good practice for consistency across the codebase.

🧰 Tools
🪛 GitHub Check: golangci-lint

[failure] 75-75:
function-length: maximum number of lines per function exceeded; max 60 but got 71


175-175: Proper pointer usage in struct method

Using a pointer to the struct's config field is appropriate here to avoid unnecessary copying and align with the updated function signature.

internal/exec/describe_config.go (1)

66-67: Injectable writer not used

printOrWriteToFile is already a dependency, but calling it directly prevents mocking.

-   return d.printOrWriteToFile(d.atmosConfig, format, output, res)
+   return d.printOrWriteToFile(d.atmosConfig, format, output, res)

(No functional change, only clarifies the dependency.)

internal/exec/describe_affected.go (1)

94-103: No test coverage for constructor

Lines 94-103 are uncovered. A trivial instantiation test would lift coverage and guard against future DI changes.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 94-103: internal/exec/describe_affected.go#L94-L103
Added lines #L94 - L103 were not covered by tests

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 94cc2ce and 22de4bb.

📒 Files selected for processing (1)
  • pkg/utils/highlight_utils.go (3 hunks)
🧰 Additional context used
🪛 GitHub Check: Build (macos-latest, macos)
pkg/utils/highlight_utils.go

[failure] 139-139:
undefined: HighlightSettings

🪛 GitHub Check: Build (ubuntu-latest, linux)
pkg/utils/highlight_utils.go

[failure] 139-139:
undefined: HighlightSettings

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Build (windows-latest, windows)
  • GitHub Check: Summary
🔇 Additional comments (4)
pkg/utils/highlight_utils.go (4)

33-33: Function signature updated correctly for configuration consistency.

The change to accept a pointer to AtmosConfiguration aligns with the broader refactoring for configuration consistency across the codebase. This approach ensures all highlight utility functions have access to the same configuration context.


74-114: Improved modularity through function refactoring.

The HighlightCodeWithConfig function has been successfully refactored to extract lexer and formatter selection into helper functions. This addresses the cyclomatic complexity issue flagged in previous reviews and improves code organization.


116-136: Good extraction of lexer selection logic.

The helper functions getLexer and isYAML effectively encapsulate the logic for determining the appropriate lexer based on content type. The YAML detection heuristic is simple but sufficient for common YAML formats.


172-172: Correctly updated to use pointer parameter.

The call to HighlightCodeWithConfig has been properly updated to pass a pointer to the configuration object, consistent with the function signature change.

@samtholiya
Copy link
Collaborator Author

samtholiya commented May 13, 2025

@coderabbitai please re-review

Copy link
Contributor

coderabbitai bot commented May 13, 2025

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Get the current state of PR #1236
gh pr view 1236 --repo cloudposse/atmos --json number,title,state,mergeable,mergeStateStatus,baseRefName,headRefName,commits,files

Length of output: 27166


🏁 Script executed:

#!/bin/bash
# Get the content of the describe_affected.go file to understand the implementation
cat cmd/describe_affected.go

# Check the CI status to understand why the merge state is "UNSTABLE"
gh pr checks 1236 --repo cloudposse/atmos

Length of output: 11572


🏁 Script executed:

#!/bin/bash
# Get the implementation of the pager in the exec package
cat internal/exec/describe_affected.go | grep -A 20 "viewWithScroll"

# Check for pending/failed CI jobs to understand the UNSTABLE status
gh pr checks 1236 --repo cloudposse/atmos --status pending,failure,cancelled

Length of output: 2856


🏁 Script executed:

#!/bin/bash
# Let's get a better understanding of the pager implementation
cat internal/exec/describe_affected.go | head -100

# Check which CI jobs are pending or failed
gh pr checks 1236 --repo cloudposse/atmos | grep -E "pending|failed|failure|cancelled"

Length of output: 3784


@samtholiya I've re-reviewed your PR, and it looks good! The implementation of the pager feature for the atmos describe affected command is well-structured with proper error handling and fallback mechanisms.

Key points from my review:

  1. The pager implementation in viewWithScroll has smart behavior:

    • Only activates when appropriate (TTY available, pager enabled, no output file)
    • Falls back gracefully to standard output if paging fails
    • Configurable through CLI flags
  2. The command line interface additions are well-designed:

    • Added --pager flag to control paging behavior
    • Good integration with existing flags like --upload
  3. PR Status:

    • No conflicts (mergeable)
    • The "UNSTABLE" status appears to be due to some pending CI jobs rather than failures
    • The pending jobs are primarily acceptance tests which often take longer to run

Overall, this is a nice quality-of-life improvement for users when working with large outputs from the describe affected command. The implementation is clean and follows good practices with appropriate error handling.

Copy link
Contributor

coderabbitai bot commented May 13, 2025

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Get PR details and check for conflicts
gh pr view 1236 --json number,title,state,mergeable

# Check recent commits to see what changes were made after the conflict notification
gh pr view 1236 --json commits --jq '.commits[-5:]'

# Check if there are any review comments or requested changes
gh pr view 1236 --json reviews --jq '.reviews'

Length of output: 67176


@samtholiya I've completed a re-review of your PR! The PR is now in a mergeable state and I can see several commits addressing previous feedback.

Looking at your recent commits, you've:

The PR looks good overall - you've successfully implemented a terminal pager for the "describe affected" command and related commands, which will significantly improve the user experience when viewing large outputs. The implementation uses Bubble Tea framework appropriately for an interactive terminal experience, and you've provided good test coverage for the new functionality.

Some notable strengths:

  • Clean separation of concerns with the pager implementation
  • Consistent updates across all relevant command handlers
  • Good documentation of the new --pager flag
  • Support for both enabling/disabling and selecting specific pager programs
  • Proper environment variable bindings for pager configuration

The PR is now mergeable, and the implementation meets the requirements of adding pager functionality to improve usability for viewing command output content.

( •ᴗ•)⚲

@samtholiya samtholiya force-pushed the feature/dev-3182-add-pager-to-atmos-describe-affected-command branch from 70871f0 to 215f607 Compare May 13, 2025 21:26
@samtholiya samtholiya force-pushed the feature/dev-3162-add-pager-to-atmos-describe-component-command branch from 85b8fc9 to 0a35636 Compare May 14, 2025 21:27
@Benbentwo Benbentwo merged commit a0f8b4f into feature/dev-3162-add-pager-to-atmos-describe-component-command May 15, 2025
83 checks passed
@Benbentwo Benbentwo deleted the feature/dev-3182-add-pager-to-atmos-describe-affected-command branch May 15, 2025 12:36
@mergify mergify bot removed the needs-cloudposse Needs Cloud Posse assistance label May 15, 2025
samtholiya added a commit that referenced this pull request May 15, 2025
@samtholiya samtholiya restored the feature/dev-3182-add-pager-to-atmos-describe-affected-command branch May 15, 2025 21:43
Copy link

mergify bot commented May 18, 2025

⚠️ The sha of the head commit of this PR conflicts with #1255. Mergify cannot evaluate rules on this PR. ⚠️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
minor New features that do not break anything
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants