Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
469bdb4
feat: implement remote stack imports
osterman Jan 29, 2026
0af41ea
refactor: extract generic cache infrastructure into pkg/cache
osterman Jan 29, 2026
c519561
fix: address PR feedback for remote stack imports
osterman Jan 29, 2026
c1a44ce
chore: ignore kubernetes.io and runatlantis.io in link checker
osterman Jan 29, 2026
d291931
fix: address PR review comments for remote imports
osterman Jan 29, 2026
21bdaea
fix: ensure cache directory exists after options applied
osterman Jan 29, 2026
7067e0d
fix: ignore cache read errors on Windows
osterman Jan 29, 2026
60bc9dc
test: improve coverage for cache and stack imports packages
osterman Jan 30, 2026
7cfb6a1
fix: handle deleted cache directory in Clear() method
osterman Jan 30, 2026
972d94c
fix: address CodeRabbit review comments
osterman Jan 30, 2026
5a0ec54
fix: persist BrowserSessionWarningShown in cache
osterman Jan 30, 2026
8f1765f
Merge remote-tracking branch 'origin/main' into osterman/remote-stack…
osterman Feb 5, 2026
b5a6f6e
Merge main and address CodeRabbit review comments
osterman Feb 5, 2026
1a44c3b
fix: preserve lock file during cache Clear() and improve test coverage
osterman Feb 6, 2026
622b2a3
fix: data race on downloadCount and improve test coverage
osterman Feb 6, 2026
4afcb2d
Merge main and resolve conflicts in lychee.toml and roadmap.js
osterman Mar 14, 2026
8172625
Merge remote-tracking branch 'origin/main' into osterman/remote-stack…
osterman May 22, 2026
5d83e14
fix: clarify ignored cache write errors
osterman May 22, 2026
5de58dd
Merge remote-tracking branch 'origin/main' into osterman/remote-stack…
osterman May 23, 2026
ee33ce0
Merge branch 'main' into osterman/remote-stack-imports
osterman May 23, 2026
cbae2e1
chore: retrigger ci
osterman May 23, 2026
ca000bd
Merge remote-tracking branch 'origin/osterman/remote-stack-imports' i…
osterman May 23, 2026
0e3431f
fix: stabilize vendor acceptance test
osterman May 23, 2026
119daa8
fix: harden import and cache path handling
osterman May 23, 2026
39bad89
fix: preserve template cache extensions
osterman May 23, 2026
6c995e0
Merge remote-tracking branch 'origin/main' into osterman/remote-stack…
osterman May 25, 2026
4f4d7fd
Merge branch 'main' into osterman/remote-stack-imports
aknysh May 25, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions errors/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,10 @@ var (
ErrInvalidAuthSection = errors.New("invalid auth section")
ErrInvalidImportSection = errors.New("invalid import section")
ErrInvalidImport = errors.New("invalid import")
ErrInvalidRemoteImport = errors.New("invalid remote import")
ErrDownloadRemoteImport = errors.New("failed to download remote import")
ErrCacheDirectoryCreation = errors.New("failed to create cache directory")
ErrClearCache = errors.New("failed to clear cache")
ErrInvalidOverridesSection = errors.New("invalid overrides section")
ErrInvalidTerraformOverridesSection = errors.New("invalid terraform overrides section")
ErrInvalidHelmfileOverridesSection = errors.New("invalid helmfile overrides section")
Expand Down Expand Up @@ -638,6 +642,7 @@ var (
ErrCacheLocked = errors.New("cache file is locked")
ErrCacheRead = errors.New("cache read failed")
ErrCacheWrite = errors.New("cache write failed")
ErrCacheFetch = errors.New("failed to fetch content for cache")
ErrCacheUnmarshal = errors.New("cache unmarshal failed")
ErrCacheMarshal = errors.New("cache marshal failed")
ErrCacheDir = errors.New("cache directory creation failed")
Expand Down
3 changes: 2 additions & 1 deletion examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ Think of each demo folder as representing an example of a standalone repository.
9. ├── demo-context/ # Simplify resource naming and tagging with our Terraform context provider.
10. ├── demo-workflows/ # Automate repetitive tasks with streamlined workflows.
11. ├── secrets-masking/ # Configure custom patterns and literals for automatic secrets masking in output.
12. └── stack-names/ # Demonstrate imperative stack naming with explicit 'name' field.
12. ├── stack-names/ # Demonstrate imperative stack naming with explicit 'name' field.
13. └── remote-stack-imports/ # Import stack configurations from remote URLs (HTTP, Git, S3, GCS).
```

## Playground
Expand Down
73 changes: 73 additions & 0 deletions examples/remote-stack-imports/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Remote Stack Imports Example

This example demonstrates how to import stack configurations from remote sources using [go-getter](https://github.com/hashicorp/go-getter) URL schemes.

## Overview

Atmos supports importing stack configurations from various remote sources:
- **HTTP/HTTPS URLs** - Raw files from web servers
- **Git repositories** - Using `git::` prefix or platform shorthand
- **S3 buckets** - Using `s3::` prefix
- **Google Cloud Storage** - Using `gcs::` prefix

## Example Structure

```
remote-stack-imports/
├── atmos.yaml # Atmos configuration
├── components/terraform/myapp/ # Simple mock component
└── stacks/
├── catalog/base.yaml # Local base configuration
└── deploy/demo.yaml # Stack with remote imports
```

## Try It

```bash
cd examples/remote-stack-imports

# View the stack configuration (includes remote imports)
atmos describe stacks

# Describe the component
atmos describe component myapp -s demo
```

## Remote Import Examples

### HTTP/HTTPS URL

```yaml
import:
- https://raw.githubusercontent.com/cloudposse/atmos/main/tests/fixtures/remote-imports/shared.yaml
```

### Git Repository

```yaml
import:
# HTTPS with specific ref
- git::https://github.com/acme/infrastructure.git//stacks/catalog/vpc?ref=v1.2.0

# GitHub shorthand
- github.com/acme/infrastructure//stacks/catalog/rds?ref=main
```

### S3 Bucket

```yaml
import:
- s3::https://s3.amazonaws.com/my-bucket/stacks/catalog/vpc.yaml
```

## Best Practices

1. **Pin Versions** - Always use `?ref=` with a specific tag or commit SHA for Git imports
2. **Cache Considerations** - Remote imports are cached locally
3. **Authentication** - Configure credentials for private repositories via environment variables
4. **Fallback to Local** - Consider vendoring critical imports for offline access

## Learn More

- [Stack Imports Documentation](https://atmos.tools/stacks/imports)
- [go-getter URL Formats](https://github.com/hashicorp/go-getter#url-format)
21 changes: 21 additions & 0 deletions examples/remote-stack-imports/atmos.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
base_path: "./"

components:
terraform:
base_path: "components/terraform"
apply_auto_approve: false
deploy_run_init: true
init_run_reconfigure: true
auto_generate_backend_file: false

stacks:
base_path: "stacks"
included_paths:
- "deploy/**/*"
excluded_paths:
- "**/_defaults.yaml"
name_template: "{{ .vars.stage }}"

logs:
file: "/dev/stderr"
level: Info
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Mock component for testing remote stack imports.
# This component doesn't provision any real resources.

variable "name" {
type = string
description = "Name of the application"
default = "myapp"
}

variable "environment" {
type = string
description = "Deployment environment"
default = "dev"
}

variable "imported_from" {
type = string
description = "Source of the import (local or remote)"
default = "unknown"
}

variable "remote_import_test" {
type = bool
description = "Flag to indicate this was imported from remote"
default = false
}

variable "shared_setting" {
type = string
description = "A shared setting imported from remote"
default = ""
}

output "name" {
value = var.name
description = "Application name"
}

output "environment" {
value = var.environment
description = "Deployment environment"
}

output "imported_from" {
value = var.imported_from
description = "Import source"
}

output "remote_import_test" {
value = var.remote_import_test
description = "Whether this was imported from remote"
}

output "shared_setting" {
value = var.shared_setting
description = "Shared setting from remote import"
}
12 changes: 12 additions & 0 deletions examples/remote-stack-imports/stacks/catalog/base.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Base configuration for the myapp component.
# This is a local import that provides component defaults.

components:
terraform:
myapp:
metadata:
type: abstract
component: myapp
vars:
name: "myapp"
environment: "base"
31 changes: 31 additions & 0 deletions examples/remote-stack-imports/stacks/deploy/demo.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Demo stack that imports from both local and remote sources.
#
# This demonstrates the remote stack imports feature:
# - Local imports work as before (catalog/base)
# - Remote imports are downloaded via go-getter and merged
#
# Note: The remote import URL points to a fixture file in the atmos repo.
# This example works once the remote-stack-imports feature is merged to main.

import:
# Local import - provides base component configuration
- catalog/base

# Remote import - fetches shared configuration from GitHub
# This URL points to the test fixture in the atmos repo
- https://raw.githubusercontent.com/cloudposse/atmos/main/tests/fixtures/remote-imports/shared.yaml

vars:
stage: demo

components:
terraform:
myapp:
metadata:
type: real
vars:
environment: "demo"
# These values will be overridden by the remote import:
# - imported_from: "remote"
# - remote_import_test: true
# - shared_setting: "value-from-remote"
18 changes: 18 additions & 0 deletions examples/remote-stack-imports/stacks/deploy/local-only.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Local-only stack for testing.
# This stack only uses local imports (no remote).

import:
# Local import only
- catalog/base

vars:
stage: local

components:
terraform:
myapp:
metadata:
type: real
vars:
environment: "local"
imported_from: "local"
6 changes: 4 additions & 2 deletions internal/exec/stack_processor_merge_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -407,15 +407,17 @@ func TestEffectiveAtmosConfig(t *testing.T) {
})

t.Run("later layer wins over earlier layer", func(t *testing.T) {
result := effectiveAtmosConfig(base,
result := effectiveAtmosConfig(
base,
map[string]any{"list_merge_strategy": "append"},
map[string]any{"list_merge_strategy": "merge"},
)
assert.Equal(t, "merge", result.Settings.ListMergeStrategy)
})

t.Run("empty string in later layer does not override earlier non-empty value", func(t *testing.T) {
result := effectiveAtmosConfig(base,
result := effectiveAtmosConfig(
base,
map[string]any{"list_merge_strategy": "append"},
map[string]any{"list_merge_strategy": ""},
)
Expand Down
Loading
Loading