Skip to content

feat(vendor): add --stack flag to vendor pull - #1889

Open
Erik Osterman (Cloud Posse) (osterman) wants to merge 34 commits into
mainfrom
osterman/vendor-pull-stack-flag
Open

feat(vendor): add --stack flag to vendor pull#1889
Erik Osterman (Cloud Posse) (osterman) wants to merge 34 commits into
mainfrom
osterman/vendor-pull-stack-flag

Conversation

@osterman

@osterman Erik Osterman (Cloud Posse) (osterman) commented Dec 18, 2025

Copy link
Copy Markdown
Member

what

  • Add --stack flag (-s) to atmos vendor pull: vendors every component declared in the given stack that has its own component.yaml, bypassing vendor.yaml entirely. Cannot be combined with --component.

why

  • Feature request: vendor all components used by a stack without specifying each one individually via repeated --component invocations.

references

  • This PR originally also carried a registry-pattern refactor of vendor internals (internal/exec/vendor*.gopkg/vendor/). While this branch was in flight, origin/main independently shipped a more complete rewrite of the same internals into pkg/vendoring/ (lockfile, SBOM/provenance, native component-updater PR workflow — feat(vendor): native component updater PR workflow #2756). That refactor has been dropped from this PR to avoid duplicating it; --stack is now implemented directly on top of main's current pkg/vendoring/internal/exec architecture.

@coderabbitai

coderabbitai Bot commented Dec 18, 2025

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Moves the vendoring subsystem out of internal/exec into a new pkg/vendor package, exposing public APIs (Pull, VendorComponent, VendorStack), wiring CLI commands (vendor pull/diff), exporting utilities (ProcessOciImage, GetSprigFuncMap) with perf instrumentation, and adding extensive vendoring logic, pattern matching, and tests.

Changes

Cohort / File(s) Summary
CLI command wiring
cmd/root.go, cmd/vendor/vendor.go, cmd/vendor/pull.go, cmd/vendor/diff.go
Replace side-effect vendor import with a named vendor import and SetAtmosConfig wiring; add pull/diff subcommands and propagate atmos config into vendor command handlers.
Vendor core package
pkg/vendor/vendor.go, pkg/vendor/config.go, pkg/vendor/options.go, pkg/vendor/types.go, pkg/vendor/uri.go, pkg/vendor/pattern.go, pkg/vendor/component.go, pkg/vendor/stack.go, pkg/vendor/model.go
New package implementing vendoring: Pull/VendorComponent/VendorStack entrypoints, config discovery/merge/import expansion, URI normalization, include/exclude pattern engine, component handling, stack orchestration, TUI model, and functional option types.
Error surfaces
errors/errors.go, pkg/vendor/errors.go
Add ErrVendoringNotConfigured in root errors and a comprehensive vendor-specific error set.
Internal exec utilities (exported)
internal/exec/oci_utils.go, internal/exec/template_utils.go, internal/exec/oci_utils_test.go, internal/exec/template_processing_test.go
Export and rename utilities (ProcessOciImage, GetSprigFuncMap), add perf.Track instrumentation, and update tests/call sites to use exported names.
Removed legacy code
internal/exec/vendor.go, internal/exec/vendor_utils.go
Delete legacy vendoring implementation, flag parsing, and internal vendor helpers (migrated into pkg/vendor).
Tests: vendor package
pkg/vendor/*_test.go, cmd/vendor/pull_test.go, pkg/vendor/*_integration_test.go
Large suite of new and migrated tests covering config loading/merging, pattern matching, component handling, stack workflows, option parsing, TUI behavior, and integration scenarios.
Test migrations & callsite updates
pkg/vender/*, pkg/vendor/* tests, pkg/vender/vendor_config_test.go, pkg/vendor/template_tokens_test.go, pkg/vender/component_vendor_test.go
Update imports and call sites to use pkg/vendor APIs and adjust return shapes (VendorConfigResult wrapper).
Snapshots & docs
tests/snapshots/*, tests/test-cases/vendor-test.yaml, website/blog/2025-12-18-vendor-stack-flag.mdx, CLAUDE.md
Update test snapshots, adjust expected error message casing, and add documentation for the new --stack flag and blog guidelines.

Sequence Diagram

sequenceDiagram
    rect rgba(200,200,255,0.5)
    participant CLI as CLI (cmd/vendor/pull.go)
    end
    rect rgba(200,255,200,0.5)
    participant Vendor as Vendor Orchestration (pkg/vendor/vendor.go)
    end
    rect rgba(255,220,200,0.5)
    participant Config as Config Processor (pkg/vendor/config.go)
    end
    rect rgba(255,200,200,0.5)
    participant Component as Component Handler (pkg/vendor/component.go)
    end
    rect rgba(240,240,200,0.5)
    participant OCI as OCI Utils (internal/exec)
    end

    CLI->>CLI: parse flags, build PullOptions
    CLI->>Vendor: Pull(atmosConfig, opts...)
    Vendor->>Vendor: ValidateFlags / decide route
    alt using vendor config
        Vendor->>Config: ReadAndProcessVendorConfigFile
        Config-->>Vendor: VendorConfigResult
    end
    Vendor->>Vendor: processAtmosVendorSource -> processTargets
    loop per target
        Vendor->>Component: prepare temp, copy/select files
        Component->>OCI: ProcessOciImage (if OCI)
        OCI-->>Component: artifact / temp dir
        Component-->>Vendor: installed package result
    end
    Vendor-->>CLI: return success or error
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • osterman
  • aknysh
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 47.90% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely identifies the primary change: adding the --stack flag to vendor pull.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch osterman/vendor-pull-stack-flag

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.

@github-actions github-actions Bot added the size/xl Extra large size PR label Dec 18, 2025
@mergify

mergify Bot commented Dec 18, 2025

Copy link
Copy Markdown

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.

@github-actions

github-actions Bot commented Dec 18, 2025

Copy link
Copy Markdown

Dependency Review

✅ No vulnerabilities or license issues found.

Scanned Files

None

@osterman Erik Osterman (Cloud Posse) (osterman) added the minor New features that do not break anything label Dec 18, 2025
@github-actions

Copy link
Copy Markdown

Warning

Changelog Entry Required

This PR is labeled minor or major but doesn't include a changelog entry.

Action needed: Add a new blog post in website/blog/ to announce this change.

Example filename: website/blog/2025-12-18-feature-name.mdx

Alternatively: If this change doesn't require a changelog entry, remove the minor or major label.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pkg/vender/component_vendor_test.go (1)

1-1: Critical: Fix package name typo.

The package is declared as vender but should be vendor to match the directory name and other vendor package files. Additionally, the filename itself contains the same typo (pkg/vender/ should be pkg/vendor/).

This will prevent compilation and breaks the module structure.

🧹 Nitpick comments (17)
pkg/vendor/types.go (1)

42-54: Consider consistent field naming.

IsComponent and IsMixins (lines 51-52) are exported names in an unexported struct. While valid Go, mixing casing can be confusing. Consider isComponent and isMixins for consistency with other fields like name and uri.

🔎 Suggested diff
 type pkgComponentVendor struct {
 	uri                 string
 	name                string
 	sourceIsLocalFile   bool
 	pkgType             pkgType
 	version             string
 	vendorComponentSpec *schema.VendorComponentSpec
 	componentPath       string
-	IsComponent         bool
-	IsMixins            bool
+	isComponent         bool
+	isMixins            bool
 	mixinFilename       string
 }
internal/exec/template_utils.go (1)

45-52: Consider tracking only the expensive initialization.

The defer perf.Track on Line 46 fires on every call to GetSprigFuncMap(), but only the first call (inside sync.Once) is expensive (~173MB). Subsequent calls just return a cached value. For a hot-path function, you could move the tracking inside the Once block to measure only the meaningful operation:

 func GetSprigFuncMap() template.FuncMap {
-	defer perf.Track(nil, "exec.GetSprigFuncMap")()
-
 	sprigFuncMapCacheOnce.Do(func() {
+		defer perf.Track(nil, "exec.GetSprigFuncMap.init")()
 		sprigFuncMapCache = sprig.FuncMap()
 	})
 	return sprigFuncMapCache
 }

That said, perf.Track returns a no-op when tracking is disabled, so the overhead is negligible. This is an optional optimization.

pkg/vendor/config.go (3)

21-50: Missing performance tracking in public function.

Per coding guidelines, public functions should include defer perf.Track(atmosConfig, "vendor.ReadAndProcessVendorConfigFile")() at the start.

🔎 Suggested fix:
+import (
+	"github.com/cloudposse/atmos/pkg/perf"
+	// ... other imports
+)

 func ReadAndProcessVendorConfigFile(
 	atmosConfig *schema.AtmosConfiguration,
 	vendorConfigFile string,
 	checkGlobalConfig bool,
 ) (schema.AtmosVendorConfig, bool, string, error) {
+	defer perf.Track(atmosConfig, "vendor.ReadAndProcessVendorConfigFile")()
+
 	var vendorConfig schema.AtmosVendorConfig

107-144: Consider logging duplicate import detection.

When a duplicate import is detected at line 137-140, the code silently skips it. A debug log would help troubleshooting.

🔎 Optional enhancement:
 		for _, imp := range currentConfig.Spec.Imports {
 			if !importMap[imp] {
 				importMap[imp] = true
 				vendorConfig.Spec.Imports = append(vendorConfig.Spec.Imports, imp)
+			} else {
+				log.Debug("Skipping duplicate import", "import", imp, "file", configFile)
 			}
 		}

261-312: Missing performance tracking in public function.

ReadAndProcessComponentVendorConfigFile is a public function and should include perf tracking per guidelines.

🔎 Suggested fix:
 func ReadAndProcessComponentVendorConfigFile(
 	atmosConfig *schema.AtmosConfiguration,
 	component string,
 	componentType string,
 ) (schema.VendorComponentConfig, string, error) {
+	defer perf.Track(atmosConfig, "vendor.ReadAndProcessComponentVendorConfigFile")()
+
 	var componentBasePath string
pkg/vendor/vendor_integration_test.go (1)

107-114: Cleanup could leave orphaned parent directories.

The cleanup removes individual file directories, but parent directories (e.g., components/terraform) may remain. Consider using a more thorough cleanup or tracking the top-level created directory.

🔎 Optional improvement:
 	t.Cleanup(func() {
-		for _, file := range expectedFiles {
-			// Remove individual files and their parent directories.
-			dir := filepath.Dir(file)
-			os.RemoveAll(dir)
-		}
+		// Clean up the entire components directory tree created by the test
+		os.RemoveAll("./components")
 	})
pkg/vendor/stack.go (2)

146-156: Default case falls through to terraform base path.

The default case on line 155 silently uses terraform's base path. This could mask configuration issues for unsupported component types.

🔎 Consider explicit handling:
 		case cfg.PackerComponentType:
 			componentBasePath = atmosConfig.Components.Packer.BasePath
 		default:
-			componentBasePath = atmosConfig.Components.Terraform.BasePath
+			log.Debug("Unknown component type, defaulting to terraform base path", "type", componentType)
+			componentBasePath = atmosConfig.Components.Terraform.BasePath
 		}

231-233: Silent return on empty URI could mask config issues.

When vendorComponentSpec.Source.Uri is empty, the function returns nil, nil without logging. A debug log would help users understand why a component wasn't vendored.

🔎 Optional enhancement:
 	if vendorComponentSpec.Source.Uri == "" {
+		log.Debug("Skipping component with empty URI", "component", componentName)
 		return nil, nil // No URI, nothing to vendor
 	}
pkg/vendor/model.go (2)

534-561: generateSkipFunction modifies input parameter.

Lines 543-544 reassign tempDir and src after converting to slash format. This works but could be confusing. Consider using local variables instead.

🔎 Optional clarity improvement:
 	return func(srcInfo os.FileInfo, src, dest string) (bool, error) {
 		// Skip .git directories
 		if filepath.Base(src) == ".git" {
 			return true, nil
 		}

 		// Normalize paths
-		tempDir = filepath.ToSlash(tempDir)
-		src = filepath.ToSlash(src)
-		trimmedSrc := u.TrimBasePathFromPath(tempDir+"/", src)
+		normalizedTempDir := filepath.ToSlash(tempDir)
+		normalizedSrc := filepath.ToSlash(src)
+		trimmedSrc := u.TrimBasePathFromPath(normalizedTempDir+"/", normalizedSrc)

 		// Check if the file should be excluded
 		if len(s.ExcludedPaths) > 0 {
-			return shouldExcludeFile(src, s.ExcludedPaths, trimmedSrc)
+			return shouldExcludeFile(normalizedSrc, s.ExcludedPaths, trimmedSrc)
 		}

 		// Only include the files that match the 'included_paths' patterns
 		if len(s.IncludedPaths) > 0 {
-			return shouldIncludeFile(src, s.IncludedPaths, trimmedSrc)
+			return shouldIncludeFile(normalizedSrc, s.IncludedPaths, trimmedSrc)
 		}

293-298: Replace custom max with stdlib version.

Project targets Go 1.25.2, which includes max in the stdlib. The custom max function at lines 293-298 can be replaced with the built-in, removing unnecessary code duplication.

pkg/vendor/component.go (3)

61-67: Missing performance tracking on public function.

Per coding guidelines, public functions should include defer perf.Track(atmosConfig, "vendor.ExecuteComponentVendorInternal")() followed by a blank line. The atmosConfig parameter is available here.

🔎 Suggested fix
 func ExecuteComponentVendorInternal(
 	atmosConfig *schema.AtmosConfiguration,
 	vendorComponentSpec *schema.VendorComponentSpec,
 	component string,
 	componentPath string,
 	dryRun bool,
 ) error {
+	defer perf.Track(atmosConfig, "vendor.ExecuteComponentVendorInternal")()
+
 	if vendorComponentSpec.Source.Uri == "" {

You'll also need to add the perf import:

"github.com/cloudposse/atmos/pkg/perf"

291-298: Inconsistent temp directory creation.

installMixin uses os.MkdirTemp directly (line 293), bypassing the createTempDir helper that applies 0o700 permission restrictions. This creates inconsistency with installComponent (line 233) and misses the permission hardening.

🔎 Suggested fix
 func installMixin(p *pkgComponentVendor, atmosConfig *schema.AtmosConfiguration) error {
-	tempDir, err := os.MkdirTemp("", "atmos-vendor-mixin")
-	if err != nil {
-		return fmt.Errorf("Failed to create temp directory %w", err)
-	}
+	tempDir, err := createTempDir()
+	if err != nil {
+		return err
+	}
 
 	defer removeTempDir(tempDir)

325-339: Consider extracting common copy options.

The same cp.Options configuration (PreserveTimes, PreserveOwner, OnSymlink) is repeated in handlePkgTypeLocalComponent (lines 270-278), installMixin (lines 325-339), and copyComponentToDestination (lines 351-367). A helper function could reduce duplication.

pkg/vendor/vendor.go (4)

3-17: Import grouping could be consolidated.

Per guidelines, imports should be organized as: stdlib, 3rd-party, then atmos packages (including internal/). Currently internal/exec is in a separate group from other atmos packages.

🔎 Suggested grouping
 import (
 	"fmt"
 	"net/url"
 	"path/filepath"
 	"strings"
 
 	"github.com/samber/lo"
 
 	cfg "github.com/cloudposse/atmos/pkg/config"
+	"github.com/cloudposse/atmos/internal/exec"
 	"github.com/cloudposse/atmos/pkg/perf"
 	"github.com/cloudposse/atmos/pkg/schema"
 	u "github.com/cloudposse/atmos/pkg/utils"
-
-	"github.com/cloudposse/atmos/internal/exec"
 )

110-112: Use available atmosConfig in perf.Track.

params.atmosConfig is available and used throughout this function, but nil is passed to perf.Track. This loses performance tracking context.

🔎 Suggested fix
 func executeAtmosVendorInternal(params *executeVendorOptions) error {
-	defer perf.Track(nil, "vendor.executeAtmosVendorInternal")()
+	defer perf.Track(params.atmosConfig, "vendor.executeAtmosVendorInternal")()

314-336: Missing perf.Track on public function with atmosConfig.

HandleVendorConfigNotExist is public and receives atmosConfig but lacks performance tracking.

🔎 Suggested fix
 func HandleVendorConfigNotExist(atmosConfig *schema.AtmosConfiguration, component, componentType string, dryRun bool) error {
+	defer perf.Track(atmosConfig, "vendor.HandleVendorConfigNotExist")()
+
 	if componentType == "" {

252-285: In-out pointer pattern works but could be clearer.

determineSourceType mutates *uri in place (line 269, 278). Returning (uri string, useOci, useLocal, isFile bool, err error) would be more idiomatic Go, but the current approach is functional.

Comment thread cmd/vendor/pull.go Outdated
Comment thread pkg/vendor/config.go Outdated
Comment thread pkg/vendor/config.go Outdated
Comment thread pkg/vendor/model.go Outdated
Comment thread pkg/vendor/stack.go Outdated
Comment thread website/blog/2025-01-17-vendor-stack-flag.mdx Outdated
@codecov

codecov Bot commented Dec 18, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 80.80495% with 248 lines in your changes missing coverage. Please review.
✅ Project coverage is 81.73%. Comparing base (42012b6) to head (eeea5c9).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
pkg/vendor/model.go 81.60% 41 Missing and 21 partials ⚠️
pkg/vendor/stack.go 71.85% 33 Missing and 5 partials ⚠️
pkg/vendor/component.go 62.10% 25 Missing and 11 partials ⚠️
pkg/vendor/vendor.go 85.71% 21 Missing and 14 partials ⚠️
cmd/vendor/pull.go 68.83% 14 Missing and 10 partials ⚠️
pkg/vendor/pattern.go 86.36% 9 Missing and 12 partials ⚠️
pkg/vendor/config.go 89.83% 11 Missing and 7 partials ⚠️
cmd/vendor/update.go 55.55% 6 Missing and 6 partials ⚠️
pkg/vendor/template.go 84.61% 1 Missing and 1 partial ⚠️

❌ Your patch check has failed because the patch coverage (80.80%) is below the target coverage (85.00%). You can increase the patch coverage or adjust the target coverage.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #1889      +/-   ##
==========================================
+ Coverage   81.68%   81.73%   +0.04%     
==========================================
  Files        1778     1783       +5     
  Lines      170559   170951     +392     
==========================================
+ Hits       139319   139724     +405     
+ Misses      23508    23488      -20     
- Partials     7732     7739       +7     
Flag Coverage Δ
unittests 81.73% <80.80%> (+0.04%) ⬆️

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

Files with missing lines Coverage Δ
cmd/root.go 78.06% <100.00%> (+0.02%) ⬆️
cmd/vendor/vendor.go 100.00% <100.00%> (ø)
errors/errors.go 100.00% <ø> (ø)
internal/exec/template_utils.go 75.92% <100.00%> (+0.08%) ⬆️
pkg/vendor/options.go 100.00% <100.00%> (ø)
pkg/vendor/types.go 100.00% <100.00%> (ø)
pkg/vendor/uri.go 92.46% <100.00%> (-2.68%) ⬇️
pkg/vendor/template.go 84.61% <84.61%> (ø)
cmd/vendor/update.go 75.41% <55.55%> (-3.94%) ⬇️
pkg/vendor/config.go 89.83% <89.83%> (ø)
... and 6 more

... and 12 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (1)
website/blog/2025-01-17-vendor-stack-flag.mdx (1)

44-44: Clarify vendor configuration file terminology.

This line still references component.yaml when the PR objectives note that vendor pull routing now prefers vendor.yaml. Update to reference the preferred file or clarify backward compatibility.

🔎 Suggested fix:
-The command will identify all components in the stack that have vendor configurations (via `component.yaml`) and vendor them together.
+The command will identify all components in the stack that have `vendor.yaml` configurations and vendor them together.
🧹 Nitpick comments (3)
pkg/vendor/stack.go (1)

147-156: Consider removing the default fallback.

The default case silently uses Terraform's base path for unknown component types. Since extractVendorableComponents only passes terraform and helmfile, this fallback may mask bugs. Either return an error or remove the default case entirely.

🔎 Suggested fix:
 	switch componentType {
 	case cfg.TerraformComponentType:
 		componentBasePath = atmosConfig.Components.Terraform.BasePath
 	case cfg.HelmfileComponentType:
 		componentBasePath = atmosConfig.Components.Helmfile.BasePath
-	case cfg.PackerComponentType:
-		componentBasePath = atmosConfig.Components.Packer.BasePath
-	default:
-		componentBasePath = atmosConfig.Components.Terraform.BasePath
+	default:
+		return nil, skipped, fmt.Errorf("unsupported component type: %s", componentType)
 	}
pkg/vendor/model.go (1)

18-26: pkg package importing from internal breaks module boundaries.

The pkg/vendor package imports github.com/cloudposse/atmos/internal/exec. This creates a dependency from public API (pkg/) to implementation details (internal/). Consider:

  1. Moving the needed functions (ProcessOciImage, SanitizeFileName) to pkg/
  2. Using interfaces to invert the dependency

This isn't blocking but worth addressing to maintain clean architecture.

pkg/vendor/pattern.go (1)

158-175: Permissive directory traversal is intentional but could be documented better.

The unconditional return true at line 174 after the required dirs check seems redundant. If containsAny returns true, we already returned. If it returns false, we still return true. The comment helps, but consider simplifying:

🔎 Suggested simplification:
 func checkDoublestarPrefixPattern(dirPath, pattern string) bool {
 	dirParts := strings.Split(strings.TrimPrefix(dirPath, "/"), "/")
 	requiredDirs := extractRequiredDirs(pattern)
 
-	if len(requiredDirs) == 0 {
-		return false
-	}
-
-	// If any directory segment matches a required directory, include it.
-	if containsAny(dirParts, requiredDirs) {
-		return true
-	}
-
-	// We must traverse ALL directories to find required ones.
-	// Files will be filtered later, so empty directories might be created but that's OK.
-	return true
+	// For patterns like "**/foo/**", we must traverse all directories
+	// to potentially find "foo". Files are filtered later.
+	// Only skip if we have specific required dirs and none match AND
+	// the directory can't possibly lead to them (not implemented - would require path prefix analysis).
+	return len(requiredDirs) == 0 || containsAny(dirParts, requiredDirs) || true
 }

Actually, if the intent is to always return true for **/ patterns (to traverse everything), the function could just return true with a clear comment. The current logic is effectively: return true unless requiredDirs is empty.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • 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 cc37a97 and 7fdae14.

📒 Files selected for processing (7)
  • cmd/vendor/pull.go (1 hunks)
  • pkg/vendor/config.go (1 hunks)
  • pkg/vendor/model.go (13 hunks)
  • pkg/vendor/pattern.go (1 hunks)
  • pkg/vendor/stack.go (1 hunks)
  • pkg/vendor/uri.go (2 hunks)
  • website/blog/2025-01-17-vendor-stack-flag.mdx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • cmd/vendor/pull.go
🧰 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 in CLI commands
Use interfaces for external dependencies to facilitate mocking and consider using testify/mock for creating mock implementations
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), and consider using custom error types for domain-specific errors
Follow standard Go coding style: use gofmt and goimports to format code, prefer short descriptive variable names, use kebab-case for command-line flags, and snake_case for environment variables
Document all exported functions, types, and methods following Go's documentation conventions
Document complex logic with inline comments in Go code
Support configuration via files, environment variables, and flags following the precedence order: flags > environment variables > config file > defaults
Provide clear error messages to users, include troubleshooting hints when appropriate, and log detailed errors for debugging

**/*.go: NEVER use fmt.Fprintf(os.Stdout/Stderr) or fmt.Println(); use data.* or ui.* functions instead
All comments must end with periods (enforced by godot linter)
Organize imports in three groups separated by blank lines, sorted alphabetically: 1) Go stdlib, 2) 3rd-party (NOT cloudposse/atmos), 3) Atmos packages; maintain aliases: cfg, log, u, errUtils
Add defer perf.Track(atmosConfig, "pkg.FuncName")() + blank line to all public functions for performance tracking; use nil if no atmosConfig param
All errors MUST be wrapped using static errors defined in errors/errors.go; use errors.Join for combining multiple errors; use fmt.Errorf with %w for adding string context; use error builder for complex errors; use errors.Is() for error checking; NEVER use dynamic errors directly
Use go.uber.org/mock/mockgen with //go:generate directives for mock generation; never create manual mocks
Keep files small...

Files:

  • pkg/vendor/uri.go
  • pkg/vendor/stack.go
  • pkg/vendor/model.go
  • pkg/vendor/pattern.go
  • pkg/vendor/config.go
website/**

📄 CodeRabbit inference engine (.cursor/rules/atmos-rules.mdc)

website/**: Update website documentation in the website/ directory when adding new features, ensure consistency between CLI help text and website documentation, and follow the website's documentation structure and style
Keep website code in the website/ directory, follow the existing website architecture and style, and test website changes locally before committing
Keep CLI documentation and website documentation in sync and document new features on the website with examples and use cases

Files:

  • website/blog/2025-01-17-vendor-stack-flag.mdx
website/blog/**/*.mdx

📄 CodeRabbit inference engine (CLAUDE.md)

website/blog/**/*.mdx: Follow PR template (what/why/references); PRs labeled minor/major MUST include blog post at website/blog/YYYY-MM-DD-feature-name.mdx with YAML front matter, after intro, and only tags from website/blog/tags.yml
Blog posts MUST use only tags defined in website/blog/tags.yml and authors defined in website/blog/authors.yml; valid tags are: feature, enhancement, bugfix, dx, breaking-change, security, documentation, deprecation, core; never invent new tags

Files:

  • website/blog/2025-01-17-vendor-stack-flag.mdx
🧠 Learnings (45)
📓 Common learnings
Learnt from: osterman
Repo: cloudposse/atmos PR: 1686
File: errors/errors.go:184-203
Timestamp: 2025-12-13T06:10:13.688Z
Learning: cloudposse/atmos: For toolchain work, duplicate/unused error sentinels in errors/errors.go should be cleaned up in a separate refactor PR and not block feature PRs; canonical toolchain sentinels live under toolchain/registry with re-exports in toolchain/errors.go.
Learnt from: osterman
Repo: cloudposse/atmos PR: 1686
File: docs/prd/tool-dependencies-integration.md:58-64
Timestamp: 2025-12-13T06:07:37.766Z
Learning: cloudposse/atmos: For PRD docs (docs/prd/*.md), markdownlint issues like MD040/MD010/MD034 can be handled in a separate documentation cleanup commit and should not block the current PR.
Learnt from: aknysh
Repo: cloudposse/atmos PR: 768
File: internal/exec/vendor_model_component.go:3-20
Timestamp: 2024-11-18T13:59:10.824Z
Learning: When replacing significant dependencies like `go-getter` that require extensive changes, prefer to address them in separate PRs.
Learnt from: Listener430
Repo: cloudposse/atmos PR: 934
File: tests/fixtures/scenarios/docs-generate/README.md.gotmpl:99-118
Timestamp: 2025-01-25T03:51:57.689Z
Learning: For the cloudposse/atmos repository, changes to template contents should be handled in dedicated PRs and are typically considered out of scope for PRs focused on other objectives.
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: .cursor/rules/atmos-rules.mdc:0-0
Timestamp: 2025-11-24T17:35:37.209Z
Learning: Applies to {go.mod,go.sum} : Manage dependencies with Go modules and keep dependencies up to date while minimizing external dependencies
📚 Learning: 2025-02-05T11:10:51.031Z
Learnt from: mss
Repo: cloudposse/atmos PR: 1024
File: internal/exec/go_getter_utils.go:31-33
Timestamp: 2025-02-05T11:10:51.031Z
Learning: The path traversal check in `ValidateURI` function in `internal/exec/go_getter_utils.go` is intentionally kept despite potentially blocking valid Git URLs, as this validation is planned to be addressed in a separate ticket.

Applied to files:

  • pkg/vendor/uri.go
📚 Learning: 2024-10-22T23:00:20.627Z
Learnt from: Cerebrovinny
Repo: cloudposse/atmos PR: 737
File: internal/exec/vendor_utils.go:131-141
Timestamp: 2024-10-22T23:00:20.627Z
Learning: In the `ReadAndProcessVendorConfigFile` function in `internal/exec/vendor_utils.go`, the existence of the vendor config file is already checked, so additional file existence checks may be unnecessary.

Applied to files:

  • pkg/vendor/uri.go
  • pkg/vendor/stack.go
  • pkg/vendor/model.go
  • website/blog/2025-01-17-vendor-stack-flag.mdx
  • pkg/vendor/config.go
📚 Learning: 2024-11-13T21:37:07.852Z
Learnt from: Cerebrovinny
Repo: cloudposse/atmos PR: 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.

Applied to files:

  • pkg/vendor/stack.go
📚 Learning: 2024-12-07T16:16:13.038Z
Learnt from: Listener430
Repo: cloudposse/atmos PR: 825
File: internal/exec/helmfile_generate_varfile.go:28-31
Timestamp: 2024-12-07T16:16:13.038Z
Learning: In `internal/exec/helmfile_generate_varfile.go`, the `--help` command (`./atmos helmfile generate varfile --help`) works correctly without requiring stack configurations, and the only change needed was to make `ProcessCommandLineArgs` exportable by capitalizing its name.

Applied to files:

  • pkg/vendor/stack.go
  • website/blog/2025-01-17-vendor-stack-flag.mdx
📚 Learning: 2025-07-05T20:59:02.914Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 1363
File: internal/exec/template_utils.go:18-18
Timestamp: 2025-07-05T20:59:02.914Z
Learning: In the Atmos project, gomplate v4 is imported with a blank import (`_ "github.com/hairyhenderson/gomplate/v4"`) alongside v3 imports to resolve AWS SDK version conflicts. V3 uses older AWS SDK versions that conflict with newer AWS modules used by Atmos. A full migration to v4 requires extensive refactoring due to API changes and should be handled in a separate PR.

Applied to files:

  • pkg/vendor/stack.go
  • pkg/vendor/model.go
  • pkg/vendor/config.go
📚 Learning: 2024-10-20T00:57:53.500Z
Learnt from: haitham911
Repo: cloudposse/atmos PR: 731
File: internal/exec/validate_stacks.go:0-0
Timestamp: 2024-10-20T00:57:53.500Z
Learning: In `internal/exec/validate_stacks.go`, when downloading the Atmos JSON Schema file to the temp directory, the temporary file is overwritten each time, so explicit removal is not necessary.

Applied to files:

  • pkg/vendor/stack.go
📚 Learning: 2025-12-16T18:20:55.630Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Applies to **/*.go : Organize imports in three groups separated by blank lines, sorted alphabetically: 1) Go stdlib, 2) 3rd-party (NOT cloudposse/atmos), 3) Atmos packages; maintain aliases: cfg, log, u, errUtils

Applied to files:

  • pkg/vendor/stack.go
  • pkg/vendor/model.go
  • pkg/vendor/config.go
📚 Learning: 2025-10-13T18:13:54.020Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 1622
File: pkg/perf/perf.go:140-184
Timestamp: 2025-10-13T18:13:54.020Z
Learning: In pkg/perf/perf.go, the `trackWithSimpleStack` function intentionally skips ownership checks at call stack depth > 1 to avoid expensive `getGoroutineID()` calls on every nested function. This is a performance optimization for the common single-goroutine execution case (most Atmos commands), accepting the rare edge case of potential metric corruption if multi-goroutine execution occurs at depth > 1. The ~19× performance improvement justifies this trade-off.

Applied to files:

  • pkg/vendor/stack.go
📚 Learning: 2024-11-19T23:00:45.899Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 795
File: internal/exec/stack_processor_utils.go:378-386
Timestamp: 2024-11-19T23:00:45.899Z
Learning: In the `ProcessYAMLConfigFile` function within `internal/exec/stack_processor_utils.go`, directory traversal in stack imports is acceptable and should not be restricted.

Applied to files:

  • pkg/vendor/stack.go
  • pkg/vendor/config.go
📚 Learning: 2025-01-09T22:27:25.538Z
Learnt from: samtholiya
Repo: cloudposse/atmos PR: 914
File: cmd/validate_stacks.go:20-23
Timestamp: 2025-01-09T22:27:25.538Z
Learning: The validate commands in Atmos can have different help handling implementations. Specifically, validate_component.go and validate_stacks.go are designed to handle help requests differently, with validate_stacks.go including positional argument checks while validate_component.go does not.

Applied to files:

  • pkg/vendor/stack.go
📚 Learning: 2025-12-16T18:20:55.630Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Applies to **/*.go : Add `defer perf.Track(atmosConfig, "pkg.FuncName")()` + blank line to all public functions for performance tracking; use nil if no atmosConfig param

Applied to files:

  • pkg/vendor/stack.go
  • pkg/vendor/model.go
  • pkg/vendor/config.go
📚 Learning: 2025-04-04T02:03:23.676Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 1185
File: internal/exec/yaml_func_store.go:26-26
Timestamp: 2025-04-04T02:03:23.676Z
Learning: The Atmos codebase currently uses `log.Fatal` for error handling in multiple places. The maintainers are aware this isn't an ideal pattern (should only be used in main() or init() functions) and plan to address it comprehensively in a separate PR. CodeRabbit should not flag these issues or push for immediate changes until that refactoring is complete.

Applied to files:

  • pkg/vendor/model.go
📚 Learning: 2025-11-24T17:35:37.209Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: .cursor/rules/atmos-rules.mdc:0-0
Timestamp: 2025-11-24T17:35:37.209Z
Learning: Applies to **/*.go : Provide clear error messages to users, include troubleshooting hints when appropriate, and log detailed errors for debugging

Applied to files:

  • pkg/vendor/model.go
  • pkg/vendor/config.go
📚 Learning: 2024-10-28T01:51:30.811Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 727
File: internal/exec/terraform_clean.go:329-332
Timestamp: 2024-10-28T01:51:30.811Z
Learning: In the Atmos Go code, when deleting directories or handling file paths (e.g., in `terraform_clean.go`), always resolve the absolute path using `filepath.Abs` and use the logger `u.LogWarning` for logging messages instead of using `fmt.Printf`.

Applied to files:

  • pkg/vendor/model.go
  • pkg/vendor/pattern.go
📚 Learning: 2025-02-21T20:56:20.761Z
Learnt from: samtholiya
Repo: cloudposse/atmos PR: 1077
File: pkg/downloader/custom_github_detector_test.go:0-0
Timestamp: 2025-02-21T20:56:20.761Z
Learning: The `github.com/charmbracelet/log` package should be imported with the alias `log`, not `clog`.

Applied to files:

  • pkg/vendor/model.go
📚 Learning: 2025-11-24T17:35:37.209Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: .cursor/rules/atmos-rules.mdc:0-0
Timestamp: 2025-11-24T17:35:37.209Z
Learning: Applies to **/*.go : Follow Go's error handling idioms: use meaningful error messages, wrap errors with context using `fmt.Errorf("context: %w", err)`, and consider using custom error types for domain-specific errors

Applied to files:

  • pkg/vendor/model.go
  • pkg/vendor/config.go
📚 Learning: 2025-04-04T02:03:21.906Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 1185
File: internal/exec/yaml_func_store.go:71-72
Timestamp: 2025-04-04T02:03:21.906Z
Learning: The codebase currently uses `log.Fatal` for error handling in library functions, which terminates the program. There is a plan to refactor this approach in a separate PR to improve API design by returning error messages instead of terminating execution.

Applied to files:

  • pkg/vendor/model.go
📚 Learning: 2025-02-19T05:50:35.853Z
Learnt from: samtholiya
Repo: cloudposse/atmos PR: 1068
File: tests/snapshots/TestCLICommands_atmos_terraform_apply_--help.stdout.golden:0-0
Timestamp: 2025-02-19T05:50:35.853Z
Learning: Backtick formatting should only be applied to flag descriptions in Go source files, not in golden test files (test snapshots) as they are meant to capture the raw command output.

Applied to files:

  • pkg/vendor/model.go
📚 Learning: 2025-09-10T22:38:42.212Z
Learnt from: Benbentwo
Repo: cloudposse/atmos PR: 1475
File: pkg/auth/identities/aws/user.go:141-145
Timestamp: 2025-09-10T22:38:42.212Z
Learning: The user confirmed that the errors package has an error string wrapping format, contradicting the previous learning about ErrWrappingFormat being invalid. The current usage of fmt.Errorf(errUtils.ErrWrappingFormat, errUtils.ErrAuthAwsFileManagerFailed, err) appears to be the correct pattern.

Applied to files:

  • pkg/vendor/model.go
  • pkg/vendor/config.go
📚 Learning: 2025-12-16T18:20:55.630Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Define interfaces for all major functionality and use dependency injection for testability; generate mocks with go.uber.org/mock/mockgen

Applied to files:

  • pkg/vendor/model.go
📚 Learning: 2025-09-13T18:06:07.674Z
Learnt from: samtholiya
Repo: cloudposse/atmos PR: 1466
File: toolchain/list.go:39-42
Timestamp: 2025-09-13T18:06:07.674Z
Learning: In the cloudposse/atmos repository, for UI messages in the toolchain package, use utils.PrintfMessageToTUI instead of log.Error or fmt.Fprintln(os.Stderr, ...). Import pkg/utils with alias "u" to follow the established pattern.

Applied to files:

  • pkg/vendor/model.go
📚 Learning: 2025-11-09T19:06:58.470Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1752
File: pkg/profile/list/formatter_table.go:27-29
Timestamp: 2025-11-09T19:06:58.470Z
Learning: In the cloudposse/atmos repository, performance tracking with `defer perf.Track()` is enforced on all functions via linting, including high-frequency utility functions, formatters, and renderers. This is a repository-wide policy to maintain consistency and avoid making case-by-case judgment calls about which functions should have profiling.

Applied to files:

  • pkg/vendor/model.go
📚 Learning: 2025-11-30T04:16:24.155Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 1821
File: pkg/merge/deferred.go:34-48
Timestamp: 2025-11-30T04:16:24.155Z
Learning: In the cloudposse/atmos repository, the `defer perf.Track()` guideline applies to functions that perform meaningful work (I/O, computation, external calls), but explicitly excludes trivial accessors/mutators (e.g., simple getters, setters with single integer increments, string joins, or map appends) where the tracking overhead would exceed the actual method cost and provide no actionable performance data. Hot-path methods called in tight loops should especially avoid perf.Track() if they perform only trivial operations.

Applied to files:

  • pkg/vendor/model.go
📚 Learning: 2025-10-02T19:17:51.630Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1504
File: pkg/profiler/profiler.go:20-31
Timestamp: 2025-10-02T19:17:51.630Z
Learning: In pkg/profiler/profiler.go, profiler-specific errors (ErrUnsupportedProfileType, ErrStartCPUProfile, ErrStartTraceProfile, ErrCreateProfileFile) must remain local and cannot be moved to errors/errors.go due to an import cycle: pkg/profiler → errors → pkg/schema → pkg/profiler. This is a valid exception to the centralized errors policy.

Applied to files:

  • pkg/vendor/model.go
📚 Learning: 2025-12-16T18:20:55.630Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Applies to **/*.go : Use colors from pkg/ui/theme/colors.go for all UI output

Applied to files:

  • pkg/vendor/model.go
📚 Learning: 2024-12-02T21:26:32.337Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 808
File: pkg/config/config.go:478-483
Timestamp: 2024-12-02T21:26:32.337Z
Learning: In the 'atmos' project, when reviewing Go code like `pkg/config/config.go`, avoid suggesting file size checks after downloading remote configs if such checks aren't implemented elsewhere in the codebase.

Applied to files:

  • pkg/vendor/model.go
  • pkg/vendor/config.go
📚 Learning: 2025-04-10T21:33:06.447Z
Learnt from: samtholiya
Repo: cloudposse/atmos PR: 1147
File: pkg/filematch/filematch.go:32-32
Timestamp: 2025-04-10T21:33:06.447Z
Learning: The replacement of "*/*" with an empty string in recursive glob patterns (containing "**") is intentional behavior in the filematch package, as confirmed by the developer. This is necessary for proper handling of recursive directory matching when using the gobwas/glob library.

Applied to files:

  • pkg/vendor/pattern.go
📚 Learning: 2025-04-10T21:33:06.447Z
Learnt from: samtholiya
Repo: cloudposse/atmos PR: 1147
File: pkg/filematch/filematch.go:32-32
Timestamp: 2025-04-10T21:33:06.447Z
Learning: The replacement of "*/*" with an empty string in recursive glob patterns (containing "**") is intentional behavior in the filematch package, as confirmed by the developer.

Applied to files:

  • pkg/vendor/pattern.go
