Skip to content

[codex] Fix remote Git stack imports - #2528

Merged
Andriy Knysh (aknysh) merged 6 commits into
mainfrom
osterman/fix-remote-folder-imports
May 28, 2026
Merged

Andriy Knysh (aknysh) merged 6 commits into
mainfrom
osterman/fix-remote-folder-imports

Conversation

@osterman

@osterman Erik Osterman (Cloud Posse) (osterman) commented May 26, 2026 •

Copy link
Copy Markdown
Member

what

  • Add a remote stack import resolver that can return multiple local import matches while preserving the existing single-file download path.
  • Handle Git go-getter //subdir imports by cloning the repository as a directory, resolving files, no-extension YAML variants, explicit globs, and recursive YAML directory imports.
  • Cache expanded remote files with stable <original-uri>#<relative-file> keys and update stack processing/tests to consume those keys for imports and provenance.

why

  • Fixes the regression where remote Git subdir imports were forced through file mode, causing git clone to drop the repository name and target https://github.com/<owner>/.
  • Supports remote folder imports for stack manifests without leaking local cache paths into import metadata.

references

  • Remote imports docs: https://atmos.tools/stacks/imports
  • Validated with go test ./pkg/stack/imports ./pkg/downloader, go test ./tests -run RemoteStackImports, and go test ./internal/exec -run '^$'.
  • Commit hooks passed after building the required local custom-gcl lint binary.

Summary by CodeRabbit

  • New Features

    • Remote imports now resolve Git subdirectories, wildcards, and nested-remote imports with deterministic matching and improved per-session and persistent caching.
    • Per-import control for nested import resolution (local vs remote) with validation and defaults.
  • Bug Fixes

    • More consistent handling of missing imports and skip-if-missing behavior; clearer errors for unresolved imports.
  • Documentation

    • Docs, examples, and PRD updated to explain nested-imports behavior and best practices.
  • Tests

    • Expanded unit and integration tests for remote/Git resolution and CLI scenarios.

Review Change Stack

@atmos-pro

atmos-pro Bot commented May 26, 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 the size/m Medium size PR label May 26, 2026
@github-actions

github-actions Bot commented May 26, 2026 •

Copy link
Copy Markdown

Dependency Review

✅ No vulnerabilities or license issues found.

Scanned Files

None

@osterman Erik Osterman (Cloud Posse) (osterman) added the patch A minor, backward compatible change label May 26, 2026
@osterman
Erik Osterman (Cloud Posse) (osterman) marked this pull request as ready for review May 26, 2026 20:56
@coderabbitai

coderabbitai Bot commented May 26, 2026 •

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

Unifies local and remote import handling via RemoteImportMatch/Resolve (Git subdir discovery, per-file caching), adds ResolveNested for nested_imports: remote with base-path inference, integrates matches into the stack processor, validates nested_imports, and adds tests, CLI fixtures, and docs.

Changes

Remote Import Resolution and Integration

Layer / File(s) Summary
Schema and StackImport contract
pkg/schema/schema.go, pkg/datafetcher/schema/stacks/stack-config/1.0.json
Add nested_imports property to schema and StackImport.NestedImports plus local/remote constants.
RemoteImporter.Resolve and cache management
pkg/stack/imports/remote.go
Add RemoteImportMatch (Path, Key, BasePath), session matchCache, Resolve(uri) to map remote URIs (Git/HTTP) to local cached stack files with globbing, per-file caching, dedupe/sort, and ResolveRemoteImport helper; extend ClearCache.
ResolveNested and source caching / base-path inference
pkg/stack/imports/remote_nested.go
Implement ResolveNested(uri, mode) to support nested_imports: remote, ensureSourceDir download/cache with readiness marker, infer remote stacks base path, and expand subdir into RemoteImportMatch entries.
Stack processor refactor to unified RemoteImportMatch pattern
internal/exec/stack_processor_utils.go
Propagate localBasePath and inheritedNestedImports, produce []RemoteImportMatch for remote and local imports (convert local glob matches), add ErrStackImportNotFound wrapping for missing non-template imports, prefer match.Key for relative-path metadata, and validate/normalize NestedImports when decoding imports.
Unit test helpers and Resolve tests
pkg/stack/imports/remote_test.go
Add git-backed test helpers, controlled downloader, and tests covering Resolve/ResolveNested behaviors, cache invalidation, helper edge cases, and stack-file resolution utilities.
CLI integration tests for Git remote imports
tests/cli_remote_imports_test.go
Add git fixture helpers, stdout capture, and tests for directory imports with skip_if_missing and nested_imports: remote.
Docs, PRD, and examples
website/docs/stacks/imports.mdx, docs/prd/import-adapter-registry.md, examples/remote-stack-imports/README.md
Document nested_imports option, default/inheritance rules, and provide examples and best-practice guidance.
ProcessImportSection unit & integration tests
internal/exec/stack_processor_utils_test.go
Add tests validating decoding, normalization, and error handling for nested_imports, plus integration test for inherited nested remote base path.

Sequence Diagram(s)

sequenceDiagram
  participant Caller as Caller
  participant RemoteImporter as RemoteImporter.Resolve
  participant MatchCache as matchCache
  participant GitDownloader as Git downloader
  participant GlobResolver as resolveStackFiles
  participant FileCache as memCache/FileCache

  Caller->>RemoteImporter: Resolve(uri)
  RemoteImporter->>MatchCache: check cached matches
  alt cache hit
    MatchCache-->>RemoteImporter: cached []RemoteImportMatch
    RemoteImporter-->>Caller: return matches
  else cache miss
    RemoteImporter->>GitDownloader: download/checkout git source (if Git)
    GitDownloader-->>RemoteImporter: local source dir
    RemoteImporter->>GlobResolver: resolveStackFiles(subdir)
    GlobResolver-->>RemoteImporter: []file paths
    RemoteImporter->>FileCache: cacheFile(key, content) per file
    RemoteImporter->>MatchCache: store cloned matches
    RemoteImporter-->>Caller: return matches
  end
Loading
sequenceDiagram
  participant Processor as ProcessImportSection
  participant Remote as ResolveRemoteImport
  participant LocalGlob as u.GetGlobMatches
  participant Matches as importMatches[] (RemoteImportMatch)

  Processor->>Remote: remote import URI
  Remote-->>Processor: []RemoteImportMatch
  Processor->>LocalGlob: local glob pattern
  LocalGlob-->>Processor: []string paths
  Processor->>Matches: convert local paths -> RemoteImportMatch (Path)
  Processor->>Matches: unified loop iterates RemoteImportMatch (match.Path / match.Key)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

Possibly related PRs

Suggested labels

minor

Suggested reviewers

  • aknysh
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 17.65% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title '[codex] Fix remote Git stack imports' is directly related to the main change—adding remote Git stack import resolver with subdirectory support and caching.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch osterman/fix-remote-folder-imports

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.

@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: 2

🧹 Nitpick comments (1)
tests/cli_remote_imports_test.go (1)

41-47: 💤 Low value

Consider adding a git availability check.

The test uses exec.Command("git", ...) which assumes git is installed. While git is typically available in development environments, consider adding a skip check at the test start:

if _, err := exec.LookPath("git"); err != nil {
    t.Skip("git not available")
}

This would gracefully handle environments without git rather than failing hard.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/cli_remote_imports_test.go` around lines 41 - 47, Add a git
availability check to avoid hard failures when git is missing: in the test entry
(or at the top of the helper runRemoteImportsGit), call exec.LookPath("git") and
if it returns an error call t.Skip("git not available") so tests are skipped
instead of failing; update runRemoteImportsGit (or the test that calls it) to
perform this check before running exec.Command("git", ...) and then proceed as
before if git is found.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/stack/imports/remote_test.go`:
- Around line 66-68: gitFileURI currently builds a file:// URL with raw OS path
which makes Windows backslashes get encoded; update gitFileURI to first convert
the path to an absolute, slash-normalized form (use filepath.Abs +
filepath.ToSlash) and, for Windows drive-letter paths (e.g., "C:/..."), ensure
the URL Path begins with a leading "/" (so it becomes "/C:/...") before
constructing (&url.URL{Scheme: "file", Path: path}).String(); this will produce
a valid file:///C:/... URI on Windows and keep POSIX behavior unchanged.

In `@pkg/stack/imports/remote.go`:
- Around line 248-255: The temp directory for fetching is currently derived only
from uriToTempName(originalURI), causing races when Resolve is called
concurrently; change the tempDir creation to produce a unique per-call directory
(e.g., use os.MkdirTemp or filepath.Join(r.cache.BaseDir(), fmt.Sprintf("%s-%s",
uriToTempName(originalURI), uuidOrTimestamp))) and use that returned path for
both the call to r.downloader.Fetch and the deferred os.RemoveAll so only this
invocation's directory is removed; update any code that assumes the original
non-unique path (references: tempDir, uriToTempName(originalURI),
r.downloader.Fetch) accordingly.

---

Nitpick comments:
In `@tests/cli_remote_imports_test.go`:
- Around line 41-47: Add a git availability check to avoid hard failures when
git is missing: in the test entry (or at the top of the helper
runRemoteImportsGit), call exec.LookPath("git") and if it returns an error call
t.Skip("git not available") so tests are skipped instead of failing; update
runRemoteImportsGit (or the test that calls it) to perform this check before
running exec.Command("git", ...) and then proceed as before if git is found.
🪄 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: c39e966f-240e-4f1d-bedf-72ac69ab7244

📥 Commits

Reviewing files that changed from the base of the PR and between 733edd0 and 6865688.

📒 Files selected for processing (4)
  • internal/exec/stack_processor_utils.go
  • pkg/stack/imports/remote.go
  • pkg/stack/imports/remote_test.go
  • tests/cli_remote_imports_test.go

Comment thread pkg/stack/imports/remote_test.go
Comment thread pkg/stack/imports/remote.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.

🧹 Nitpick comments (1)
pkg/stack/imports/remote_test.go (1)

58-64: 💤 Low value

Consider CI environment compatibility.

These tests invoke the git binary directly. While pragmatic for testing real git behavior, they'll skip or fail in environments without git installed.

If CI stability becomes a concern, consider wrapping these tests with a skip check:

func skipWithoutGit(t *testing.T) {
    t.Helper()
    if _, err := exec.LookPath("git"); err != nil {
        t.Skip("git not available")
    }
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/stack/imports/remote_test.go` around lines 58 - 64, The tests call the
external git binary from runGit which will fail in CI machines that lack git;
add a pre-check helper (e.g., skipWithoutGit) that uses exec.LookPath("git") and
t.Skip when not found, and invoke that helper at the start of Test functions (or
inside runGit) so tests gracefully skip instead of failing in environments
without git; update runGit or each test that uses runGit to call
skipWithoutGit(t) before executing git commands.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@pkg/stack/imports/remote_test.go`:
- Around line 58-64: The tests call the external git binary from runGit which
will fail in CI machines that lack git; add a pre-check helper (e.g.,
skipWithoutGit) that uses exec.LookPath("git") and t.Skip when not found, and
invoke that helper at the start of Test functions (or inside runGit) so tests
gracefully skip instead of failing in environments without git; update runGit or
each test that uses runGit to call skipWithoutGit(t) before executing git
commands.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c478dada-a983-4ee2-8b12-8410cc55055b

📥 Commits

Reviewing files that changed from the base of the PR and between 6865688 and 2a96afe.

📒 Files selected for processing (3)
  • pkg/stack/imports/remote.go
  • pkg/stack/imports/remote_test.go
  • tests/cli_remote_imports_test.go

coderabbitai[bot]
coderabbitai Bot previously approved these changes May 26, 2026
@codecov

codecov Bot commented May 26, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.11594% with 41 lines in your changes missing coverage. Please review.
✅ Project coverage is 78.61%. Comparing base (bdc3a7b) to head (b6ce69f).

Files with missing lines Patch % Lines
pkg/stack/imports/remote.go 89.52% 10 Missing and 10 partials ⚠️
pkg/stack/imports/remote_nested.go 83.01% 9 Missing and 9 partials ⚠️
internal/exec/stack_processor_utils.go 93.75% 2 Missing and 1 partial ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #2528      +/-   ##
==========================================
+ Coverage   78.54%   78.61%   +0.06%     
==========================================
  Files        1144     1145       +1     
  Lines      109984   110311     +327     
==========================================
+ Hits        86387    86721     +334     
+ Misses      18807    18777      -30     
- Partials     4790     4813      +23     
Flag Coverage Δ
unittests 78.61% <88.11%> (+0.06%) ⬆️

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

Files with missing lines Coverage Δ
pkg/schema/schema.go 87.70% <ø> (ø)
internal/exec/stack_processor_utils.go 81.93% <93.75%> (+0.53%) ⬆️
pkg/stack/imports/remote_nested.go 83.01% <83.01%> (ø)
pkg/stack/imports/remote.go 91.63% <89.52%> (-0.37%) ⬇️

... 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[bot]
coderabbitai Bot previously approved these changes May 26, 2026

@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: 1

🧹 Nitpick comments (2)
pkg/stack/imports/remote_nested.go (1)

19-23: ⚡ Quick win

Use r.atmosConfig instead of nil in perf.Track.

The receiver has access to r.atmosConfig (used at line 128 in remoteStacksBasePath). Passing it ensures consistent tracking.

Suggested fix
 func (r *RemoteImporter) ResolveNested(uri, nestedImports string) ([]RemoteImportMatch, error) {
-	defer perf.Track(nil, "imports.RemoteImporter.ResolveNested")()
+	defer perf.Track(r.atmosConfig, "imports.RemoteImporter.ResolveNested")()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/stack/imports/remote_nested.go` around lines 19 - 23, The perf tracking
call in RemoteImporter.ResolveNested currently passes nil which breaks
consistency with other calls using the importer config; replace the defer
perf.Track(nil, "imports.RemoteImporter.ResolveNested")() with a call that
passes the importer config (use r.atmosConfig) so it matches how
remoteStacksBasePath and other methods report metrics; update the defer to defer
perf.Track(r.atmosConfig, "imports.RemoteImporter.ResolveNested")().
pkg/stack/imports/remote_test.go (1)

290-314: ⚡ Quick win

Add an explicit prerequisite sub-test for git env propagation.

This test depends on subprocess env behavior (GIT_CONFIG_GLOBAL, GIT_CONFIG_NOSYSTEM, HOME) but doesn’t assert that prerequisite explicitly before the main assertion. Add a small precheck sub-test that validates git config sees the expected rewrite first.

Proposed test shape.
 func TestRemoteImporter_Resolve_GitHubShorthandSubdir(t *testing.T) {
   repoDir := initGitRepo(t, map[string]string{
     "stacks/dev.yaml": "vars:\n  shorthand: true\n",
   })
@@
   t.Setenv("HOME", t.TempDir())
+
+  t.Run("prerequisite: git sees insteadOf rewrite", func(t *testing.T) {
+    cmd := exec.Command("git", "config", "--global", "--get-regexp", "^url\\..*\\.insteadOf$")
+    out, err := cmd.CombinedOutput()
+    require.NoError(t, err, string(out))
+    assert.Contains(t, string(out), "https://github.com/acme/infrastructure")
+  })
 
   atmosConfig := &schema.AtmosConfiguration{}
   importer := newTestRemoteImporter(t, atmosConfig)

As per coding guidelines, "**/*_test.go: Add prerequisite sub-tests for subprocess behavior: when a test depends on implicit env propagation, add an explicit sub-test confirming the behavior before the main test runs.".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/stack/imports/remote_test.go` around lines 290 - 314, Add a small
prerequisite sub-test at the start of
TestRemoteImporter_Resolve_GitHubShorthandSubdir that validates the git
subprocess sees the configured rewrite (the
GIT_CONFIG_GLOBAL/GIT_CONFIG_NOSYSTEM/HOME envs set in the test). Specifically,
before calling importer.Resolve, run a git subprocess (e.g.,
exec.Command("git","config","--global","--get","insteadOf",
"https://github.com/acme/infrastructure") or "git config --global --list") with
the same environment and assert its output contains the expected file://...
rewrite (use gitFileURI(repoDir) to build the expected value); keep the check as
a t.Run sub-test so failures clearly indicate env propagation issues. Ensure
this runs prior to invoking newTestRemoteImporter/importer.Resolve.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/stack/imports/remote_test.go`:
- Around line 27-53: Replace the manual stub type stubRemoteDownloader in
remote_test.go with a mockgen-generated mock for the downloader interface:
generate a mock (e.g., mock_downloader.NewMockDownloader) for the interface that
declares Fetch/FetchAnd* methods, create a gomock controller in the test,
instantiate the generated mock, and configure only the expected Fetch
interaction via mock.EXPECT().Fetch(...) with appropriate matchers for src,
dest, mode (downloader.ClientMode) and timeout to return the desired error or
nil; remove the manual Fetch/FetchAnd* implementations and update the test to
use the generated mock instance.

---

Nitpick comments:
In `@pkg/stack/imports/remote_nested.go`:
- Around line 19-23: The perf tracking call in RemoteImporter.ResolveNested
currently passes nil which breaks consistency with other calls using the
importer config; replace the defer perf.Track(nil,
"imports.RemoteImporter.ResolveNested")() with a call that passes the importer
config (use r.atmosConfig) so it matches how remoteStacksBasePath and other
methods report metrics; update the defer to defer perf.Track(r.atmosConfig,
"imports.RemoteImporter.ResolveNested")().

In `@pkg/stack/imports/remote_test.go`:
- Around line 290-314: Add a small prerequisite sub-test at the start of
TestRemoteImporter_Resolve_GitHubShorthandSubdir that validates the git
subprocess sees the configured rewrite (the
GIT_CONFIG_GLOBAL/GIT_CONFIG_NOSYSTEM/HOME envs set in the test). Specifically,
before calling importer.Resolve, run a git subprocess (e.g.,
exec.Command("git","config","--global","--get","insteadOf",
"https://github.com/acme/infrastructure") or "git config --global --list") with
the same environment and assert its output contains the expected file://...
rewrite (use gitFileURI(repoDir) to build the expected value); keep the check as
a t.Run sub-test so failures clearly indicate env propagation issues. Ensure
this runs prior to invoking newTestRemoteImporter/importer.Resolve.
🪄 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: ddcb6582-41ec-4530-bebd-f6e8efbb7f86

📥 Commits

Reviewing files that changed from the base of the PR and between c2a3b3d and 9c20964.

📒 Files selected for processing (11)
  • docs/prd/import-adapter-registry.md
  • examples/remote-stack-imports/README.md
  • internal/exec/stack_processor_utils.go
  • internal/exec/stack_processor_utils_test.go
  • pkg/datafetcher/schema/stacks/stack-config/1.0.json
  • pkg/schema/schema.go
  • pkg/stack/imports/remote.go
  • pkg/stack/imports/remote_nested.go
  • pkg/stack/imports/remote_test.go
  • tests/cli_remote_imports_test.go
  • website/docs/stacks/imports.mdx
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/stack/imports/remote.go

Comment thread pkg/stack/imports/remote_test.go Outdated
@aknysh
Andriy Knysh (aknysh) merged commit 5cbc94a into main May 28, 2026
60 checks passed
@aknysh
Andriy Knysh (aknysh) deleted the osterman/fix-remote-folder-imports branch May 28, 2026 03:51
@atmos-pro

atmos-pro Bot commented May 28, 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

Copy link
Copy Markdown

These changes were released in v1.220.0.

This branch was successfully deployed

1 active deployment
preview — b6ce69f7 Deployed May 28, 2026 by github-actions[bot]
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