github cloudposse/atmos v1.224.0

5 hours ago
feat(terraform): add tflint hook and terraform lint command @osterman (#2675)

what

  • Add a built-in tflint hook kind that lints Terraform/OpenTofu on lifecycle events with zero configuration (kind: tflint, defaults to after.terraform.plan).
  • Add a standalone atmos terraform lint CLI 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 CommandEngine an opt-in CaptureStdout mode: since tflint --format=sarif writes SARIF only to stdout (no file-output flag), the engine redirects the tool's stdout into the existing ATMOS_OUTPUT_FILE side channel.
  • Wire tflint through the shared sarif result 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, a kind: tflint docs section in stacks/hooks.mdx, atmos terraform lint CLI docs, a changelog blog post, and a roadmap milestone.

why

  • terraform validate misses 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

Summary by CodeRabbit

  • New Features
    • Added atmos terraform lint with --all/--affected, --stack, --error-mode, --max-findings, and markdown/rich output.
    • Introduced built-in tflint hook kind and a tflint workflow step that consumes SARIF emitted to stdout.
    • Workflow steps now support an args field in addition to with.
  • Bug Fixes
    • Improved SARIF handling for missing/empty capture files and ensured stdout-emitted SARIF produces scan artifacts.
    • Fixed --identity=false Terraform lint to avoid evaluating unauthenticated YAML/function values.
  • Documentation
    • Added tflint/Terraform lint docs plus blog content and runnable hook/lint examples.
  • Style
    • Improved SARIF badge/link rendering (including plain mode) and made spinners safe to truncate.
test: cover Terraform same-stack dependency order @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 legacy settings.depends_on declarations through terraform 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[].name form 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 @osterman (#2771)

what

  • Add driver and cache fields to the native type: container build step (action: build)
  • driver selects/creates the Buildx builder instance (docker buildx create), supports a bare-string shorthand (driver: docker-container) or a full form with name, provider, and driver-specific opts (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 driver the same way it already rejects bake/buildx-only config
  • Adds the required blog post and roadmap milestone, and documents driver/cache plus three ways to avoid Docker Hub rate limits in the container step docs

why

  • The native container build step had no equivalent to docker/setup-buildx-action's driver-opts or docker/build-push-action's cache-from/cache-to, blocking migration of existing GitHub Actions Docker builds onto native type: container steps
  • Remote cache import/export needs a Buildx builder capable of exporting cache (the docker-container driver), 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.cache with cache.from and cache.to.
  • 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.cache examples and Docker Hub pull-rate mitigation guidance.
    • Added a blog post explaining Buildx cache and driver usage.
docs: deprecate legacy component dependency guidance @osterman (#2773)

what

  • Replace active settings.depends_on examples with canonical dependencies.components syntax 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_on page 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.components format across examples, including component, stack, type, and file/folder dependency listings.
    • Replaced/condensed the legacy settings.depends_on documentation with a dedicated migration guide and clarified common error handling.
    • Refreshed !append YAML documentation to append under dependencies.components.
  • 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.
Improve emulator workflows, Terraform execution, and CI reliability @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 binary for the tested artifact.
  • Validation Markdown fixtures follow the repository EditorConfig indentation policy.
  • Windows cache-test fixtures create .exe binaries where the toolchain does, preventing a platform-only acceptance failure.

Validation

  • go test ./tests -run '^TestCachedTestTool(BinaryNameForOS|BinaryPath|BinaryExists)$' -count=1
  • GOOS=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` @osterman (#2761)

what

  • Add a complete JSON Schema for atmos.yaml that 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 AtmosConfiguration field and every new YAML function constant; a corpus test validates all examples/** and demo/** configs plus this repository's own atmos.yaml and .atmos.d (which surfaced and fixed real gaps: map-form env, boolean pager, function-valued polymorphic fields).
  • Add atmos config schema [output-path], mirroring atmos stack schema, printing the embedded schema served at atmos://schema/atmos/config/1.0.
  • Seed a built-in config entry in the schemas registry so atmos validate schema validates atmos.yaml, atmos.d/**, and project-local profiles out of the box (overridable via schemas.config), add atmos config validate as a shorthand alias for atmos 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 validate as the symmetric alias for atmos validate stacks, and make pkg/filematch resolve literal (non-glob) patterns with a direct stat instead of walking the entire tree — atmos validate schema in 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_schema tool alongside atmos_validate_stacks) and the experimental LSP server (line-positioned schema diagnostics for atmos.yaml documents and fragments, with anyOf cascade noise filtered to the most specific violation); pkg/validator gains ValidateYAMLContent for 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.yaml has 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 atmos matched atmos.yaml/atmos.yml but validated them against the stack-manifest schema, and a gap where atmos validate schema silently skipped all validation in repositories without a stacks directory.
  • Editor integration works with one yaml-language-server modeline (or natively via atmos 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.yaml JSON Schema
  • closes #2731 — supersedes the hand-written schema in that PR (thanks @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 schema to print or write the generated atmos.yaml JSON Schema.
    • Added atmos config validate and atmos stack validate, plus built-in schema validation for atmos.yaml/atmos.d//local profile fragments via schemas.config override/disable.
    • Added atmos_validate_schema tool and LSP config-schema diagnostics with accurate line/column reporting.
    • Added native atmos validate ci / atmos ci validate workflow validation (including SARIF/annotations when enabled).
  • 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 @osterman (#1687)

what

  • Implement atmos init command for bootstrapping new Atmos projects from built-in templates
  • Implement atmos scaffold command with three subcommands:
    • atmos scaffold generate - Generate code from scaffold templates
    • atmos scaffold list - List available scaffold templates from atmos.yaml
    • atmos 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 into cmd/ packages
  • 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/init vs init command) 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/init branch
  • Uses command registry pattern from #1643
  • Follows established patterns from atmos terraform and atmos describe commands

Summary by CodeRabbit

New Features

  • Added atmos init command to initialize Atmos projects from templates with interactive and non-interactive modes
  • Added atmos scaffold command suite with generate, list, and validate subcommands 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 @osterman (#1834)

what

  • Eliminated painful CSV-based quote escaping in !terraform.state and !terraform.output functions
  • 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 @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.
  • 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.
  • Documentation
    • Updated CLI/config docs, plus added a release blog post and roadmap entry.
  • Chores
    • Ignore local .tool-versions.lock files.
fix(ci): prevent broken GitHub Action symlinks @osterman (#2760)

what

  • Remove the stale atmos-gitops Claude skill symlink.
  • Add a repository-wide verifier for Git-tracked symlinks, run in PR/main CI and pre-commit.
  • Add atmos fix symlinks to 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 plan could 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@v1 action staging path.

Summary by CodeRabbit

  • New Features

    • Added checks to verify that tracked symbolic links resolve correctly.
    • Added an atmos fix symlinks command 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 @osterman (#2753)

what

  • Document Atmos's native Component Updater around atmos vendor update and atmos 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 @osterman (#2749)

what

  • Consolidates the two hand-maintained atmos-manifest JSON Schema copies (the website copy at atmos.tools and 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 floating 1.0 copy.
  • Adds atmos stack schema [output-path], a real CLI subcommand that prints/writes the embedded schema, replacing a throwaway go run ./tools/gen-schema script used only during development.
  • Removes hardcoded schemas.atmos.manifest version pins from examples/fixtures that weren't demonstrating custom-schema usage.

why

  • The published schema was a single mutable file — upgrading the atmos binary could silently change what a pinned atmos.yaml validates 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 @sgtoj (#2785)

what

  • Capture successful output from named shell and atmos steps 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 shell and
    atmos output: 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 shell and atmos execution bypassed StepExecutor result 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/atmos command-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: none behavior for custom-command shell and
    atmos steps.
  • 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 shell and atmos is 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.
  • Bug Fixes
    • output: none now 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 @osterman (#2777)

what

  • Normalize component auth default-only identity markers before merging with active global/profile auth.
  • Ignore undefined default: false markers and reject undefined default: true markers 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

  • Default Auth Behavior PRD
  • Component Auth Default-Only Markers fix log

Summary by CodeRabbit

  • Bug Fixes

    • Improved component authentication configuration merging for identity default markers.
    • Prevented undefined default: false markers from creating invalid identities or causing initialization failures.
    • Added validation for undefined default: true markers 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 @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 = true are excluded from generated *.terraform.tfvars.json when 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.
  • 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

  • matchesTag in internal/exec/yaml_func_utils.go now 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): matchesTag only 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

Summary by CodeRabbit

  • Bug Fixes
    • Improved YAML !template tag 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.
  • Tests
    • Added unit coverage for !template handling with no-separator formats (e.g., inline array/object forms) and for positive/negative tag matching cases.
fix(config): resolve atmos.yaml imports relative to config dir @sgtoj (#2772)

what

  • Top-level import: entries in atmos.yaml now resolve relative to the directory containing that atmos.yaml, not the current working directory. Running a command from a subdirectory no longer breaks imports like import: [".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, the atmos_base_path provider parameter) keep their existing resolution semantics.
  • Base-path provenance is tracked across multiple --config files and through imported declarations, so a later --config file's imports anchor to the file that actually declared the effective base_path.
  • Nested imports (local and go-getter adapters) resolve a declared relative base_path against the importing file's directory via a shared helper.

why

  • mergeImports resolved the import base path with filepath.Abs(base_path), which anchors to the current working directory. With the common empty or dot-relative base_path (e.g. .), running atmos from a nested directory resolved import: globs against the subdirectory and failed to find them — for example, atmos.yaml at /repo/acme with import: [".atmos/commands/**/*"], invoked from /repo/acme/somepath, looked for .atmos/commands under somepath.
  • Imports should anchor to the atmos root (the directory of the discovered atmos.yaml), consistent with how base_path is 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=10 for 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_path is 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.
  • 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 @osterman (#2775)

what

  • Add ref:<name> and ref:main guidance to invalid --use-version errors.
  • Cover the bare main failure 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-version for selecting a Git branch or tag, resolved to the latest available build artifact.
  • Documentation
    • Updated version-format guidance and “Invalid version format” messaging to include ref:<name> (along with pr:NNNN, sha:XXXXXXX, and semver).
    • Refreshed the PRD sections covering security mitigations and testing strategy to reflect the new ref approach.
  • Bug Fixes
    • Improved the invalid version-spec error hint to explicitly show the supported ref:<name> format.
  • Tests
    • Adjusted unit tests to validate the revised error messaging for invalid version input.
fix(terraform): preserve sensitive output types when masking is disabled @osterman (#2770)

what

  • Make --mask=false opt 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 @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 USAGE heading 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 @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

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 @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 @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's steps — they previously failed with executable file not found in $PATH (exit 127).
  • BuildToolchainPATH now resolves each dependency's PATH entries through the same onedir-aware logic as atmos toolchain env, adding the nested .pkg/.../bin directory(ies) recorded in .atmos-onedir.json instead of the bare version directory.
  • Add toolchain.EntrypointDirsForVersion as the single source of truth for onedir entrypoint-directory resolution, shared by atmos toolchain env, the dependency PATH builder (custom commands, hooks, terraform/helmfile/packer, ansible), and ToolchainEnvironment.
  • Make ToolchainEnvironment dirs (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/.../bin per .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 env already 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 runs node --version now succeeds; a single-binary control (kubectl) keeps resolving.
Testing
  • New unit tests, with all net-new code covered (EntrypointDirsForVersion 100%, BuildToolchainPATH 100%, collectToolchainDirs 94.7%, addEntrypointDirs 100%):
    • TestEntrypointDirsForVersion_* (pkg/toolchain): onedir multi-dir, flat, not-installed.
    • TestBuildToolchainPATH_Onedir (pkg/dependencies): asserts exec.LookPath resolves the nested onedir entrypoint, flat kubectl still resolves, and a shared onedir dir is deduplicated. Verified this fails on pre-fix code and passes after.
    • TestNewEnvironment_OnedirDirs (pkg/dependencies): env.dirs onedir-complete, dedup + absolutize, primary-dir fallback.
  • go build ./..., go vet, custom golangci-lint (patch-scoped), and gofumpt are clean; consumer packages (hooks, ansible, helm, terraform/output) pass.
  • Cross-platform: .exe handling + filepath.Join. A Windows acceptance subset for ./pkg/toolchain/... and ./pkg/dependencies/... passed, including TestBuildToolchainPATH_Onedir, TestEntrypointDirsForVersion_*, and TestNewEnvironment_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 PATH unchanged when no resolved directories are available (including for empty PATH input).
  • Tests
    • Added coverage for onedir entrypoint directory behavior and BuildToolchainPATH edge cases (empty PATH handling).
fix(toolchain): repair dangling onedir symlink entrypoints (node) @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, whose corepack/npm/npx entrypoints are symlinks into a sibling ../lib/ tree:
    • node@<=24.10.0 (whose files: list leads with corepack) no longer fails the install with chmod .../bin/corepack: no such file or directory.
    • node@24.18.0 no longer reports success while leaving npm/npx dangling.
    • After install, node, npm, npx, and corepack all 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 via os.Stat (follows the link) and is chmod-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

  • createValidatedSymlinkForOS wrote the containment-validated resolved path as the symlink target. resolved is 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 of install_path.
  • Writing the archive's original relative linkname reproduces the upstream archive exactly and resolves correctly whether the install root is absolute or relative.
  • resolved is 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, and nodejs/node ships .cmd shims 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 exact chmod ... corepack: no such file or directory error. Also exercised on a GitHub-hosted windows-latest runner (pkg/toolchain/installer package 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.

Don't miss a new atmos release

NewReleases is sending notifications on new releases.