📚 Learning: 2025-09-13T16:39:20.007Z
Learnt from: samtholiya
Repo: cloudposse/atmos PR: 1466
File: cmd/markdown/atmos_toolchain_aliases.md:2-4
Timestamp: 2025-09-13T16:39:20.007Z
Learning: In the cloudposse/atmos repository, CLI documentation files in cmd/markdown/ follow a specific format that uses " $ atmos command" (with leading space and dollar sign prompt) in code blocks. This is the established project convention and should not be changed to comply with standard markdownlint rules MD040 and MD014.

Applied to files:

  • website/blog/2025-01-17-vendor-stack-flag.mdx
📚 Learning: 2025-12-13T06:07:37.766Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1686
File: docs/prd/tool-dependencies-integration.md:58-64
Timestamp: 2025-12-13T06:07:37.766Z
Learning: cloudposse/atmos: For PRD docs (docs/prd/*.md), markdownlint issues like MD040/MD010/MD034 can be handled in a separate documentation cleanup commit and should not block the current PR.

Applied to files:

  • website/blog/2025-01-17-vendor-stack-flag.mdx
📚 Learning: 2025-12-16T18:20:55.630Z
Learnt from: CR
Repo: cloudposse/atmos PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-16T18:20:55.630Z
Learning: Applies to website/docs/cli/commands/**/*.mdx : All CLI commands/flags need Docusaurus documentation in website/docs/cli/commands/ with specific structure: frontmatter, Intro component, Screengrab component, Usage section, Arguments/Flags using <dl><dt>/<dd>, and Examples section

Applied to files:

  • website/blog/2025-01-17-vendor-stack-flag.mdx
📚 Learning: 2025-01-19T15:49:15.593Z
Learnt from: samtholiya
Repo: cloudposse/atmos PR: 955
File: tests/snapshots/TestCLICommands_atmos_validate_editorconfig_--help.stdout.golden:0-0
Timestamp: 2025-01-19T15:49:15.593Z
Learning: In future commits, the help text for Atmos CLI commands should be limited to only show component and stack parameters for commands that actually use them. This applies to the example usage section in command help text.

Applied to files:

  • website/blog/2025-01-17-vendor-stack-flag.mdx
📚 Learning: 2025-10-10T23:51:36.597Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1599
File: internal/exec/terraform.go:394-402
Timestamp: 2025-10-10T23:51:36.597Z
Learning: In Atmos (internal/exec/terraform.go), when adding OpenTofu-specific flags like `--var-file` for `init`, do not gate them based on command name (e.g., checking if `info.Command == "tofu"` or `info.Command == "opentofu"`) because command names don't reliably indicate the actual binary being executed (symlinks, aliases). Instead, document the OpenTofu requirement in code comments and documentation, trusting users who enable the feature (e.g., `PassVars`) to ensure their terraform command points to an OpenTofu binary.

Applied to files:

  • website/blog/2025-01-17-vendor-stack-flag.mdx
📚 Learning: 2024-11-12T13:06:56.194Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 768
File: website/docs/cheatsheets/vendoring.mdx:70-70
Timestamp: 2024-11-12T13:06:56.194Z
Learning: In `atmos vendor pull --everything`, the `--everything` flag uses the TTY for TUI but is not interactive.

Applied to files:

  • website/blog/2025-01-17-vendor-stack-flag.mdx
📚 Learning: 2025-11-08T19:56:18.660Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1697
File: internal/exec/oci_utils.go:0-0
Timestamp: 2025-11-08T19:56:18.660Z
Learning: In the Atmos codebase, when a function receives an `*schema.AtmosConfiguration` parameter, it should read configuration values from `atmosConfig.Settings` fields rather than using direct `os.Getenv()` or `viper.GetString()` calls. The Atmos pattern is: viper.BindEnv in cmd/root.go binds environment variables → Viper unmarshals into atmosConfig.Settings via mapstructure → business logic reads from the Settings struct. This provides centralized config management, respects precedence, and enables testability. Example: `atmosConfig.Settings.AtmosGithubToken` instead of `os.Getenv("ATMOS_GITHUB_TOKEN")` in functions like `getGHCRAuth` in internal/exec/oci_utils.go.

Applied to files:

  • pkg/vendor/config.go
📚 Learning: 2024-10-23T21:36:40.262Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 740
File: cmd/cmd_utils.go:340-359
Timestamp: 2024-10-23T21:36:40.262Z
Learning: In the Go codebase for Atmos, when reviewing functions like `checkAtmosConfig` in `cmd/cmd_utils.go`, avoid suggesting refactoring to return errors instead of calling `os.Exit` if such changes would significantly increase the scope due to the need to update multiple call sites.

Applied to files:

  • pkg/vendor/config.go
📚 Learning: 2025-04-11T22:06:46.999Z
Learnt from: samtholiya
Repo: cloudposse/atmos PR: 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.

Applied to files:

  • pkg/vendor/config.go
📚 Learning: 2025-09-10T22:38:42.212Z
Learnt from: Benbentwo
Repo: cloudposse/atmos PR: 1475
File: pkg/auth/identities/aws/user.go:141-145
Timestamp: 2025-09-10T22:38:42.212Z
Learning: ErrWrappingFormat is correctly defined as "%w: %w" in the errors package and is used throughout the codebase to wrap two error types together. The usage fmt.Errorf(errUtils.ErrWrappingFormat, errUtils.ErrAuthAwsFileManagerFailed, err) is the correct pattern when both arguments are error types.

Applied to files:

  • pkg/vendor/config.go
📚 Learning: 2025-01-07T20:38:09.618Z
Learnt from: samtholiya
Repo: cloudposse/atmos PR: 896
File: cmd/editor_config.go:37-40
Timestamp: 2025-01-07T20:38:09.618Z
Learning: Error handling suggestion for `cmd.Help()` in `cmd/editor_config.go` was deferred as the code is planned for future modifications.

Applied to files:

  • pkg/vendor/config.go
📚 Learning: 2025-02-03T06:00:11.419Z
Learnt from: samtholiya
Repo: cloudposse/atmos PR: 959
File: cmd/describe_config.go:20-20
Timestamp: 2025-02-03T06:00:11.419Z
Learning: The `describe config` command should use `PrintErrorMarkdownAndExit` with empty title and suggestion for consistency with other commands.

Applied to files:

  • pkg/vendor/config.go
📚 Learning: 2025-10-22T14:55:44.014Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1695
File: pkg/auth/manager.go:169-171
Timestamp: 2025-10-22T14:55:44.014Z
Learning: Go 1.20+ supports multiple %w verbs in fmt.Errorf, which returns an error implementing Unwrap() []error. This is valid and does not panic. Atmos uses Go 1.24.8 and configures errorlint with errorf-multi: true to validate this pattern.

Applied to files:

  • pkg/vendor/config.go
📚 Learning: 2024-11-30T22:07:08.610Z
Learnt from: aknysh
Repo: cloudposse/atmos PR: 810
File: internal/exec/yaml_func_terraform_output.go:35-40
Timestamp: 2024-11-30T22:07:08.610Z
Learning: In the Go function `processTagTerraformOutput` in `internal/exec/yaml_func_terraform_output.go`, parameters cannot contain spaces. The code splits the input by spaces, and if the parameters contain spaces, `len(parts) != 3` will fail and show an error to the user.

Applied to files:

  • pkg/vendor/config.go
📚 Learning: 2024-12-12T15:17:45.245Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 808
File: examples/demo-atmos.d/atmos.d/tools/helmfile.yml:10-10
Timestamp: 2024-12-12T15:17:45.245Z
Learning: In `examples/demo-atmos.d/atmos.d/tools/helmfile.yml`, when suggesting changes to `kubeconfig_path`, ensure that the values use valid Go template syntax.

Applied to files:

  • pkg/vendor/config.go
📚 Learning: 2025-12-13T03:21:35.786Z
Learnt from: osterman
Repo: cloudposse/atmos PR: 1813
File: cmd/terraform/shell.go:28-73
Timestamp: 2025-12-13T03:21:35.786Z
Learning: In Atmos, when calling cfg.InitCliConfig, you must first populate the schema.ConfigAndStacksInfo struct with global flag values using flags.ParseGlobalFlags(cmd, v) rather than passing an empty struct. The LoadConfig function (pkg/config/load.go) reads config selection fields (AtmosConfigFilesFromArg, AtmosConfigDirsFromArg, BasePath, ProfilesFromArg) directly from the ConfigAndStacksInfo struct, NOT from Viper. Passing an empty struct causes config selection flags (--base-path, --config, --config-path, --profile) to be silently ignored. Correct pattern: parse flags → populate struct → call InitCliConfig. See cmd/terraform/plan_diff.go for reference implementation.

Applied to files:

  • pkg/vendor/config.go
🧬 Code graph analysis (3)
pkg/vendor/stack.go (6)
pkg/perf/perf.go (1)
  • Track (121-138)
errors/errors.go (1)
  • ErrStackNotFound (467-467)
pkg/config/const.go (4)
  • TerraformComponentType (57-57)
  • HelmfileComponentType (58-58)
  • PackerComponentType (59-59)
  • ComponentVendorConfigFileName (61-61)
pkg/logger/log.go (1)
  • Debug (24-26)
pkg/utils/yaml_utils.go (1)
  • UnmarshalYAML (687-689)
pkg/schema/vendor_component.go (2)
  • VendorComponentConfig (30-35)
  • VendorComponentSpec (20-23)
pkg/vendor/pattern.go (2)
pkg/logger/log.go (1)
  • Debug (24-26)
pkg/utils/glob_utils.go (1)
  • PathMatch (74-106)
pkg/vendor/config.go (7)
pkg/logger/log.go (1)
  • Debug (24-26)
pkg/vendor/errors.go (15)
  • ErrVendoringNotConfigured (25-25)
  • ErrPermissionDenied (26-26)
  • ErrNoYAMLConfigFiles (29-29)
  • ErrDuplicateComponentsFound (32-32)
  • ErrDuplicateImport (31-31)
  • ErrVendorConfigSelfImport (21-21)
  • ErrMissingVendorConfigDefinition (22-22)
  • ErrSourceMissing (19-19)
  • ErrTargetsMissing (20-20)
  • ErrNoComponentsWithTags (28-28)
  • ErrDuplicateComponents (30-30)
  • ErrComponentNotDefined (33-33)
  • ErrFolderNotFound (44-44)
  • ErrInvalidComponentKind (45-45)
  • ErrComponentConfigFileNotFound (43-43)
pkg/utils/slice_utils.go (1)
  • SliceContainsString (12-21)
pkg/schema/vendor_component.go (1)
  • VendorComponentConfig (30-35)
pkg/config/const.go (4)
  • TerraformComponentType (57-57)
  • HelmfileComponentType (58-58)
  • PackerComponentType (59-59)
  • ComponentVendorConfigFileName (61-61)
pkg/utils/file_utils.go (2)
  • IsDirectory (20-28)
  • FileExists (31-39)
pkg/utils/yaml_utils.go (1)
  • UnmarshalYAML (687-689)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Acceptance Tests (windows)
  • GitHub Check: Summary
🔇 Additional comments (14)
pkg/vendor/uri.go (3)

1-10: Imports and package structure look good.

Clean organization with stdlib first, 3rd-party next, and Atmos packages last. The logger alias log follows project conventions.


274-299: Normalization logic is sound.

The function correctly prioritizes special URI handling before applying triple-slash normalization and double-slash-dot appending. The early returns for file/OCI/S3/local/archive URIs prevent unintended modifications.


301-330: Triple-slash normalization handles edge cases well.

Good use of go-getter's SourceDirSubdir for robust parsing. The separation of query parameters before reconstruction prevents malformed URIs.

pkg/vendor/config.go (2)

107-144: Merge logic is solid.

Good use of maps for O(1) duplicate detection. The error messages include the config file path for easier debugging.


146-192: Recursive import handling is well-designed.

The chain-based duplicate detection prevents import cycles. Setting File on each source provides good traceability for debugging vendor issues.

pkg/vendor/stack.go (3)

1-16: Imports are well-organized.

Three groups (stdlib, 3rd-party, Atmos) with correct aliases. Good to see perf included for tracking.


18-65: Stack vendoring orchestration looks correct.

Good flow: describe stacks → extract vendorable components → execute vendor model. The empty stack handling with skipped count logging is helpful for debugging.


221-273: Package creation logic is clean.

Good separation between component and mixin package creation. The early return for empty URI prevents unnecessary processing.

pkg/vendor/model.go (3)

56-85: TUI model execution is well-structured.

Good TTY detection fallback, clear error propagation, and the failed package count check ensures proper exit status.


456-511: Component installation logic is correct.

Clean separation between dry-run and real installation paths. The fallback error for unknown package types provides a safety net.


532-575: Skip function generation is well-organized.

Good structure: .git exclusion first, then include/exclude pattern processing. The comment at lines 545-549 clearly explains the logic.

pkg/vendor/pattern.go (3)

1-19: Clean constants and imports.

Good practice defining log keys as constants for consistency across debug messages.


21-29: Clean routing between file and directory handling.

Simple dispatch pattern that keeps the complexity in the specialized functions.


309-316: File detection heuristic may have edge cases.

The dot-based file detection (!strings.Contains(part, ".")) would exclude legitimate directory names containing dots (e.g., v1.2.3). For pattern extraction purposes this is likely acceptable since such directories would still be traversed via the permissive checkDoublestarPrefixPattern.

Consider adding a test case for directory names with dots to confirm expected behavior.

Comment thread pkg/vendor/config.go Outdated
Comment thread pkg/vendor/config.go Outdated
Comment thread website/blog/2025-12-18-vendor-stack-flag.mdx

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pkg/vendor/component.go (1)

344-346: Capitalize error message.

🔎 Suggested fix:
-	if err := cp.Copy(tempDir, p.componentPath, copyOptions); err != nil {
-		return fmt.Errorf("Failed to copy package %s error %w", p.name, err)
+	if err := cp.Copy(tempDir, p.componentPath, copyOptions); err != nil {
+		return fmt.Errorf("failed to copy package %s: %w", p.name, err)
 	}
♻️ Duplicate comments (2)
pkg/vendor/config.go (2)

28-58: Missing perf.Track on public function.

Per coding guidelines, public functions need performance tracking.

🔎 Suggested fix:
+import (
+	...
+	"github.com/cloudposse/atmos/pkg/perf"
+	...
+)

 func ReadAndProcessVendorConfigFile(
 	atmosConfig *schema.AtmosConfiguration,
 	vendorConfigFile string,
 	checkGlobalConfig bool,
 ) (VendorConfigResult, error) {
+	defer perf.Track(atmosConfig, "vendor.ReadAndProcessVendorConfigFile")()
+
 	result := VendorConfigResult{}

300-333: Missing perf.Track on public function.

Same issue as ReadAndProcessVendorConfigFile.

🔎 Suggested fix:
 func ReadAndProcessComponentVendorConfigFile(
 	atmosConfig *schema.AtmosConfiguration,
 	component string,
 	componentType string,
 ) (schema.VendorComponentConfig, string, error) {
+	defer perf.Track(atmosConfig, "vendor.ReadAndProcessComponentVendorConfigFile")()
+
 	var componentConfig schema.VendorComponentConfig
🧹 Nitpick comments (13)
pkg/vender/vendor_config_test.go (1)

32-33: Consider using require.NoError for error assertions.

When an error would invalidate subsequent assertions, require.NoError fails fast and provides clearer test output. Currently using assert.Nil(t, err) in multiple places.

🔎 Suggested pattern:
-err := os.MkdirAll(componentPath, 0o755)
-assert.Nil(t, err)
+err := os.MkdirAll(componentPath, 0o755)
+require.NoError(t, err)

Also applies to: 51-52, 88-89, 130-131

pkg/vendor/model.go (2)

60-73: Missing perf.Track on public function.

Per coding guidelines, public functions should include performance tracking. This function orchestrates the TUI model execution.

🔎 Add performance tracking:
 func executeVendorModel[T pkgComponentVendor | pkgAtmosVendor](
 	packages []T,
 	dryRun bool,
 	atmosConfig *schema.AtmosConfiguration,
 ) error {
+	defer perf.Track(atmosConfig, "vendor.executeVendorModel")()
+
 	if len(packages) == 0 {
 		return nil
 	}

97-147: Missing perf.Track on public function newModelVendor.

Same guideline applies here. Add tracking at function entry.

🔎 Add performance tracking:
 func newModelVendor[T pkgComponentVendor | pkgAtmosVendor](
 	pkgs []T,
 	dryRun bool,
 	atmosConfig *schema.AtmosConfiguration,
 ) (modelVendor, error) {
+	defer perf.Track(atmosConfig, "vendor.newModelVendor")()
+
 	p := progress.New(
pkg/vendor/vendor_test.go (1)

444-489: Consider removing unused local file setup.

Lines 448-451 create a local file that isn't used by any test case. The localFile variable is written but never referenced in the test table.

🔎 Remove unused file creation:
 func TestDetermineSourceType_Extended(t *testing.T) {
 	// Additional test cases for determineSourceType.
-	tempDir := t.TempDir()
-
-	// Create a local file for testing.
-	localFile := tempDir + "/local.tf"
-	err := os.WriteFile(localFile, []byte("# test"), 0o644)
-	assert.NoError(t, err)
-
 	tests := []struct {
pkg/vendor/model_test.go (2)

279-301: This test is tautological and doesn't test actual behavior.

Lines 289-300 just verify string comparisons (key == "ctrl+c") rather than testing handleKeyPress. The actual behavior test is at lines 655-697 (TestHandleKeyPress_QuitKeys), making this test redundant.

🔎 Consider removing or refactoring:
-func TestModelVendor_HandleKeyPress(t *testing.T) {
-	model := &modelVendor{
-		packages: []pkgVendor{{name: "test"}},
-	}
-
-	// Verify model is properly initialized for key handling.
-	assert.NotNil(t, model)
-	assert.Equal(t, "test", model.packages[0].name)
-
-	// Test quit key detection logic (the handleKeyPress function checks these keys).
-	quitKeys := []string{"ctrl+c", "esc", "q"}
-	for _, key := range quitKeys {
-		isQuitKey := key == "ctrl+c" || key == "esc" || key == "q"
-		assert.True(t, isQuitKey, "Key %s should be a quit key", key)
-	}
-
-	// Non-quit keys should not trigger quit.
-	nonQuitKeys := []string{"x", "a", "enter", "space"}
-	for _, key := range nonQuitKeys {
-		isQuitKey := key == "ctrl+c" || key == "esc" || key == "q"
-		assert.False(t, isQuitKey, "Key %s should not be a quit key", key)
-	}
-}

261-267: Consider refactoring to use Go's builtin max function.

Go 1.21 and later versions include a builtin max function, and the project uses Go 1.25.2. While the custom max(a, b int) in pkg/vendor/model.go is actively used and functioning correctly, migrating to the builtin would reduce maintenance overhead and align with standard Go practices.

pkg/vendor/stack_test.go (1)

104-230: Component extraction tests look good, but setupComponent field is unused.

The setupComponent field in the test struct (line 141) is never checked or used in the test logic. Consider removing it or implementing the conditional setup.

🔎 Either use or remove the field:
 	tests := []struct {
 		name           string
 		stacksMap      map[string]any
 		expectedCount  int
 		expectedSkip   int
 		expectError    bool
-		setupComponent bool
 	}{
pkg/vendor/stack.go (2)

1-16: Import organization issue.

Per coding guidelines, imports should be in three groups: stdlib, 3rd-party, Atmos packages. Here, errors (Atmos package) is mixed before 3rd-party imports would normally go. Since there are no 3rd-party imports, it's acceptable, but the order within Atmos packages should be alphabetical.

🔎 Alphabetize Atmos imports:
 import (
 	"fmt"
 	"os"
 	"path/filepath"
 	"strings"

-	"github.com/cloudposse/atmos/errors"
-	"github.com/cloudposse/atmos/internal/exec"
 	cfg "github.com/cloudposse/atmos/pkg/config"
+	"github.com/cloudposse/atmos/errors"
+	"github.com/cloudposse/atmos/internal/exec"
 	log "github.com/cloudposse/atmos/pkg/logger"
 	"github.com/cloudposse/atmos/pkg/perf"
 	"github.com/cloudposse/atmos/pkg/schema"
 	u "github.com/cloudposse/atmos/pkg/utils"
 )

193-265: Implementation is clean with one note.

createComponentPackages has unused parameters (_ *schema.AtmosConfiguration, _ string for componentType). If these aren't needed for future expansion, consider removing them to simplify the signature.

pkg/vendor/vendor.go (3)

110-112: Use available atmosConfig instead of nil for perf.Track.

params.atmosConfig is available but you're passing nil. This loses context for performance tracking.

🔎 Suggested fix:
 func executeAtmosVendorInternal(params *executeVendorOptions) error {
-	defer perf.Track(nil, "vendor.executeAtmosVendorInternal")()
+	defer perf.Track(params.atmosConfig, "vendor.executeAtmosVendorInternal")()

256-284: determineSourceType mutates the uri pointer in-place.

This side effect could be surprising. Consider returning the modified URI as part of the result struct instead. However, I see the pattern is intentional to avoid multiple returns.


313-335: HandleVendorConfigNotExist duplicates VendorComponent logic.

This function does the same thing as VendorComponent (lines 69-96). Consider consolidating to avoid drift.

🔎 Suggested refactor:
 func HandleVendorConfigNotExist(atmosConfig *schema.AtmosConfiguration, component, componentType string, dryRun bool) error {
 	if componentType == "" {
 		componentType = "terraform"
 	}
-
-	config, path, err := ReadAndProcessComponentVendorConfigFile(
-		atmosConfig,
-		component,
-		componentType,
-	)
-	if err != nil {
-		return err
-	}
-
-	return ExecuteComponentVendorInternal(
-		atmosConfig,
-		&config.Spec,
-		component,
-		path,
-		dryRun,
-	)
+	return VendorComponent(atmosConfig, component,
+		WithComponentDryRun(dryRun),
+		WithComponentComponentType(componentType),
+	)
 }
pkg/vendor/component.go (1)

294-299: Inconsistent temp directory creation.

installMixin uses os.MkdirTemp directly instead of createTempDir helper. This skips the permission hardening done in createTempDir.

🔎 Suggested fix:
 func installMixin(p *pkgComponentVendor, atmosConfig *schema.AtmosConfiguration) error {
-	tempDir, err := os.MkdirTemp("", "atmos-vendor-mixin")
+	tempDir, err := createTempDir()
 	if err != nil {
-		return fmt.Errorf("Failed to create temp directory %w", err)
+		return fmt.Errorf("failed to create temp directory: %w", err)
 	}

Comment thread pkg/vendor/component_test.go Outdated
Comment thread pkg/vendor/component.go Outdated
Comment thread pkg/vendor/component.go Outdated
Comment thread pkg/vendor/component.go Outdated
Comment thread pkg/vendor/vendor_test.go Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 5

♻️ Duplicate comments (2)
pkg/vendor/config.go (2)

21-58: Missing performance tracking on public function.

Per coding guidelines, ReadAndProcessVendorConfigFile should have defer perf.Track(atmosConfig, "vendor.ReadAndProcessVendorConfigFile")() at the start.

🔎 Suggested fix:
 func ReadAndProcessVendorConfigFile(
 	atmosConfig *schema.AtmosConfiguration,
 	vendorConfigFile string,
 	checkGlobalConfig bool,
 ) (VendorConfigResult, error) {
+	defer perf.Track(atmosConfig, "vendor.ReadAndProcessVendorConfigFile")()
+
 	result := VendorConfigResult{}

300-333: Missing performance tracking on public function.

Same as above - ReadAndProcessComponentVendorConfigFile needs defer perf.Track(atmosConfig, "vendor.ReadAndProcessComponentVendorConfigFile")().

🔎 Suggested fix:
 func ReadAndProcessComponentVendorConfigFile(
 	atmosConfig *schema.AtmosConfiguration,
 	component string,
 	componentType string,
 ) (schema.VendorComponentConfig, string, error) {
+	defer perf.Track(atmosConfig, "vendor.ReadAndProcessComponentVendorConfigFile")()
+
 	var componentConfig schema.VendorComponentConfig
🧹 Nitpick comments (8)
cmd/vendor/pull_test.go (1)

157-177: Consider consolidating edge case tests into the main table.

The TestParsePullOptions_EmptyTags and TestParsePullOptions_SingleTag tests could be incorporated as additional rows in the main table-driven test to reduce duplication.

🔎 Suggested consolidation:
 		{
 			name: "all options set",
 			viperSetup: func(v *viper.Viper) {
 				v.Set("component", "vpc")
 				v.Set("tags", "core,networking")
 				v.Set("dry-run", true)
 				v.Set("type", "terraform")
 			},
 			expectedOpts: &PullOptions{
 				Component:     "vpc",
 				Stack:         "",
 				Tags:          []string{"core", "networking"},
 				DryRun:        true,
 				Everything:    false,
 				ComponentType: "terraform",
 			},
 		},
+		{
+			name: "empty tags string yields nil",
+			viperSetup: func(v *viper.Viper) {
+				v.Set("tags", "")
+			},
+			expectedOpts: &PullOptions{
+				Tags: nil,
+			},
+		},
+		{
+			name: "single tag",
+			viperSetup: func(v *viper.Viper) {
+				v.Set("tags", "networking")
+			},
+			expectedOpts: &PullOptions{
+				Tags: []string{"networking"},
+			},
+		},
pkg/vendor/vendor_test.go (1)

444-489: Unused local file created in TestDetermineSourceType_Extended.

The localFile variable at line 449-451 is created but never used in the test cases. Consider removing it or adding a test case that uses it.

🔎 Either remove unused file or add a test case:
 func TestDetermineSourceType_Extended(t *testing.T) {
 	// Additional test cases for determineSourceType.
-	tempDir := t.TempDir()
-
-	// Create a local file for testing.
-	localFile := tempDir + "/local.tf"
-	err := os.WriteFile(localFile, []byte("# test"), 0o644)
-	assert.NoError(t, err)

 	tests := []struct {
pkg/vendor/stack.go (1)

126-138: Consider returning error for unknown component type instead of defaulting.

getComponentBasePath silently defaults to terraform for unknown types. This could mask configuration issues.

🔎 Alternative with explicit error handling:
 func getComponentBasePath(atmosConfig *schema.AtmosConfiguration, componentType string) string {
 	switch componentType {
 	case cfg.TerraformComponentType:
 		return atmosConfig.Components.Terraform.BasePath
 	case cfg.HelmfileComponentType:
 		return atmosConfig.Components.Helmfile.BasePath
 	case cfg.PackerComponentType:
 		return atmosConfig.Components.Packer.BasePath
 	default:
+		log.Debug("Unknown component type, defaulting to terraform", "type", componentType)
 		return atmosConfig.Components.Terraform.BasePath
 	}
 }

At minimum, a debug log would help troubleshooting.

pkg/vendor/config_test.go (1)

279-301: Consider testing actual handleKeyPress behavior instead of duplicating logic.

The test at lines 289-300 manually checks if keys match quit conditions rather than invoking handleKeyPress. This tests string comparison logic you wrote in the test, not the actual function behavior.

🔎 Consider this approach:
 func TestModelVendor_HandleKeyPress(t *testing.T) {
 	model := &modelVendor{
 		packages: []pkgVendor{{name: "test"}},
 	}
 
-	// Verify model is properly initialized for key handling.
-	assert.NotNil(t, model)
-	assert.Equal(t, "test", model.packages[0].name)
-
-	// Test quit key detection logic (the handleKeyPress function checks these keys).
-	quitKeys := []string{"ctrl+c", "esc", "q"}
-	for _, key := range quitKeys {
-		isQuitKey := key == "ctrl+c" || key == "esc" || key == "q"
-		assert.True(t, isQuitKey, "Key %s should be a quit key", key)
-	}
-
-	// Non-quit keys should not trigger quit.
-	nonQuitKeys := []string{"x", "a", "enter", "space"}
-	for _, key := range nonQuitKeys {
-		isQuitKey := key == "ctrl+c" || key == "esc" || key == "q"
-		assert.False(t, isQuitKey, "Key %s should not be a quit key", key)
-	}
+	// Test quit keys return a command
+	cmd := model.handleKeyPress(tea.KeyMsg{Type: tea.KeyCtrlC})
+	assert.NotNil(t, cmd, "ctrl+c should return quit command")
+
+	// Test non-quit keys return nil
+	cmd = model.handleKeyPress(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("x")})
+	assert.Nil(t, cmd, "x should not return a command")
 }

Note: TestHandleKeyPress_QuitKeys at line 655 already covers this properly, so this test could be removed or simplified.

pkg/vendor/model.go (1)

295-300: Custom max function may be redundant with Go 1.21+.

Go 1.21 introduced builtin max. If Go 1.21+ is required, this can be removed. If supporting older versions, add a build constraint or comment.

🔎 If Go 1.21+ is required:
-func max(a, b int) int {
-	if a > b {
-		return a
-	}
-	return b
-}

The builtin max handles this automatically.

pkg/vendor/component.go (1)

44-49: Consider returning error from removeTempDir for critical cleanup.

Currently logs a warning on failure. For temp directories with potentially sensitive data, consider if callers need to know cleanup failed.

The current approach is acceptable for non-sensitive temp data.

pkg/vendor/vendor.go (2)

3-17: Import organization needs adjustment.

Per guidelines, imports should be: 1) stdlib, 2) 3rd-party, 3) Atmos packages. The internal/exec import at line 16 is an Atmos package but placed after the other Atmos packages.

🔎 Reorder imports:
 import (
 	"fmt"
 	"net/url"
 	"path/filepath"
 	"strings"
 
 	"github.com/samber/lo"
 
+	"github.com/cloudposse/atmos/internal/exec"
 	cfg "github.com/cloudposse/atmos/pkg/config"
 	"github.com/cloudposse/atmos/pkg/perf"
 	"github.com/cloudposse/atmos/pkg/schema"
 	u "github.com/cloudposse/atmos/pkg/utils"
-
-	"github.com/cloudposse/atmos/internal/exec"
 )

110-157: Consider using params.atmosConfig in perf.Track.

Line 112 passes nil to perf.Track. Since params.atmosConfig is available, using it would provide better context for profiling.

🔎 Use atmosConfig for tracking:
 func executeAtmosVendorInternal(params *executeVendorOptions) error {
-	defer perf.Track(nil, "vendor.executeAtmosVendorInternal")()
+	defer perf.Track(params.atmosConfig, "vendor.executeAtmosVendorInternal")()

Comment thread pkg/vendor/component_test.go Outdated
Comment thread pkg/vendor/component_test.go Outdated
Comment thread pkg/vendor/component.go Outdated
Comment thread pkg/vendor/component.go Outdated
Comment thread pkg/vendor/vendor_test.go Outdated
@mergify

mergify Bot commented Dec 19, 2025

Copy link
Copy Markdown

💥 This pull request now has conflicts. Could you fix it Erik Osterman (Cloud Posse) (@osterman)? 🙏

…olute path

The shouldExcludeFile function was matching exclude patterns against the
full absolute path (src), causing simple patterns like "README.md" to
fail. Now matches against trimmedSrc (relative path), consistent with
the fix applied to checkComponentExcludes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Strings.Split preserves surrounding spaces, so --tags "networking, database"
produced " database" which wouldn't match configured tag names. Trim each
entry and skip empty strings, consistent with cmd/terraform/generate/files.go.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@mergify

mergify Bot commented Mar 13, 2026

Copy link
Copy Markdown

💥 This pull request now has conflicts. Could you fix it Erik Osterman (Cloud Posse) (@osterman)? 🙏

@mergify mergify Bot added the conflict This PR has conflicts label Mar 13, 2026
@mergify mergify Bot removed the conflict This PR has conflicts label Jun 8, 2026
@mergify

mergify Bot commented Jun 8, 2026

Copy link
Copy Markdown

💥 This pull request now has conflicts. Could you fix it Erik Osterman (Cloud Posse) (@osterman)? 🙏

@mergify mergify Bot added the conflict This PR has conflicts label Jun 8, 2026
@atmos-pro

atmos-pro Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Tip

Atmos Pro  

No affected stacks workflow was detected for this pull request.
If this is expected, no action is needed.
Learn More. Ask AI.

@github-actions github-actions Bot added size/xxl and removed size/xl Extra large size PR labels Jul 21, 2026
@mergify mergify Bot removed the conflict This PR has conflicts label Jul 21, 2026
@mergify

mergify Bot commented Jul 21, 2026

Copy link
Copy Markdown

Warning

This PR exceeds the recommended limit of 10,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.

@github-actions

Copy link
Copy Markdown

Resource Changes Found for bucket in test

Atmos CI

create

Plan: 4 to add, 0 to change, 0 to destroy.
To reproduce this locally, run:

atmos terraform plan bucket -s test

Create

+ aws_s3_bucket.checkov_target
+ aws_s3_bucket.this
+ aws_s3_bucket.trivy_target
+ aws_s3_bucket_public_access_block.trivy_target
Terraform Plan Summary
  # aws_s3_bucket.checkov_target will be created
  + resource "aws_s3_bucket" "checkov_target" {
      + acceleration_status         = (known after apply)
      + acl                         = (known after apply)
      + arn                         = (known after apply)
      + bucket                      = "atmos-native-ci-e2e-checkov-test"
      + bucket_domain_name          = (known after apply)
      + bucket_prefix               = (known after apply)
      + bucket_regional_domain_name = (known after apply)
      + force_destroy               = false
      + hosted_zone_id              = (known after apply)
      + id                          = (known after apply)
      + object_lock_enabled         = (known after apply)
      + policy                      = (known after apply)
      + region                      = (known after apply)
      + request_payer               = (known after apply)
      + tags_all                    = (known after apply)
      + website_domain              = (known after apply)
      + website_endpoint            = (known after apply)

      + cors_rule (known after apply)

      + grant (known after apply)

      + lifecycle_rule (known after apply)

      + logging (known after apply)

      + object_lock_configuration (known after apply)

      + replication_configuration (known after apply)

      + server_side_encryption_configuration (known after apply)

      + versioning (known after apply)

      + website (known after apply)
    }

  # aws_s3_bucket.this will be created
  + resource "aws_s3_bucket" "this" {
      + acceleration_status         = (known after apply)
      + acl                         = (known after apply)
      + arn                         = (known after apply)
      + bucket                      = "atmos-native-ci-e2e-test"
      + bucket_domain_name          = (known after apply)
      + bucket_prefix               = (known after apply)
      + bucket_regional_domain_name = (known after apply)
      + force_destroy               = false
      + hosted_zone_id              = (known after apply)
      + id                          = (known after apply)
      + object_lock_enabled         = (known after apply)
      + policy                      = (known after apply)
      + region                      = (known after apply)
      + request_payer               = (known after apply)
      + tags                        = {
          + "AtmosFixture" = "native-ci-e2e"
          + "Stage"        = "test"
        }
      + tags_all                    = {
          + "AtmosFixture" = "native-ci-e2e"
          + "Stage"        = "test"
        }
      + website_domain              = (known after apply)
      + website_endpoint            = (known after apply)

      + cors_rule (known after apply)

      + grant (known after apply)

      + lifecycle_rule (known after apply)

      + logging (known after apply)

      + object_lock_configuration (known after apply)

      + replication_configuration (known after apply)

      + server_side_encryption_configuration (known after apply)

      + versioning (known after apply)

      + website (known after apply)
    }

  # aws_s3_bucket.trivy_target will be created
  + resource "aws_s3_bucket" "trivy_target" {
      + acceleration_status         = (known after apply)
      + acl                         = (known after apply)
      + arn                         = (known after apply)
      + bucket                      = "atmos-native-ci-e2e-trivy-test"
      + bucket_domain_name          = (known after apply)
      + bucket_prefix               = (known after apply)
      + bucket_regional_domain_name = (known after apply)
      + force_destroy               = false
      + hosted_zone_id              = (known after apply)
      + id                          = (known after apply)
      + object_lock_enabled         = (known after apply)
      + policy                      = (known after apply)
      + region                      = (known after apply)
      + request_payer               = (known after apply)
      + tags_all                    = (known after apply)
      + website_domain              = (known after apply)
      + website_endpoint            = (known after apply)

      + cors_rule (known after apply)

      + grant (known after apply)

      + lifecycle_rule (known after apply)

      + logging (known after apply)

      + object_lock_configuration (known after apply)

      + replication_configuration (known after apply)

      + server_side_encryption_configuration (known after apply)

      + versioning (known after apply)

      + website (known after apply)
    }

  # aws_s3_bucket_public_access_block.trivy_target will be created
  + resource "aws_s3_bucket_public_access_block" "trivy_target" {
      + block_public_acls       = true
      + block_public_policy     = true
      + bucket                  = (known after apply)
      + id                      = (known after apply)
      + ignore_public_acls      = true
      + restrict_public_buckets = true
    }

Plan: 4 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + bucket_name = "atmos-native-ci-e2e-test"

…stack-flag

# Conflicts:
#	cmd/vendor/update.go
#	cmd/vendor/vendor.go
#	internal/exec/vendor.go
#	internal/exec/vendor_component_utils_test.go
#	internal/exec/vendor_exclude_test.go
#	internal/exec/vendor_model.go
#	internal/exec/vendor_model_test.go
#	internal/exec/vendor_utils.go
#	pkg/vender/component_vendor_test.go
#	pkg/vender/vendor_config_test.go
#	pkg/vendor/component.go
#	pkg/vendor/uri_triple_slash_test.go
#	pkg/vendor/vendor_integration_test.go
#	tests/snapshots/TestCLICommands_atmos_vendor_pull_using_SSH.stderr.golden
#	tests/snapshots/TestCLICommands_atmos_vendor_pull_with_custom_detector_and_handling_credentials_leakage.stderr.golden
…g rewrite

Main independently rebuilt the vendor subsystem into pkg/vendoring/ (native
component updater PRs, lockfile, SBOM/provenance) while this branch built its
own, now-superseded pkg/vendor registry-pattern rewrite. Drops the duplicate
rewrite and re-implements the one genuinely missing piece -- vendor pull
--stack -- on top of main's current internal/exec/vendor.go +
pkg/vendoring/install architecture: it vendors every component in a stack
that declares its own component.yaml, bypassing vendor.yaml entirely.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
tests/test-cases/vendor-test.yaml's "atmos vendor pull without configuration"
case still expected this branch's now-deleted errors.go sentinel text
("vendoring is not configured"), silently carried through the earlier merge
instead of main's actual internal/exec/vendor_utils.go message
("Vendoring is not configured. To set up vendoring, please see
https://atmos.tools/core-concepts/vendor/"). CI caught the mismatch on both
linux and macos acceptance runs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…r subcommands

vendor pull already had --stack/--tags; vendor update had --tags only; diff/clean/verify
had neither. Adds --labels (stack-resolved metadata.labels, composable with --stack) and
brings --tags (vendor.yaml source tags) and --stack to diff/clean/verify, with a shared
internal/exec.ResolveVendorComponentSelector and cmd/vendor selector helpers. Fixes a bug
where an unresolvable --stack/--tags/--labels selector silently fell through to "act on
everything" instead of erroring. diff gains batch mode when a selector matches multiple
components.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
coderabbitai[bot]
coderabbitai Bot previously approved these changes Aug 6, 2026
…--tags

internal/exec/vendor.go's parseVendorTagsFlag split --tags on commas without
trimming, so "networking, database" produced " database" with a leading
space and "a,,b" produced an empty entry. cmd/vendor's splitTags (used by
vendor clean/diff/verify) already trims and filters correctly; parseVendorTagsFlag
now matches.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
atmos.yaml's InitCliConfig was always called with processStacks=false
across vendor pull/diff/clean/verify/update, so
atmosConfig.StackConfigFilesAbsolutePaths was never populated and
ExecuteDescribeStacksScoped silently read zero stack files. Every
--stack/--labels invocation failed with a misleading "not found" error
regardless of whether the stack existed, because unit tests mock
ExecuteDescribeStacksScoped directly and no CLI-level test exercised
these flags end-to-end.

Pass processStacks=true only when --stack/--labels is actually used, so
the common --component/--tags/--everything paths keep their existing
performance characteristics. Add CLI regression tests
(atmos_vendor_pull_stack, atmos_vendor_pull_labels) against a new
tests/fixtures/scenarios/vendor-stack-labels/ fixture with real
component.yaml manifests and stack metadata.labels, and fix
TestVendorUpdateCommand_UnknownStackErrors, which relied on an
InitCliConfig(false) no-op and needed a real (non-matching) stack file
now that stack processing genuinely runs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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 size/l Large size PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant