Skip to content

feat: support listing helmfile and packer components#1471

Merged
aknysh merged 4 commits intomainfrom
support-listing-multiple-component-types
Sep 10, 2025
Merged

feat: support listing helmfile and packer components#1471
aknysh merged 4 commits intomainfrom
support-listing-multiple-component-types

Conversation

@RoseSecurity
Copy link
Contributor

@RoseSecurity RoseSecurity commented Sep 8, 2025

what

❯ atmos list components
armis/configuration

# Component structurealias atmos=/Users/rosesecurity/Desktop/Projects/atmos/build/atmos
❯ tree -d -L3
.
├── components
│   ├── packer
│   │   └── instance
│   └── terraform
│       └── armis
├── scripts
└── stacks
    ├── defaults
    │   ├── armis
    │   └── instance
    └── orgs

# Test locally-built Atmos
❯ atmos list components
armis/configuration
instance/defaults
  • Updated getStackComponents in pkg/list/list_components.go to extract components from terraform, helmfile, and packer sections, 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.
  • Removed the now-unused ErrParseTerraformComponents error, simplifying error handling for component parsing.
  • Updated test assertions in TestListComponentsWithStack to include Helmfile and Packer components in the expected output.
  • Removed the test case for missing Terraform components, as the function now handles missing components more generically.
  • Added new test data and a test case in TestGetComponentsForSpecificStack to verify Packer component extraction.

why

  • Currently, atmos list components does 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

    • Component listing now aggregates Terraform, Helmfile, and Packer components into a unified list and returns all found components.
  • Bug Fixes

    • Replaced per-source "missing" errors with a single clear "no components found" response when nothing is present.
  • Tests

    • Expanded test coverage with additional stacks and Packer components to validate unified listing behavior.

@RoseSecurity RoseSecurity requested a review from a team as a code owner September 8, 2025 19:36
@RoseSecurity RoseSecurity added the patch A minor, backward compatible change label Sep 8, 2025
@github-actions github-actions bot added the size/s Small size PR label Sep 8, 2025
@RoseSecurity RoseSecurity added enhancement New feature or request and removed size/s Small size PR labels Sep 8, 2025
@github-actions github-actions bot added the size/s Small size PR label Sep 8, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 8, 2025

📝 Walkthrough

Walkthrough

Aggregates component keys from terraform, helmfile, and packer in getStackComponents; introduces exported ErrNoComponentsFound when no components are found and removes the terraform-specific exported error. Tests updated to include a packer stack and expect additional components.

Changes

Cohort / File(s) Change summary
Component aggregation logic
pkg/list/list_components.go
Replace terraform-only component collection with aggregation across terraform, helmfile, and packer maps. Add exported ErrNoComponentsFound = errors.New("no components found"); remove ErrParseTerraformComponents. Return aggregated components or ErrNoComponentsFound when none exist.
Tests update and new cases
pkg/list/list_components_test.go
Update expected component list to include additional components (helmfile/packer). Add stack3 with a packer ami component and corresponding tests; remove terraform-only missing-component test.

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
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

minor

Suggested reviewers

  • osterman
  • aknysh

Pre-merge checks (2 passed, 1 warning)

❌ Failed Checks (1 warning)
Check Name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed Checks (2 passed)
Check Name Status Explanation
Title Check ✅ Passed The title succinctly and accurately summarizes the main enhancement—adding support for listing Helmfile and Packer components—making it clear and specific to the core change.
Description Check ✅ Passed The description provides clear “what” and “why” sections with usage examples, a summary of code changes, test updates, and rationale for supporting multiple component types, giving reviewers ample context.

📜 Recent 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 1601abf and e29807a.

📒 Files selected for processing (1)
  • pkg/list/list_components_test.go (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • 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). (4)
  • GitHub Check: Build (windows-latest, windows)
  • GitHub Check: Analyze (go)
  • GitHub Check: Lint (golangci)
  • GitHub Check: Summary
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch support-listing-multiple-component-types

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

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

🧹 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), nil
pkg/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.

📥 Commits

Reviewing files that changed from the base of the PR and between 4cdbe27 and b38477a.

📒 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.go
  • pkg/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.

@RoseSecurity
Copy link
Contributor Author

@coderabbitai full review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 8, 2025

✅ Actions performed

Full review triggered.

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: 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.

📥 Commits

Reviewing files that changed from the base of the PR and between b38477a and 1601abf.

📒 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?

coderabbitai[bot]
coderabbitai bot previously approved these changes Sep 8, 2025
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: 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 4cdbe27 and 1601abf.

📒 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
Copy link

codecov bot commented Sep 8, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 56.00%. Comparing base (5876172) to head (06ce943).
⚠️ Report is 2 commits behind head on main.

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     
Flag Coverage Δ
unittests 56.00% <100.00%> (+0.16%) ⬆️

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.

@aknysh aknysh added no-release Do not create a new release (wait for additional code changes) and removed patch A minor, backward compatible change labels Sep 10, 2025
Copy link
Member

@aknysh aknysh left a comment

Choose a reason for hiding this comment

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

thanks @RoseSecurity

@aknysh aknysh merged commit 055fc4f into main Sep 10, 2025
65 checks passed
@aknysh aknysh deleted the support-listing-multiple-component-types branch September 10, 2025 16:42
@github-actions
Copy link

These changes were released in v1.190.0-test.4.

osterman pushed a commit that referenced this pull request Sep 23, 2025
* feat: support listing helmfile and packer components

* chore: add ErrNoComponentsFound error

* chore: fix test with incorrect expected response
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request no-release Do not create a new release (wait for additional code changes) size/s Small size PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants