Skip to content

fix: base path resolution fallback when git root is unavailable#2236

Merged
aknysh merged 3 commits intomainfrom
aknysh/fix-paths-and-env-vars
Mar 20, 2026
Merged

fix: base path resolution fallback when git root is unavailable#2236
aknysh merged 3 commits intomainfrom
aknysh/fix-paths-and-env-vars

Conversation

@aknysh
Copy link
Copy Markdown
Member

@aknysh aknysh commented Mar 20, 2026

what

  • Fix failed to find import error when ATMOS_BASE_PATH is set to a relative path on CI workers without a .git directory (e.g., Spacelift)
  • Make tryResolveWithGitRoot and tryResolveWithConfigPath source-aware by passing the source parameter through the call chain
  • For runtime sources (ATMOS_BASE_PATH env var, --base-path flag, atmos_base_path provider param), tryResolveWithConfigPath now tries CWD first before config dir
  • Both paths use os.Stat validation with fallback
  • Add 5 bug reproduction tests for the base path resolution fix
  • Add 4 cycle detection tests for the metadata.component stack overflow fix verification
  • Extract basePathSourceRuntime and cwdResolutionErrFmt constants
  • Extract tryCWDRelative helper to reduce cognitive complexity

why

The v1.210.1 fix (PR #2215) added os.Stat + CWD fallback to tryResolveWithGitRoot, but this only works when git root IS available. On CI workers without .git (e.g., Spacelift), getGitRootOrEmpty() returns "" and the code falls through to tryResolveWithConfigPath — which lacked the same fallback. It unconditionally joined with cliConfigPath (the atmos.yaml directory), producing the wrong path.

The broken code path (before this fix)

  1. User sets ATMOS_BASE_PATH=.terraform/modules/monorepo
  2. processEnvVars sets BasePath and BasePathSource = "runtime"
  3. resolveAbsolutePath classifies .terraform/modules/monorepo as a bare path → calls tryResolveWithGitRoot
  4. getGitRootOrEmpty() returns "" — no .git on CI
  5. Falls to tryResolveWithConfigPath(".terraform/modules/monorepo", "/workspace")
  6. Unconditionally returns /workspace/.terraform/modules/monorepoWRONG (doesn't exist)
  7. Correct path: /workspace/components/terraform/iam-delegated-roles/.terraform/modules/monorepo

Why absolute paths work

When ATMOS_BASE_PATH is set to an absolute path, resolveAbsolutePath returns it as-is at the first check (filepath.IsAbs), bypassing all resolution logic.

The asymmetry fixed

Function Before After
tryResolveWithGitRoot Has os.Stat + CWD fallback (v1.210.1) Unchanged — now passes source to fallback
tryResolveWithConfigPath No os.Stat, no CWD fallback Source-aware: runtime → CWD first, config → config dir first, both with os.Stat

Resolution order after fix

Source Git Root Available Git Root Unavailable
Runtime (env var, CLI flag, provider param) git root → CWD (existing) CWD → config dir (new)
Config (base_path in atmos.yaml) git root → CWD (existing) config dir → CWD (new)

Stack overflow verification

The same user also reported fatal error: stack overflow when abstract components have metadata.component set (fixed in v1.210.0, PR #2214). Additional tests were written to verify the cycle detection works for various patterns including multiple abstract/real pairs, cross-component cycles, and defer delete re-entry. All pass — the cycle detection is working correctly for all reproducible patterns.

references

Summary by CodeRabbit

  • Documentation

    • New guide describing CI-only "failed to find import" behavior with relative ATMOS_BASE_PATH and how resolution differs vs absolute paths.
  • Bug Fixes

    • Source-aware base-path resolution with proper existence checks and adjusted fallback ordering to prefer CWD for runtime cases, preventing incorrect joined paths.
  • Tests

    • New and updated regression tests covering base-path resolution scenarios and multiple cycle-detection cases to prevent stack-overflow and ensure correct fallback behavior.

aknysh and others added 2 commits March 19, 2026 23:46
Make tryResolveWithGitRoot and tryResolveWithConfigPath source-aware by
passing the source parameter through the call chain. For runtime sources
(ATMOS_BASE_PATH env var, --base-path flag, atmos_base_path provider
param), tryResolveWithConfigPath now tries CWD first before config dir.
Both paths use os.Stat validation with fallback.

This fixes "failed to find import" on CI workers without .git where the
v1.210.1 os.Stat fallback in tryResolveWithGitRoot was bypassed because
getGitRootOrEmpty returned empty.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@aknysh aknysh requested a review from a team as a code owner March 20, 2026 05:27
@aknysh aknysh added the patch A minor, backward compatible change label Mar 20, 2026
@aknysh aknysh self-assigned this Mar 20, 2026
@github-actions github-actions bot added the size/l Large size PR label Mar 20, 2026
@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 20, 2026

Dependency Review

✅ No vulnerabilities or license issues found.

Scanned Files

None

@codecov
Copy link
Copy Markdown

codecov bot commented Mar 20, 2026

Codecov Report

❌ Patch coverage is 75.00000% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 76.92%. Comparing base (5d12f9d) to head (5f75adc).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
pkg/config/config.go 73.33% 4 Missing and 4 partials ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #2236      +/-   ##
==========================================
+ Coverage   76.89%   76.92%   +0.02%     
==========================================
  Files        1003     1003              
  Lines       95524    95545      +21     
==========================================
+ Hits        73456    73495      +39     
+ Misses      17805    17783      -22     
- Partials     4263     4267       +4     
Flag Coverage Δ
unittests 76.92% <75.00%> (+0.02%) ⬆️

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

Files with missing lines Coverage Δ
pkg/config/utils.go 88.05% <100.00%> (ø)
pkg/config/config.go 71.20% <73.33%> (-0.83%) ⬇️

... and 5 files with indirect coverage changes

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

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 20, 2026

📝 Walkthrough

Walkthrough

Makes base-path resolution source-aware to fix a CI-only failed to find import when ATMOS_BASE_PATH is a relative path and no .git exists: adds os.Stat validation, CWD fallback, a runtime/config source distinction, helper tryCWDRelative, new tests, and documentation of the issue and fix.

Changes

Cohort / File(s) Summary
Documentation
docs/fixes/2026-03-19-failed-to-find-import-no-git-root-fallback.md
New doc describing the CI relative-ATMOS_BASE_PATH failure, reproducer, and the chosen Option B source-aware fix.
Base Path Resolution Logic
pkg/config/config.go, pkg/config/utils.go
Introduce basePathSourceRuntime, thread source through resolveAbsolutePathtryResolveWithGitRoottryResolveWithConfigPath, add os.Stat checks, add tryCWDRelative(), and change fallback ordering based on source.
Base Path Resolution Tests
pkg/config/base_path_resolution_test.go
Updated calls for new signatures and added regression/integration tests for CI scenario with relative ATMOS_BASE_PATH, git-root disabled, CWD fallback, and existence-check behavior.
Config Tests
pkg/config/config_test.go
Updated test call sites to pass the new third source argument to path-resolution helpers.
Cycle Detection Tests
internal/exec/stack_processor_utils_test.go
Added multiple regression tests exercising abstract/real component inheritance cycles and cache isolation to prevent stack-overflow and validate circular-inheritance errors.

Sequence Diagram(s)

sequenceDiagram
    participant Caller as InitCliConfig
    participant Resolve as resolveAbsolutePath
    participant GitRoot as tryResolveWithGitRoot
    participant ConfigPath as tryResolveWithConfigPath
    participant CWD as tryCWDRelative
    participant Disk as os.Stat

    Caller->>Resolve: path, source (runtime/config)
    Resolve->>GitRoot: attempt git-root resolution
    GitRoot->>Disk: stat candidate under git root
    alt exists
        GitRoot-->>Resolve: return git-root path
    else
        GitRoot->>ConfigPath: delegate to config-path resolution (with source)
        ConfigPath->>Disk: stat config-dir candidate
        alt config exists
            ConfigPath-->>Resolve: return config-dir path
        else
            ConfigPath->>CWD: try CWD-relative (order depends on source)
            CWD->>Disk: stat CWD candidate
            alt CWD exists
                CWD-->>Resolve: return CWD path
            else
                CWD-->>Resolve: error
            end
        end
    end
    Resolve-->>Caller: final path or error
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • osterman
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: fixing base path resolution fallback when git root is unavailable, which directly addresses the primary issue described in the PR objectives.
Docstring Coverage ✅ Passed Docstring coverage is 83.33% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch aknysh/fix-paths-and-env-vars
📝 Coding Plan
  • Generate coding plan for human review comments

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.

Tip

You can enable review details to help with troubleshooting, context usage and more.

Enable the reviews.review_details setting to include review details such as the model used, the time taken for each step and more in the review comments.

Copy link
Copy Markdown
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: 2

🧹 Nitpick comments (1)
pkg/config/config.go (1)

387-398: Consider distinguishing stat error types.

The helper swallows all os.Stat errors uniformly. Unlike tryResolveWithGitRoot (lines 332-337), this doesn't distinguish between "not exist" and permission/access errors. For a fallback helper this is acceptable, but permission errors could silently fall through to the next candidate.

If you want parity with the git-root path:

♻️ Optional: surface non-NotExist errors
 func tryCWDRelative(path string) (string, bool) {
 	cwdJoined, err := absPathOrError(path, fmt.Sprintf(cwdResolutionErrFmt, path))
 	if err != nil {
 		return "", false
 	}
-	if _, statErr := os.Stat(cwdJoined); statErr == nil {
+	if info, statErr := os.Stat(cwdJoined); statErr == nil {
+		_ = info // exists
 		log.Trace("Path resolved relative to CWD", "path", path, "resolved", cwdJoined)
 		return cwdJoined, true
+	} else if !os.IsNotExist(statErr) {
+		log.Trace("Permission or access error checking CWD path", "path", cwdJoined, "error", statErr)
 	}
 	return "", false
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/config/config.go` around lines 387 - 398, The tryCWDRelative helper
currently swallows all os.Stat errors; update tryCWDRelative to mirror
tryResolveWithGitRoot by checking os.IsNotExist(statErr) and treating "not
exist" as the expected fallback, but for any other statErr surface it (e.g.,
log.Warn or log.Error with the error value and context) instead of silently
ignoring it; keep absPathOrError usage for the initial resolution and reference
the tryCWDRelative and tryResolveWithGitRoot functions when making this change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/fixes/2026-03-19-failed-to-find-import-no-git-root-fallback.md`:
- Around line 398-400: The references section incorrectly states current
behavior for tryResolveWithConfigPath; reword the Line 399 note to indicate
pre-fix behavior (e.g., "tryResolveWithConfigPath — pre-fix: lacked os.Stat
fallback") so it doesn't conflict with the implemented fix described above;
ensure tryResolveWithGitRoot and getGitRootOrEmpty remain described accurately
and that the wording clearly distinguishes pre-fix vs implemented behavior for
tryResolveWithConfigPath.

In `@internal/exec/stack_processor_utils_test.go`:
- Around line 491-537: The test fixture allComponentsMap currently models two
isolated pairs (iam-delegated-roles-defaults/iam-delegated-roles and
eks-defaults/eks); add a cross-dependency so the test exercises coupled paths —
for example, add the other component’s defaults into one real component’s
metadata.inherits array (e.g., add "eks-defaults" to
iam-delegated-roles.metadata.inherits or add "iam-delegated-roles-defaults" to
eks.metadata.inherits) and adjust any vars if needed to reflect the dependency;
update only the inherits arrays on iam-delegated-roles or eks in
allComponentsMap so mergeComponentConfigurations and the dependency traversal
see the cross-link.

---

Nitpick comments:
In `@pkg/config/config.go`:
- Around line 387-398: The tryCWDRelative helper currently swallows all os.Stat
errors; update tryCWDRelative to mirror tryResolveWithGitRoot by checking
os.IsNotExist(statErr) and treating "not exist" as the expected fallback, but
for any other statErr surface it (e.g., log.Warn or log.Error with the error
value and context) instead of silently ignoring it; keep absPathOrError usage
for the initial resolution and reference the tryCWDRelative and
tryResolveWithGitRoot functions when making this change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e4a70eff-f627-4d04-90d7-975824dce0f4

📥 Commits

Reviewing files that changed from the base of the PR and between 5d12f9d and 8779313.

📒 Files selected for processing (6)
  • docs/fixes/2026-03-19-failed-to-find-import-no-git-root-fallback.md
  • internal/exec/stack_processor_utils_test.go
  • pkg/config/base_path_resolution_test.go
  • pkg/config/config.go
  • pkg/config/config_test.go
  • pkg/config/utils.go

- Clarify pre-fix vs post-fix wording in fix doc references section
- Add cross-dependency to multiple abstract components cycle test
- Log permission/access errors in tryCWDRelative for parity with tryResolveWithGitRoot

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
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.

🧹 Nitpick comments (1)
docs/fixes/2026-03-19-failed-to-find-import-no-git-root-fallback.md (1)

186-223: Consider noting the constant usage.

The pseudo-code uses the literal string "runtime" (Line 188), but the actual implementation uses a constant basePathSourceRuntime. This is fine for documentation clarity, but a brief note like "// where 'runtime' is basePathSourceRuntime constant" could help readers cross-reference the actual code.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/fixes/2026-03-19-failed-to-find-import-no-git-root-fallback.md` around
lines 186 - 223, The doc example uses the literal "runtime" but the real code
uses the constant basePathSourceRuntime; update the comment around the
conditional in tryResolveWithConfigPath to mention that "runtime" corresponds to
the basePathSourceRuntime constant (e.g., add a brief inline note like "//
'runtime' == basePathSourceRuntime constant") so readers can cross-reference the
actual implementation.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@docs/fixes/2026-03-19-failed-to-find-import-no-git-root-fallback.md`:
- Around line 186-223: The doc example uses the literal "runtime" but the real
code uses the constant basePathSourceRuntime; update the comment around the
conditional in tryResolveWithConfigPath to mention that "runtime" corresponds to
the basePathSourceRuntime constant (e.g., add a brief inline note like "//
'runtime' == basePathSourceRuntime constant") so readers can cross-reference the
actual implementation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 654157f8-c783-4f18-836c-fa2229f1841a

📥 Commits

Reviewing files that changed from the base of the PR and between 8779313 and 5f75adc.

📒 Files selected for processing (3)
  • docs/fixes/2026-03-19-failed-to-find-import-no-git-root-fallback.md
  • internal/exec/stack_processor_utils_test.go
  • pkg/config/config.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/config/config.go

@aknysh aknysh requested a review from osterman March 20, 2026 15:30
@aknysh aknysh merged commit 0ef4161 into main Mar 20, 2026
57 checks passed
@aknysh aknysh deleted the aknysh/fix-paths-and-env-vars branch March 20, 2026 15:41
@github-actions
Copy link
Copy Markdown

These changes were released in v1.211.0-rc.1.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

patch A minor, backward compatible change size/l Large size PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants