feat: support listing helmfile and packer components#1471
Conversation
📝 WalkthroughWalkthroughAggregates component keys from Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor Caller
participant List as list_components.go
participant Stack as Stack (components map)
Caller->>List: getStackComponents(stack)
List->>Stack: inspect components.terraform (if present) -> collect keys
List->>Stack: inspect components.helmfile (if present) -> collect keys
List->>Stack: inspect components.packer (if present) -> collect keys
Note over List: aggregate found keys into allComponents
alt allComponents not empty
List-->>Caller: return allComponents
else no components found
List-->>Caller: return ErrNoComponentsFound
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Pre-merge checks (2 passed, 1 warning)❌ Failed Checks (1 warning)
✅ Passed Checks (2 passed)
📜 Recent review detailsConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro 💡 Knowledge Base configuration:
You can enable these sources in your CodeRabbit configuration. 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
✨ Finishing Touches
🧪 Generate unit tests
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (4)
pkg/list/list_components.go (2)
34-49: DRY the per-type extraction loop.Trim repetition by looping over known component types.
- // Extract terraform components - if terraformComponents, ok := componentsMap["terraform"].(map[string]any); ok { - allComponents = append(allComponents, lo.Keys(terraformComponents)...) - } - - // Extract helmfile components - if helmfileComponents, ok := componentsMap["helmfile"].(map[string]any); ok { - allComponents = append(allComponents, lo.Keys(helmfileComponents)...) - } - - // Extract packer components - if packerComponents, ok := componentsMap["packer"].(map[string]any); ok { - allComponents = append(allComponents, lo.Keys(packerComponents)...) - } + componentTypes := []string{"terraform", "helmfile", "packer"} + for _, typ := range componentTypes { + if m, ok := componentsMap[typ].(map[string]any); ok { + allComponents = append(allComponents, lo.Keys(m)...) + } + }
56-56: Option: return a de-duped slice here.Keeps single-stack callers clean even if types overlap; callers can still sort later.
- return allComponents, nil + return lo.Uniq(allComponents), nilpkg/list/list_components_test.go (2)
114-120: Good packer fixture; add a unit subtest that mixes all types.Add a subtest in TestGetStackComponents to assert aggregation across terraform, helmfile, and packer in one stack.
t.Run("aggregates terraform, helmfile, and packer", func(t *testing.T) { stackData := map[string]any{ "components": map[string]any{ "terraform": map[string]any{"vpc": map[string]any{}}, "helmfile": map[string]any{"echo-server": map[string]any{}}, "packer": map[string]any{"ami": map[string]any{}}, }, } components, err := getStackComponents(stackData) require.NoError(t, err) assert.ElementsMatch(t, []string{"vpc", "echo-server", "ami"}, components) })
130-134: Broaden exported-path coverage.Consider adding helmfile/packer entries to TestFilterAndListComponents’ stacksMap to verify the exported FilterAndListComponents aggregates non-terraform types, not just unit helpers.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
pkg/list/list_components.go(1 hunks)pkg/list/list_components_test.go(3 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.go
📄 CodeRabbit inference engine (.cursor/rules/atmos-rules.mdc)
**/*.go: Use Viper for managing configuration, environment variables, and flags
Use interfaces for external dependencies to facilitate mocking
All code must pass golangci-lint checks
Follow Go's error handling idioms
Use meaningful error messages
Wrap errors with context using fmt.Errorf("context: %w", err)
Consider using a custom error type for domain-specific errors
Follow standard Go coding style
Use gofmt and goimports to format code
Prefer short, descriptive variable names
Use snake_case for environment variables
Document all exported functions, types, and methods
Document complex logic with inline comments
Follow Go's documentation conventions
Use Viper for configuration management
Support configuration via files, environment variables, and flags
Follow the precedence order: flags > environment variables > config file > defaults
**/*.go: All comments in Go code must end with periods (enforced by golangci-lint godot).
Wrap all returned errors using static errors from the errors package; never return dynamic errors directly.
Always bind environment variables with viper.BindEnv() and provide ATMOS_ alternatives for each external var.
Separate structured logging from UI output: use stderr for prompts/errors to user; stdout only for data; never use logging for UI.
Most text UI must go to stderr; only data/results to stdout. Prefer utils.PrintfMessageToTUI for UI messages.
For non-standard execution paths, capture telemetry with telemetry.CaptureCmd or telemetry.CaptureCmdString and never capture user data.
Ensure cross-platform compatibility: prefer SDKs to external binaries, use filepath/os/runtime for portability, and add build constraints when needed.
Files:
pkg/list/list_components_test.gopkg/list/list_components.go
**/*_test.go
📄 CodeRabbit inference engine (.cursor/rules/atmos-rules.mdc)
**/*_test.go: Every new feature must include comprehensive unit tests
Test both happy paths and error conditions
Use table-driven tests for testing multiple scenarios
Include integration tests for command flows
Test CLI end-to-end when possible
Use test fixtures for complex inputs
Consider using testify/mock for creating mock implementations
**/*_test.go: Always use t.Skipf() with a reason for skipped tests; never use t.Skip() or reasonless t.Skipf().
Test files must mirror implementation structure and naming (e.g., aws_ssm_store_test.go).
Use table-driven unit tests and mock interfaces for external dependencies; target >80% coverage.
Files:
pkg/list/list_components_test.go
pkg/**/*_test.go
📄 CodeRabbit inference engine (CLAUDE.md)
Unit tests reside under pkg/ alongside implementations.
Files:
pkg/list/list_components_test.go
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
- GitHub Check: Analyze (go)
- GitHub Check: Analyze (javascript-typescript)
- GitHub Check: Lint (golangci)
- GitHub Check: Lint (golangci)
- GitHub Check: Analyze (go)
- GitHub Check: Lint (golangci)
- GitHub Check: Analyze (go)
- GitHub Check: Build (windows-latest, windows)
- GitHub Check: Lint (golangci)
- GitHub Check: Analyze (go)
- GitHub Check: Summary
🔇 Additional comments (1)
pkg/list/list_components_test.go (1)
56-56: LGTM: validates Helmfile/Packer items are surfaced.
|
@coderabbitai full review |
✅ Actions performedFull review triggered. |
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
pkg/list/list_components.go (1)
53-55: Wrap the “no components” error with context.Adds call-site breadcrumbs and aligns with error-wrapping guidance. This echoes an earlier suggestion.
- if len(allComponents) == 0 { - return nil, ErrNoComponentsFound - } + if len(allComponents) == 0 { + return nil, fmt.Errorf("getStackComponents: %w", ErrNoComponentsFound) + }
🧹 Nitpick comments (2)
pkg/list/list_components.go (2)
36-51: Reduce maintenance by aggregating generically (and preallocate).Rather than hard-coding terraform/helmfile/packer, iterate all component-type maps; this future-proofs for new types and trims allocations.
- var allComponents []string - - // Extract terraform components - if terraformComponents, ok := componentsMap["terraform"].(map[string]any); ok { - allComponents = append(allComponents, lo.Keys(terraformComponents)...) - } - - // Extract helmfile components - if helmfileComponents, ok := componentsMap["helmfile"].(map[string]any); ok { - allComponents = append(allComponents, lo.Keys(helmfileComponents)...) - } - - // Extract packer components - if packerComponents, ok := componentsMap["packer"].(map[string]any); ok { - allComponents = append(allComponents, lo.Keys(packerComponents)...) - } + // Aggregate keys from all component-type maps under "components". + size := 0 + for _, v := range componentsMap { + if m, ok := v.(map[string]any); ok { + size += len(m) + } + } + allComponents := make([]string, 0, size) + for _, v := range componentsMap { + if m, ok := v.(map[string]any); ok { + allComponents = append(allComponents, lo.Keys(m)...) + } + }
25-34: Consistently wrap parse errors with context.Keeps error chains descriptive while preserving sentinels.
- stackMap, ok := stackData.(map[string]any) + stackMap, ok := stackData.(map[string]any) if !ok { - return nil, ErrParseStacks + return nil, fmt.Errorf("getStackComponents: %w", ErrParseStacks) } - componentsMap, ok := stackMap["components"].(map[string]any) + componentsMap, ok := stackMap["components"].(map[string]any) if !ok { - return nil, ErrParseComponents + return nil, fmt.Errorf("getStackComponents: %w", ErrParseComponents) }
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
pkg/list/list_components.go(2 hunks)pkg/list/list_components_test.go(4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- pkg/list/list_components_test.go
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go
📄 CodeRabbit inference engine (.cursor/rules/atmos-rules.mdc)
**/*.go: Use Viper for managing configuration, environment variables, and flags
Use interfaces for external dependencies to facilitate mocking
All code must pass golangci-lint checks
Follow Go's error handling idioms
Use meaningful error messages
Wrap errors with context using fmt.Errorf("context: %w", err)
Consider using a custom error type for domain-specific errors
Follow standard Go coding style
Use gofmt and goimports to format code
Prefer short, descriptive variable names
Use snake_case for environment variables
Document all exported functions, types, and methods
Document complex logic with inline comments
Follow Go's documentation conventions
Use Viper for configuration management
Support configuration via files, environment variables, and flags
Follow the precedence order: flags > environment variables > config file > defaults
**/*.go: All comments in Go code must end with periods (enforced by golangci-lint godot).
Wrap all returned errors using static errors from the errors package; never return dynamic errors directly.
Always bind environment variables with viper.BindEnv() and provide ATMOS_ alternatives for each external var.
Separate structured logging from UI output: use stderr for prompts/errors to user; stdout only for data; never use logging for UI.
Most text UI must go to stderr; only data/results to stdout. Prefer utils.PrintfMessageToTUI for UI messages.
For non-standard execution paths, capture telemetry with telemetry.CaptureCmd or telemetry.CaptureCmdString and never capture user data.
Ensure cross-platform compatibility: prefer SDKs to external binaries, use filepath/os/runtime for portability, and add build constraints when needed.
Files:
pkg/list/list_components.go
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: Build (macos-latest, macos)
- GitHub Check: Build (windows-latest, windows)
- GitHub Check: Analyze (go)
- GitHub Check: Lint (golangci)
- GitHub Check: Summary
🔇 Additional comments (2)
pkg/list/list_components.go (2)
17-18: Good addition: generic “no components” sentinel.Introducing ErrNoComponentsFound clarifies intent and decouples from terraform-only errors.
114-116: Confirm intended UX for “list all” when empty.For a specific stack, “no components” errors; for all stacks, you return [] with nil error. Is this asymmetry intentional for CLI UX?
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
pkg/list/list_components.go (1)
71-73: Bug: fmt.Errorf uses two %w verbs (invalid) — can panic.Only one %w is allowed. Preserve both error identities using errors.Join (Go 1.20+).
- return nil, fmt.Errorf("%w: %s: %w", ErrProcessStack, stackName, err) + return nil, errors.Join(fmt.Errorf("%w: %s", ErrProcessStack, stackName), err)If Join isn’t available in your Go version, fallback (wrap only the sentinel, keep cause message):
- return nil, fmt.Errorf("%w: %s: %w", ErrProcessStack, stackName, err) + return nil, fmt.Errorf("%w: %s: %v", ErrProcessStack, stackName, err)pkg/list/list_components_test.go (1)
86-92: Fix expected error: missing “components” key is a parse error, not “no components found”.The implementation returns ErrParseComponents when the “components” map is absent; ErrNoComponentsFound is used when the map exists but has zero known component types.
- require.Error(t, err) - assert.True(t, errors.Is(err, ErrNoComponentsFound)) + require.Error(t, err) + assert.True(t, errors.Is(err, ErrParseComponents))
🧹 Nitpick comments (1)
pkg/list/list_components.go (1)
36-51: Reduce duplication by looping over supported component types.Same pattern repeated 3x; iterate over a small allowlist for maintainability.
- // Extract terraform components - if terraformComponents, ok := componentsMap["terraform"].(map[string]any); ok { - allComponents = append(allComponents, lo.Keys(terraformComponents)...) - } - - // Extract helmfile components - if helmfileComponents, ok := componentsMap["helmfile"].(map[string]any); ok { - allComponents = append(allComponents, lo.Keys(helmfileComponents)...) - } - - // Extract packer components - if packerComponents, ok := componentsMap["packer"].(map[string]any); ok { - allComponents = append(allComponents, lo.Keys(packerComponents)...) - } + // Extract components for known types + for _, t := range []string{"terraform", "helmfile", "packer"} { + if m, ok := componentsMap[t].(map[string]any); ok { + allComponents = append(allComponents, lo.Keys(m)...) + } + }
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
pkg/list/list_components.go(2 hunks)pkg/list/list_components_test.go(4 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: Acceptance Tests (macos-latest, macos)
- GitHub Check: Acceptance Tests (ubuntu-latest, linux)
- GitHub Check: Acceptance Tests (windows-latest, windows)
- GitHub Check: Summary
🔇 Additional comments (5)
pkg/list/list_components.go (2)
17-18: Good sentinel error for empty component sets.Clearer than the previous Terraform-specific error.
53-56: Right error on empty aggregated list.Returning ErrNoComponentsFound here matches the new semantics.
pkg/list/list_components_test.go (3)
56-60: Expanded expectations look good.Covers non-Terraform components surfaced by the new aggregator.
114-120: Nice: adds packer fixture to cover new type.
130-134: Good coverage for packer-only stack.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #1471 +/- ##
==========================================
+ Coverage 55.84% 56.00% +0.16%
==========================================
Files 274 274
Lines 28936 28947 +11
==========================================
+ Hits 16158 16212 +54
+ Misses 10986 10941 -45
- Partials 1792 1794 +2
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
These changes were released in v1.190.0-test.4. |
* feat: support listing helmfile and packer components * chore: add ErrNoComponentsFound error * chore: fix test with incorrect expected response
what
getStackComponentsinpkg/list/list_components.goto extract components fromterraform,helmfile, andpackersections, returning all found components in a single list. The function now returns a generic parse error if no components are found, instead of a Terraform-specific error.ErrParseTerraformComponentserror, simplifying error handling for component parsing.TestListComponentsWithStackto include Helmfile and Packer components in the expected output.TestGetComponentsForSpecificStackto verify Packer component extraction.why
atmos list componentsdoes not support components that are not Terraform. As we expand the tool to support more component types, being able to list these will be beneficial. This serves as a starting point, as we can add support for pretty printing components, types, and descriptions with Charmbracelet libraries. This pull request enhances the component extraction logic to support multiple component types (Terraform, Helmfile, and Packer) instead of only Terraform, and updates error handling and tests accordingly. The extraction function now aggregates all supported component types, and the tests have been expanded to cover these changes.Summary by CodeRabbit
New Features
Bug Fixes
Tests