feat(terraform): add tflint hook and terraform lint command Erik Osterman (Cloud Posse) (@osterman) (#2675)
what
- Add a built-in
tflinthook kind that lints Terraform/OpenTofu on lifecycle events with zero configuration (kind: tflint, defaults toafter.terraform.plan). - Add a standalone
atmos terraform lintCLI command (--all/--affected/single-component targeting) that runs TFLint once per unique component even when it's used by multiple stacks, instead of repeating the same lint for every stack. - Teach the shared hook
CommandEnginean opt-inCaptureStdoutmode: sincetflint --format=sarifwrites SARIF only to stdout (no file-output flag), the engine redirects the tool's stdout into the existingATMOS_OUTPUT_FILEside channel. - Wire tflint through the shared
sarifresult handler so findings render in the terminal, Atmos Pro, and — in CI — as a step summary, inline PR annotations, and a GitHub Code Scanning upload, exactly like the existing scanner kinds. - Ship the full release surface:
examples/hooks-tflint, akind: tflintdocs section instacks/hooks.mdx,atmos terraform lintCLI docs, a changelog blog post, and a roadmap milestone.
why
terraform validatemisses lint-class problems (unused declarations, deprecated syntax, invalid provider/instance settings); Atmos had built-in security/cost scanner kinds but no linter kind.- tflint's stdout-only SARIF output was the one gap preventing full parity; a small, opt-in engine capture closes it without touching the other kinds (trivy/checkov/kics/infracost keep streaming stdout).
- Reusing the shared SARIF handler means tflint inherits the native-CI integration (annotations + Code Scanning) with no tflint-specific wiring.
- Components shared across many stacks would otherwise get linted once per stack; the standalone command dedupes by component so CI cost doesn't scale with stack count.
references
- Docs: Manage Lifecycle Events with Hooks (
kind: tflint) - Docs: atmos terraform lint
- Builds on: Scanner Findings as Inline PR Annotations and Code Scanning Alerts
- Example:
examples/hooks-tflint
Summary by CodeRabbit
- New Features
- Added
atmos terraform lintwith--all/--affected,--stack,--error-mode,--max-findings, andmarkdown/richoutput. - Introduced built-in
tflinthook kind and atflintworkflow step that consumes SARIF emitted to stdout. - Workflow steps now support an
argsfield in addition towith.
- Added
- Bug Fixes
- Improved SARIF handling for missing/empty capture files and ensured stdout-emitted SARIF produces scan artifacts.
- Fixed
--identity=falseTerraform lint to avoid evaluating unauthenticated YAML/function values.
- Documentation
- Added
tflint/Terraform lint docs plus blog content and runnable hook/lint examples.
- Added
- Style
- Improved SARIF badge/link rendering (including plain mode) and made spinners safe to truncate.
test: cover Terraform same-stack dependency order Erik Osterman (Cloud Posse) (@osterman) (#2776)
what
- Add a credential-free eight-component Terraform dependency fixture that mirrors the reported same-stack graph.
- Exercise canonical
component:,name:alias, and legacysettings.depends_ondeclarations throughterraform plan --all --dry-run -s <stack>. - Assert the execution summary contains only the selected stack and every prerequisite completes before its dependent starts.
why
- Prevent the scheduler from again diverging from dependency-list rendering for the
dependencies.components[].nameform addressed by #2755. - Preserve compatibility coverage for canonical and legacy dependency declarations without Terraform, credentials, or state backends.
references
feat(container): Buildx cache and driver config for build steps Erik Osterman (Cloud Posse) (@osterman) (#2771)
what
- Add
driverandcachefields to the nativetype: containerbuild step (action: build) driverselects/creates the Buildx builder instance (docker buildx create), supports a bare-string shorthand (driver: docker-container) or a full form withname,provider, and driver-specificopts(e.g. a mirrored BuildKit image)cache(from/to) imports/exports Buildx remote build cache on the plain (non-bake) build path, passed through as--cache-from/--cache-to- Builder creation is idempotent (a stable default name of
atmos, reused so create-if-missing and the builder's own cache persist across runs) and never mutates the ambient default builder — Atmos always selects it explicitly with--builder - Podman rejects
driverthe same way it already rejectsbake/buildx-only config - Adds the required blog post and roadmap milestone, and documents
driver/cacheplus three ways to avoid Docker Hub rate limits in thecontainerstep docs
why
- The native container build step had no equivalent to
docker/setup-buildx-action'sdriver-optsordocker/build-push-action'scache-from/cache-to, blocking migration of existing GitHub Actions Docker builds onto nativetype: containersteps - Remote cache import/export needs a Buildx builder capable of exporting cache (the
docker-containerdriver), and pointing that builder at a mirrored BuildKit image is the cleanest way to avoid Docker Hub rate limits without changing Dockerfiles or host-level daemon config
references
- Related docs:
/workflows/steps/type/container#driver,/workflows/steps/type/container#cache
Summary by CodeRabbit
-
New Features
- Added Buildx builder configuration (including named builder selection/creation) via
with.driver, supporting both shorthand and detailed driver options. - Added Buildx build cache import/export via
with.cachewithcache.fromandcache.to.
- Added Buildx builder configuration (including named builder selection/creation) via
-
Bug Fixes
- Podman runtime now fails earlier when a Docker Buildx driver is provided, with a clearer unsupported-driver error.
-
Documentation
- Updated container build step docs with
with.driver/with.cacheexamples and Docker Hub pull-rate mitigation guidance. - Added a blog post explaining Buildx cache and driver usage.
- Updated container build step docs with
docs: deprecate legacy component dependency guidance Erik Osterman (Cloud Posse) (@osterman) (#2773)
what
- Replace active
settings.depends_onexamples with canonicaldependencies.componentssyntax across docs, blogs, and public AI examples. - Move the Spacelift guide into Deprecated, add a native-CI warning, and redirect the former integration URL.
- Reduce the legacy
depends_onpage to migration guidance and remove active Spacelift promotion.
why
- Prevent new configurations from adopting deprecated dependency syntax or treating the legacy Spacelift integration as a recommended path.
references
Summary by CodeRabbit
-
Documentation
- Updated CLI, migration, and stack/dependency docs to use the
dependencies.componentsformat across examples, including component, stack, type, and file/folder dependency listings. - Replaced/condensed the legacy
settings.depends_ondocumentation with a dedicated migration guide and clarified common error handling. - Refreshed
!appendYAML documentation to append underdependencies.components.
- Updated CLI, migration, and stack/dependency docs to use the
-
Examples / Website
- Updated example stack manifests to declare inter-component dependencies via
dependencies.components. - Added a deprecated Spacelift reference and a documentation redirect to the deprecated page.
- Updated example stack manifests to declare inter-component dependencies via
Improve emulator workflows, Terraform execution, and CI reliability Erik Osterman (Cloud Posse) (@osterman) (#2755)
Summary
- Make emulator discovery and execution component-aware: scope inventory to configured stack components, restore interactive stack selection, add runtime filtering, and consistently resolve emulator identities, endpoints, and provider settings.
- Add Terraform component mocks across schema, execution, scheduler, and examples; improve component/stack provenance, affected discovery, graph state handling, workspace paths, and Terraform diagnostics.
- Improve secret initialization, container/composition/devcontainer/Helm workflow behavior, and the related CLI help, docs, snapshots, and screengrabs.
- Strengthen cross-platform testing and CI: preserve toolchain paths, make cache locking and cached executables Windows-safe, sanitize runner-specific snapshots, retry transient vendoring and bootstrap downloads, and keep all build artifacts produced through
atmos build. - Refresh examples, PRDs, skills, schemas, website documentation, and generated casts; also update the website websocket dependency for the reported security advisories.
CI reliability fixes
- Vendoring retries transient Git clone failures with bounded exponential backoff.
- The Atmos bootstrap download is retried while the build action continues to invoke
atmos build binaryfor the tested artifact. - Validation Markdown fixtures follow the repository EditorConfig indentation policy.
- Windows cache-test fixtures create
.exebinaries where the toolchain does, preventing a platform-only acceptance failure.
Validation
go test ./tests -run '^TestCachedTestTool(BinaryNameForOS|BinaryPath|BinaryExists)$' -count=1GOOS=windows GOARCH=amd64 go test -c -o build/precondition_cached_tools_windows.test.exe ./tests./build/atmos validate --affected --exclude 'tests/fixtures/**' --exclude '**/*.go' --format rich./build/atmos build binary --target=macos --version=test./build/atmos build version --target=macos
Generate atmos.yaml JSON Schema from code; add `atmos config schema` Erik Osterman (Cloud Posse) (@osterman) (#2761)
what
- Add a complete JSON Schema for
atmos.yamlthat is generated from the Go configuration structs (pkg/config/schema,go generate) — Go doc comments become schema descriptions, runtime-only fields are excluded, and YAML function (!include,!env, …), null, and scalar-string-list alternatives are injected so real configs validate. - Guard the schema so it can never go stale: a drift test regenerates in-process and byte-compares against the committed artifact; ratchet tests force classification of every new
AtmosConfigurationfield and every new YAML function constant; a corpus test validates allexamples/**anddemo/**configs plus this repository's ownatmos.yamland.atmos.d(which surfaced and fixed real gaps: map-formenv, booleanpager, function-valued polymorphic fields). - Add
atmos config schema [output-path], mirroringatmos stack schema, printing the embedded schema served atatmos://schema/atmos/config/1.0. - Seed a built-in
configentry in theschemasregistry soatmos validate schemavalidatesatmos.yaml,atmos.d/**, and project-local profiles out of the box (overridable viaschemas.config), addatmos config validateas a shorthand alias foratmos validate schema config, and stop the command from bailing out when no stacks directory exists — schema validation now works in config-only repositories. - Add
atmos stack validateas the symmetric alias foratmos validate stacks, and makepkg/filematchresolve literal (non-glob) patterns with a direct stat instead of walking the entire tree —atmos validate schemain this repository drops from ~6s to ~0.6s, so per-file results stream immediately. - Wire the schema into the AI/MCP server (new read-only
atmos_validate_schematool alongsideatmos_validate_stacks) and the experimental LSP server (line-positioned schema diagnostics foratmos.yamldocuments and fragments, with anyOf cascade noise filtered to the most specific violation);pkg/validatorgainsValidateYAMLContentfor unsaved editor buffers. - Publish floating (
.../atmos-config/1.0/...) and per-release pinned schema copies from the website workflows, and add command docs, a schemas reference section, MCP/LSP doc updates, a changelog post, and a roadmap milestone.
why
atmos.yamlhas grown a larger configuration surface than stack manifests but had no schema at all: typos and mistyped sections were silently ignored by the loader, and editors offered no completion or validation.- A hand-written schema (like the stack-manifest one) would rot as options ship; generating it from the exact structs Atmos decodes into makes drift impossible, with CI enforcing regeneration.
- Fixes a latent misdefault where
atmos validate schema atmosmatchedatmos.yaml/atmos.ymlbut validated them against the stack-manifest schema, and a gap whereatmos validate schemasilently skipped all validation in repositories without a stacks directory. - Editor integration works with one
yaml-language-servermodeline (or natively viaatmos lsp start), and pinned per-release URLs let teams upgrade the binary without silently absorbing schema changes — the same model the manifest schema adopted in #2749.
references
- closes #2351 — the request for a first-class
atmos.yamlJSON Schema - closes #2731 — supersedes the hand-written schema in that PR (thanks rennokki (@rennokki)!); generating the schema from the config structs means it can never drift from what the CLI actually accepts
- Builds on #2749 (consolidated atmos-manifest schema + per-release pins) and its floating/pinned publishing model
- Changelog:
website/blog/2026-07-16-atmos-yaml-schema.mdx; docs:/cli/commands/config/config-schema,/cli/configuration/schemas,/ai/mcp-server,/cli/commands/lsp/start
Summary by CodeRabbit
- New Features
- Added
atmos config schemato print or write the generatedatmos.yamlJSON Schema. - Added
atmos config validateandatmos stack validate, plus built-in schema validation foratmos.yaml/atmos.d//local profile fragments viaschemas.configoverride/disable. - Added
atmos_validate_schematool and LSP config-schema diagnostics with accurate line/column reporting. - Added native
atmos validate ci/atmos ci validateworkflow validation (including SARIF/annotations when enabled).
- Added
- Documentation
- Updated CLI, configuration, and blog docs for the new schema/validation commands, tooling, and editor integration.
- Chores
- Prod/preview deploys now generate schema artifacts and publish/pin versioned snapshots; schema drift is enforced by tests.
feat: implement atmos init and atmos scaffold commands Erik Osterman (Cloud Posse) (@osterman) (#1687)
what
- Implement
atmos initcommand for bootstrapping new Atmos projects from built-in templates - Implement
atmos scaffoldcommand with three subcommands:atmos scaffold generate- Generate code from scaffold templatesatmos scaffold list- List available scaffold templates from atmos.yamlatmos scaffold validate- Validate scaffold.yaml configuration files
- Refactor code organization to eliminate thin wrappers and improve maintainability:
- Rename
pkg/init/→pkg/generator/for clearer, more generic naming - Move
pkg/init/config/→pkg/project/config/for project-level operations - Consolidate template processing into
pkg/generator/engine/ - Move business logic from
internal/exec/directly intocmd/packages
- Rename
- Add comprehensive documentation (PRD, blog post, CLI reference)
- Add test coverage for new packages (76.9% for templates, 91.7% for filesystem, 95.7% for merge)
why
- User Experience: Users can now initialize Atmos projects in seconds instead of manually creating configuration files
- Developer Experience: Scaffolding accelerates development by generating boilerplate code from templates
- Code Quality: Refactoring eliminates confusing package names (
pkg/initvsinitcommand) and removes unnecessary indirection - Maintainability: Moving logic into
cmd/packages reduces layers and makes the codebase easier to navigate - Extensibility: Template-based approach allows users to create custom templates for their organization
references
- Related to initial POC work in
experiments/initbranch - Uses command registry pattern from #1643
- Follows established patterns from
atmos terraformandatmos describecommands
Summary by CodeRabbit
New Features
- Added
atmos initcommand to initialize Atmos projects from templates with interactive and non-interactive modes - Added
atmos scaffoldcommand suite withgenerate,list, andvalidatesubcommands for creating code and configuration from templates - Template support includes variable substitution, dry-run previews, and intelligent file merging for updates
Documentation
- Added comprehensive usage guides, examples, and Product Requirements Documents for template and scaffolding features
refactor: Replace CSV-based YAML function argument parsing Erik Osterman (Cloud Posse) (@osterman) (#1834)
what
- Eliminated painful CSV-based quote escaping in
!terraform.stateand!terraform.outputfunctions - Implemented a simple, grammar-aware function argument parser that exploits semantic properties of component/stack naming
- Updated documentation with clean syntax examples and YAML block scalar demonstrations
why
The CSV parser was the wrong tool for this job. It required double-quote escaping ("") to include JSON literals in expressions, creating ugly, hard-to-read YAML. The new parser uses a lightweight heuristic: component and stack names never contain spaces and never start with special characters (., |, [, {, ", '), while expressions always do. This enables clean, readable syntax without escaping.
references
- Addresses usability pain point for complex YAML function expressions
- Part of ongoing effort to improve YAML function syntax
Summary by CodeRabbit
- New Features
- Added consistent, grammar-aware argument parsing for YAML functions, including Terraform state/output, env, random, include, and store.
- Terraform functions now default to the current stack when none is provided.
- Syntax errors are clearer and position-aware.
- Documentation
- Updated Terraform function examples to use cleaner quoting (including YAML block-scalar examples for complex defaults).
- Bug Fixes
- Corrected Terraform-backed default expressions in quick-start examples (proper fallbacks and lookup behavior).
- Tests / CI
- Added parser coverage checks and Terraform example syntax validation to automated test runs.
feat(toolchain): parallel installs with safe locking Erik Osterman (Cloud Posse) (@osterman) (#2758)
what
- Install independent toolchain packages concurrently (default four), with parent-owned terminal rendering, lifecycle states, and byte-progress display.
- Add context-aware cross-process and in-process reader/writer locks for assets, installs, latest markers, lockfiles,
.tool-versions, and uninstall mutations. - Materialize legacy Cosign evidence locally, route it to compatible Cosign v2.6.1, serialize verifier execution, and retry only pre-verdict transport/process failures.
- Restore full package IDs in uninstall results and keep automatic dependency installs on the configured toolchain path.
why
- Parallel workers previously exposed shared verifier and mutable-file races; this keeps installs fast while preserving UI and data integrity.
- OpenTofu releases still publish legacy certificate/signature evidence, and Cosign v3 both deprecates that path and intermittently rejects valid Rekor evidence.
references
- N/A
Summary by CodeRabbit
- New Features
- Parallel, bounded toolchain installs with
--max-concurrency/toolchain.max_concurrency(requires ≥ 1), plus enhanced batch progress and download progress reporting. - Improved Cosign verification for legacy certificate/signature option formats.
- Parallel, bounded toolchain installs with
- Bug Fixes
- Safer concurrent installs/verifications via cross-process locking for caches, downloads,
.tool-versions, and tool lockfiles; serialized trust repair and improved macOS retry classification. - More reliable batch failure reporting and clearer tool-spec messaging.
- Safer concurrent installs/verifications via cross-process locking for caches, downloads,
- Documentation
- Updated CLI/config docs, plus added a release blog post and roadmap entry.
- Chores
- Ignore local
.tool-versions.lockfiles.
- Ignore local
fix(ci): prevent broken GitHub Action symlinks Erik Osterman (Cloud Posse) (@osterman) (#2760)
what
- Remove the stale
atmos-gitopsClaude skill symlink. - Add a repository-wide verifier for Git-tracked symlinks, run in PR/main CI and pre-commit.
- Add
atmos fix symlinksto run the verifier from any repository subdirectory. - Standardize repository-root custom commands on
working_directory: !repo-root .. - Upgrade every CI bootstrap pin to Atmos 1.223.0, which supports that YAML tag.
- Require the verifier status on
main.
why
- A dangling symlink prevented GitHub Actions from staging the Atmos action repository before
atmos plancould start. - Checking all tracked symlinks protects every action consumer from the same class of packaging failure.
- A consistent, supported working-directory mechanism avoids duplicated shell root-resolution and lets every CI workflow load the custom-command configuration.
It caused problems like this:
Download action repository 'actions/checkout@v6' (SHA:df4cb1c069e1874edd31b4311f1884172cec0e10)
Download action repository 'cloudposse/atmos@v1' (SHA:1c00575b4be0393764fd202c4f0fb8efaf2411a0)
Error: Could not find file '/home/runner/_work/_actions/_temp_abab9a8a-faa8-41be-b57a-2d057d929d26/_staging/atmos-1c00575b4be0393764fd202c4f0fb8efaf2411a0/.claude/skills/atmos-gitops'.
references
- No issue; fixes the broken
cloudposse/atmos@v1action staging path.
Summary by CodeRabbit
-
New Features
- Added checks to verify that tracked symbolic links resolve correctly.
- Added an
atmos fix symlinkscommand for manually running the validation. - Added automatic validation during commits and continuous integration checks.
-
Improvements
- Standardized repository-root execution for development and build commands.
- Updated automated workflows to use Atmos bootstrap version 1.223.0.
- Clarified build script usage and behavior.
Clarify Component Updater and correct changelog chronology Erik Osterman (Cloud Posse) (@osterman) (#2753)
what
- Document Atmos's native Component Updater around
atmos vendor updateandatmos vendor diff. - Move the legacy Component Updater GitHub Action reference into deprecated documentation with a migration notice.
- Align changelog post frontmatter and filenames to publication dates, and render timeline dates in UTC.
why
- Make the native vendor-update workflow discoverable while clearly deprecating the legacy action.
- Prevent stale source filenames and viewer timezones from showing an incorrect changelog chronology.
references
Consolidate atmos-manifest schema, publish per-release pins Erik Osterman (Cloud Posse) (@osterman) (#2749)
what
- Consolidates the two hand-maintained atmos-manifest JSON Schema copies (the website copy at
atmos.toolsand the copy embedded in the CLI binary) into a single source of truth: the embedded schema. - The website copy is no longer committed — it's gitignored and generated at build time from the embedded schema.
- Every Atmos release now publishes an immutable, version-pinned schema snapshot (
atmos.tools/schemas/atmos/atmos-manifest/<version>/atmos-manifest.json) alongside the existing floating1.0copy. - Adds
atmos stack schema [output-path], a real CLI subcommand that prints/writes the embedded schema, replacing a throwawaygo run ./tools/gen-schemascript used only during development. - Removes hardcoded
schemas.atmos.manifestversion pins from examples/fixtures that weren't demonstrating custom-schema usage.
why
- The published schema was a single mutable file — upgrading the
atmosbinary could silently change what a pinnedatmos.yamlvalidates against, with no way to pin to "the schema as of release X." - The website and embedded schema copies had quietly drifted apart (missing
dependencies,generate,provision,source, and dozens of workflow step fields in one or the other), so validation behavior differed depending on which copy you hit. - Schema export deserves a discoverable, real CLI command instead of an internal-only script nobody would find.
references
- Closes #2592
- Blog posts:
versioned-manifest-schema,atmos-stack-schema-command
🚀 Enhancements
fix(steps): expose shell and atmos outputs to later steps Brian Ojeda (@sgtoj) (#2785)
what
- Capture successful output from named
shellandatmossteps in custom
commands and workflows. - Expose trimmed
.value, masked stdout/stderr/exit-code metadata, and declared
outputs through.steps.<name>. - Evaluate declared outputs once after successful command execution, outside the
retry envelope. - Support host execution, persistent workflow containers, and per-step container
overrides. - Preserve legacy runners, retries, live streaming, custom-command
shelland
atmosoutput: none, terminal sessions, process cleanup, and failure
propagation. - Document command-result behavior and correct the interactive-session capture
guidance. - Add regression coverage for markdown consumers, retries, masking, suppression,
terminal sessions, output-template failures, and container execution.
why
- Legacy
shellandatmosexecution bypassedStepExecutorresult storage. - Later steps therefore failed with
map has no entry for key "<name>", even
after the producer completed successfully. - Retaining the legacy execution paths avoids known Windows regressions and
child-process cleanup issues while satisfying the documented step-output
contract. - Captured values are masked before entering template context so later rendering
cannot expose registered secrets. - Keeping output evaluation outside retries prevents a template error from
repeating a successful, potentially non-idempotent command.
validation
- Audited against the step-output and
shell/atmoscommand-step PRDs. - Patch-scoped coverage reported no uncovered added behavior.
- Passed complete tests for
./cmd,./internal/exec,./pkg/runner/step, and
./pkg/workflow. - Passed focused race tests with shuffled ordering.
- Verified masking and
output: nonebehavior for custom-commandshelland
atmossteps. - Passed
go build ./.... - Passed patch-scoped lint against
upstream/main. - Built the Docusaurus website successfully.
- Re-ran the original custom-command and workflow reproductions successfully.
- Full-repository test wrappers reached and passed every touched package;
unrelated AWS/XDG assertions and an acceptance-test timeout caused the overall
local run to remain nonzero.
references
Summary by CodeRabbit
- New Features
- Step output capture for
shellandatmosis now consistent across local and container execution, including retries and named declared outputs (value, stdout, stderr, exit code). - Secret masking is applied to both live output and stored step metadata.
- Step output capture for
- Bug Fixes
output: nonenow suppresses streamed output while preserving stored results for downstream steps.- Output-template evaluation failures no longer cause retries and prevent storing a step result.
- Retry behavior now ensures only the final successful attempt’s stored outputs are used.
- Interactive/TTY sessions still attach directly to the terminal and remain non-capturable.
- Documentation
- Updated workflow step output documentation and added a fix note for command output availability.
fix(auth): normalize component default markers Erik Osterman (Cloud Posse) (@osterman) (#2777)
what
- Normalize component auth default-only identity markers before merging with active global/profile auth.
- Ignore undefined
default: falsemarkers and reject undefineddefault: truemarkers with a targeted configuration error. - Add regression coverage plus the Default Auth Behavior PRD and fix log.
why
- Prevent a harmless false-only marker from becoming an incomplete runtime identity that fails authentication initialization.
- Preserve real component identity definitions and deterministic default selection.
references
Summary by CodeRabbit
-
Bug Fixes
- Improved component authentication configuration merging for identity default markers.
- Prevented undefined
default: falsemarkers from creating invalid identities or causing initialization failures. - Added validation for undefined
default: truemarkers with clearer configuration errors. - Preserved existing behavior for complete identity declarations and valid global identities.
- Ensured invalid configurations do not modify existing global authentication settings.
-
Documentation
- Added guidance covering default-marker behavior, validation rules, and expected configuration outcomes.
Prevent sensitive YAML function value leaks Erik Osterman (Cloud Posse) (@osterman) (#2783)
what
- Keep Terraform-declared sensitive inputs out of generated JSON varfiles and pass them through
TF_VAR_*during execution. - Block degraded
(computed)values from Terraform handoff paths and add function-aware debug logging for graceful YAML-function degradation. - Add a generic producer/consumer regression fixture covering explicit-tag preservation, resolved state values, and JSON/environment transport.
why
- A resolved value from a sensitive YAML function could bypass masker-derived detection, be written to a varfile, or be transported as lookup arguments instead of the resolved value.
references
- N/A
Summary by CodeRabbit
- Security
- Terraform inputs marked
sensitive = trueare excluded from generated*.terraform.tfvars.jsonwhen masking is enabled, including--with-secrets. - Their resolved values are passed to Terraform via
TF_VAR_<name>environment variables (not written to disk). - Masking-disabled behavior preserves prior JSON compatibility.
- Terraform inputs marked
- Bug Fixes
- Execution, shell setup, and varfile generation now reject unresolved “(computed)” Terraform values before handoff (including nested maps/slices).
- Sensitive root-module handling is improved using module metadata.
- Diagnostics
- Added debug logging for recoverable YAML function parsing errors with function name and context.
- Tests
- Added regression tests plus a new sensitive-state fixture covering explicit YAML tags and non-leakage.
fix(yaml): accept non-whitespace boundaries after YAML function tags zack-is-cool (#2779)
what
matchesTagininternal/exec/yaml_func_utils.gonow accepts any character that can't extend a tag name (not just whitespace) as a valid boundary after a YAML function tag.
why
- Regression introduced in the unsupported-YAML-tag validation work (#1515):
matchesTagonly treated space/tab/newline as a valid separator after a tag name. - When Go template rendering trims the separating whitespace (e.g. a
{{-trim marker eating the newline after!template), the rendered value becomes!template["one","two"]with no separator. The tag no longer matched and the value silently fell through as a literal string instead of being decoded, breaking!template(and any other YAML function hitting the same boundary) for anyone whose templates produce output flush against the tag.
references
- Closes #2778
Summary by CodeRabbit
- Bug Fixes
- Improved YAML
!templatetag recognition when the tag is directly adjacent to its content (no whitespace separator). - Prevented incorrect tag matches by tightening boundary detection, including correct behavior around punctuation.
- Improved YAML
- Tests
- Added unit coverage for
!templatehandling with no-separator formats (e.g., inline array/object forms) and for positive/negative tag matching cases.
- Added unit coverage for
fix(config): resolve atmos.yaml imports relative to config dir Brian Ojeda (@sgtoj) (#2772)
what
- Top-level
import:entries inatmos.yamlnow resolve relative to the directory containing thatatmos.yaml, not the current working directory. Running a command from a subdirectory no longer breaks imports likeimport: [".atmos/commands/**/*"]. - Anchoring is scoped to config-sourced empty and dot-relative base paths (
"",.,./,..,../…). Bare paths (foo), absolute paths,!cwd, and runtime overrides (ATMOS_BASE_PATH,--base-path, theatmos_base_pathprovider parameter) keep their existing resolution semantics. - Base-path provenance is tracked across multiple
--configfiles and through imported declarations, so a later--configfile's imports anchor to the file that actually declared the effectivebase_path. - Nested imports (local and go-getter adapters) resolve a declared relative
base_pathagainst the importing file's directory via a shared helper.
why
mergeImportsresolved the import base path withfilepath.Abs(base_path), which anchors to the current working directory. With the common empty or dot-relativebase_path(e.g..), runningatmosfrom a nested directory resolvedimport:globs against the subdirectory and failed to find them — for example,atmos.yamlat/repo/acmewithimport: [".atmos/commands/**/*"], invoked from/repo/acme/somepath, looked for.atmos/commandsundersomepath.- Imports should anchor to the atmos root (the directory of the discovered
atmos.yaml), consistent with howbase_pathis resolved for config-file sources. - The change preserves the established source-aware convention (runtime/CLI/env dot paths → CWD; bare paths → git-root search;
!cwd→ CWD), so anchoring is limited to config-sourced empty and dot-relative values.
references
- Fix record:
docs/fixes/2026-07-20-config-imports-relative-to-config-dir.md. - Related base-path work:
docs/prd/base-path-resolution-semantics.md,docs/fixes/2026-03-17-failed-to-find-import-base-path-resolution.md. - Testing:
go build ./...;go test ./pkg/config/... ./internal/exec/... ./cmd/...; the new CWD-independence tests fail on the pre-fix code (import resolves against CWD) and pass after;go test ./pkg/config ./pkg/config/adapters -shuffle=on -count=10for registry isolation.
Summary by CodeRabbit
- Bug Fixes
- Resolved
import:globs and relative import paths relative to the declaring config file’s directory (not the process working directory). - Fixed how imported
base_pathis anchored and prioritized across nested imports and multi-file merges, including correct handling of CWD-anchored (!cwd) behavior and bare/empty base paths. - Improved resilience when nested import base paths can’t be resolved or provenance read fails—continuing without breaking the whole resolve.
- Resolved
- Documentation
- Documented the updated config-relative import behavior and compatibility rules.
- Tests
- Expanded validation for base-path resolution, glob/relative imports, merge precedence, and new error-path scenarios.
fix(version): clarify Git ref version override guidance Erik Osterman (Cloud Posse) (@osterman) (#2775)
what
- Add
ref:<name>andref:mainguidance to invalid--use-versionerrors. - Cover the bare
mainfailure path and update the related blog and implementation documentation.
why
- Branch and tag override support already exists, but the stale hint and format lists made it appear unsupported.
references
- N/A
Summary by CodeRabbit
- New Features
- Added
ref:<name>to--use-versionfor selecting a Git branch or tag, resolved to the latest available build artifact.
- Added
- Documentation
- Updated version-format guidance and “Invalid version format” messaging to include
ref:<name>(along withpr:NNNN,sha:XXXXXXX, and semver). - Refreshed the PRD sections covering security mitigations and testing strategy to reflect the new
refapproach.
- Updated version-format guidance and “Invalid version format” messaging to include
- Bug Fixes
- Improved the invalid version-spec error hint to explicitly show the supported
ref:<name>format.
- Improved the invalid version-spec error hint to explicitly show the supported
- Tests
- Adjusted unit tests to validate the revised error messaging for invalid version input.
fix(terraform): preserve sensitive output types when masking is disabled Erik Osterman (Cloud Posse) (@osterman) (#2770)
what
- Make
--mask=falseopt out of sensitive Terraform/OpenTofu output registration, retaining structured values in generated JSON varfiles. - Add coverage for the default secret-safe
TF_VAR_*path and the disabled-masking compatibility path. - Document the resulting disk-exposure tradeoff.
why
- Environment variables are strings, so untyped structured values cannot retain their object shape when sensitive-output registration routes them through
TF_VAR_*.
references
Closes #2768
Summary by CodeRabbit
- Bug Fixes
- When secret masking is disabled, sensitive Terraform/OpenTofu outputs keep their original structured (object/typed) form.
- Masking/secret-safe handling is applied only when masking is enabled; with masking off, secret-safe
TF_VAR_handling may not apply and values can appear in generated.tfvars.json.
- Documentation
- Added clearer warnings about the behavior and security implications of disabling masking, and improved formatting of a logging-related example.
- Tests
- Added regression coverage for masking-disabled behavior to prevent unintended transformations.
fix(cast): record help output once Erik Osterman (Cloud Posse) (@osterman) (#2774)
what
- Record help casts through the masked I/O stream once, and cover the lifecycle and rendered help output with regression tests.
- Regenerate CLI screengrabs, validate one
USAGEheading per help cast, and add missing MCP help assets.
why
- The help-specific recorder tee duplicated every rendered help block in docs casts.
- Complete, validated screengrabs prevent both duplicate payloads and broken documented embeds.
references
- None.
fix(toolchain): fail incomplete parallel installs Erik Osterman (Cloud Posse) (@osterman) (#2769)
what
- Treat missing terminal results in parallel toolchain installs as failures.
- Add regression coverage for a prematurely closed event channel.
why
- Prevents incomplete parallel installs from reporting a successful command.
references
- Closes #2767
Summary by CodeRabbit
- Bug Fixes
- Improved batch tool installation reporting when one or more installations do not return a final result.
- Failed installations are now counted accurately, and the reported error includes the number of failures.
- Tests
- Added coverage for incomplete batch installation results.
feat(cast): auto-install animated renderers Erik Osterman (Cloud Posse) (@osterman) (#2763)
what
- Auto-install pinned agg and FFmpeg renderers for animated cast outputs
- Resolve managed binaries to absolute paths while keeping static formats native
- Add renderer coverage and document automatic installation and MP4 palette constraints
why
- Remove the manual renderer setup requirement for GIF and MP4 cast rendering
references
- N/A
Summary by CodeRabbit
- New Features
- Animated GIF and MP4 rendering now automatically manages and installs the needed tools on first use.
- Bug Fixes
- Rendering now reports clearer failures and prevents overwriting existing animated outputs.
- Cached tool discovery on Windows is fixed (improves test/tooling reliability).
- Terminal-width fallback is more consistent in non-interactive environments by honoring
COLUMNS.
- Documentation
- Updated cast rendering and workflow docs to reflect managed installation and MP4-from-GIF behavior.
fix(toolchain): expose onedir command-dependency tools on step PATH Brian Ojeda (@sgtoj) (#2759)
what
- Fix onedir (multi-file) tools declared in a custom command's
dependencies.tools(e.g.nodejs/node,npm/cli,aws-cli) not being reachable from the command'ssteps— they previously failed withexecutable file not found in $PATH(exit 127). BuildToolchainPATHnow resolves each dependency's PATH entries through the same onedir-aware logic asatmos toolchain env, adding the nested.pkg/.../bindirectory(ies) recorded in.atmos-onedir.jsoninstead of the bare version directory.- Add
toolchain.EntrypointDirsForVersionas the single source of truth for onedir entrypoint-directory resolution, shared byatmos toolchain env, the dependency PATH builder (custom commands, hooks, terraform/helmfile/packer, ansible), andToolchainEnvironment. - Make
ToolchainEnvironmentdirs (ToolchainDirs()/PrependToPath()) onedir-complete — every entrypoint directory, not just the primary binary's. - Flat single-binary tools are unchanged (still their version dir); a not-installed tool falls back to the bare version dir (preserves existing behavior).
why
- Onedir installs (#2750) keep executables nested under
.pkg/.../binper.atmos-onedir.json, but the command-dependency PATH builder still prepended the bare version dir, so node/npm/aws-cli were never on PATH for steps. atmos toolchain envalready resolved these correctly; the command-dependency path simply wasn't reusing that resolution. Unifying both behind one helper makes onedir tools generally usable as command/hook/component dependencies.- This is the remaining gap after #2750 (onedir installs) and #2754 (onedir symlink materialization); this change is solely about how onedir command-dependency tools are exposed on PATH.
references
- Builds on #2750 (multi-file "onedir" installs) and #2754 (onedir symlink entrypoints).
- Repro: a custom command with
dependencies.tools: { nodejs/node: v24.18.0 }whose step runsnode --versionnow succeeds; a single-binary control (kubectl) keeps resolving.
Testing
- New unit tests, with all net-new code covered (
EntrypointDirsForVersion100%,BuildToolchainPATH100%,collectToolchainDirs94.7%,addEntrypointDirs100%):TestEntrypointDirsForVersion_*(pkg/toolchain): onedir multi-dir, flat, not-installed.TestBuildToolchainPATH_Onedir(pkg/dependencies): assertsexec.LookPathresolves the nested onedir entrypoint, flatkubectlstill resolves, and a shared onedir dir is deduplicated. Verified this fails on pre-fix code and passes after.TestNewEnvironment_OnedirDirs(pkg/dependencies):env.dirsonedir-complete, dedup + absolutize, primary-dir fallback.
go build ./...,go vet, customgolangci-lint(patch-scoped), andgofumptare clean; consumer packages (hooks, ansible, helm, terraform/output) pass.- Cross-platform:
.exehandling +filepath.Join. A Windows acceptance subset for./pkg/toolchain/...and./pkg/dependencies/...passed, includingTestBuildToolchainPATH_Onedir,TestEntrypointDirsForVersion_*, andTestNewEnvironment_OnedirDirs.
Summary by CodeRabbit
- New Features
- Improved tool discovery for onedir-style installations.
- Automatically prepends required entrypoint directories to the toolchain
PATH, deduplicated and normalized (absolute, order preserved). - Preserves flat-install behavior by falling back to the tool’s primary binary directory.
- Bug Fixes
- Ensures executable resolution from onedir layouts works reliably; returns
PATHunchanged when no resolved directories are available (including for emptyPATHinput).
- Ensures executable resolution from onedir layouts works reliably; returns
- Tests
- Added coverage for onedir entrypoint directory behavior and
BuildToolchainPATHedge cases (emptyPATHhandling).
- Added coverage for onedir entrypoint directory behavior and
fix(toolchain): repair dangling onedir symlink entrypoints (node) Brian Ojeda (@sgtoj) (#2754)
what
- onedir (multi-file) installs now recreate an archive's internal symlink entrypoints using the archive's original relative target (e.g.
../lib/node_modules/corepack/dist/corepack.js) instead of a root-rebased path. - Fixes
nodejs/node, whosecorepack/npm/npxentrypoints are symlinks into a sibling../lib/tree:node@<=24.10.0(whosefiles:list leads withcorepack) no longer fails the install withchmod .../bin/corepack: no such file or directory.node@24.18.0no longer reports success while leavingnpm/npxdangling.- After install,
node,npm,npx, andcorepackall resolve to real files and run.
- Adds a regression test that installs a leading-symlink onedir package under a relative
install_path(the real-world default, e.g.install_path: .tools) and asserts the entrypoint resolves viaos.Stat(follows the link) and ischmod-able — the exact operation that failed. - Updates the symlink-target unit assertions to expect the archive-relative target, and corrects the onedir docs note that claimed targets are "recreated absolute."
why
createValidatedSymlinkForOSwrote the containment-validatedresolvedpath as the symlink target.resolvedis the target rebased onto the toolchain root, so with a relative install root (the default.tools) it became a cwd-relative path. A symlink is interpreted relative to its own directory, so.../bin/corepack -> .tools/bin/.../lib/...resolved to.../bin/.tools/bin/.../lib/...and dangled. This reproduced independently ofinstall_path.- Writing the archive's original relative
linknamereproduces the upstream archive exactly and resolves correctly whether the install root is absolute or relative. resolvedis still used for the security containment check and the Windows copy/hard-link fallback, and absolute targets are still rejected — no change to the archive-symlink "arbitrary file write" protection.- Note: this is a POSIX-only change (macOS/Linux). Windows onedir installs don't create symlinks — an archive's file symlink is reproduced as a hard link/copy via that unchanged
resolved-based fallback, andnodejs/nodeships.cmdshims there — so Windows behavior is unaffected.
references
- Follow-up to #2750 (multi-file / "onedir" installs), which shipped the onedir install path but left
nodejs/node's internal symlink entrypoints dangling. - Testing:
go test ./pkg/toolchain/... ./cmd/toolchain/...passes; the new regression test fails on the pre-fix code with the exactchmod ... corepack: no such file or directoryerror. Also exercised on a GitHub-hostedwindows-latestrunner (pkg/toolchain/installerpackage green; symlink-privilege tests self-skip on Windows, Windows hard-link/copy fallback covered).
Summary by CodeRabbit
-
Bug Fixes
- Fixed onedir installations so archived relative symlinks resolve correctly when using absolute or relative install paths.
- Ensured symlink-based entry points correctly resolve to their target executables.
-
Documentation
- Clarified that archived symlinks retain their relative targets while remaining safely within the installation package.