github cloudposse/atmos v1.225.0

2 hours ago
feat(auth): add azure/interactive browser authentication provider @aknysh (#2862)

what

  • New azure/interactive provider kind: MSAL interactive browser authentication — authorization code + PKCE on a localhost redirect, the same flow az login uses (AcquireTokenInteractive). One command (atmos auth login) opens the browser, completes SSO, and mints Management/Graph/Key Vault tokens.
  • interactiveProvider embeds deviceCodeProvider, reusing the MSAL client, silent token acquisition (refresh tokens make repeat logins silent — no browser), token fan-out, and the Azure CLI cache write-back with the correct guest home account ID from #2861. Spec shape is identical to azure/device-code.
  • The shared machinery is parameterized by auth method: credentials persist auth_method: interactive, and the MSAL cache account_source mirrors az's own labels (authorization_code for the browser flow, device_code otherwise).
  • Docs: provider reference, Azure tutorial (interactive flow recommended for humans; device code reframed as fallback with the Conditional Access caveat), kind lists, blog post (azure-interactive-auth), shipped roadmap milestone, PRD (docs/prd/azure-interactive-auth.md), and the fix doc for #2861 (docs/fixes/2026-08-03-azure-cli-cache-corruption-guest-users.md).
  • Test coverage ≈92% of changed lines: two injection seams (acquireInteractive, checkInteractive) per the repo's DI convention, since the real interactive flow needs a live IdP and a browser; tests cover the success path, acquisition failure, and headless refusal against a sandboxed HOME.

why

  • Microsoft-managed Conditional Access policies now block the device code flow in many tenants, so azure/device-code increasingly fails; azure/cli requires a pre-existing az login session (two commands); azure/oidc is CI-only. Azure users had no one-command human login equivalent to aws/iam-identity-center.
  • The interactive browser flow carries full Conditional Access context (MFA, device state), so tenants allow it — and it uses the Azure CLI public client, which pre-authorizes localhost redirects.

why the name azure/interactive

"Interactive" is Microsoft's own term for this flow, not our invention:

  • MSAL's API for it is literally AcquireTokenInteractive, and Microsoft's flow taxonomy divides authentication into "interactive and non-interactive flows" — where "interactive" specifically means the browser-based authorization-code sign-in a user completes, as opposed to device code, silent, or client-credential flows.
  • Provider kinds in Atmos name the auth mechanism, not the UX: aws/iam-identity-center opens a browser too, but the kind names the mechanism; aws/saml treats the browser as a driver: option; gcp/workload-identity-federation names the federation mechanism. azure/interactive follows the same rule using the platform's own vocabulary.
  • Alternatives considered: azure/browser (self-explanatory but bakes UX into the kind name, contrary to the convention above) and a spec.flow: browser option on azure/device-code (avoids a new kind, but makes the kind name actively misleading when the flow isn't device code).

manual verification

Tested end to end in a real Entra tenant where the device code flow is blocked by a Microsoft-managed Conditional Access policy, with an operator who is a guest (B2B) user in that tenant. Starting from a fully wiped ~/.azure:

  1. One-command loginatmos auth login opened the default browser, SSO completed (no device code, no az login), and tokens were minted.
  2. Silent repeat login — running atmos auth login again succeeded without opening the browser (same token expiry), confirming refresh-token persistence.
  3. atmos auth whoami — reported provider, identity, subscription principal, tenant, and expiry.
  4. az CLI drop-inaz account show worked even though az login was never run, thanks to the Azure CLI-compatible cache write-back.
  5. Cache forensics — exactly one MSAL Account entry, labeled account_source: authorization_code (matching what az itself records), carrying the true home account ID. This is the guest-user case that used to corrupt the az cache before #2861.

Details in the PRD's Verification section (docs/prd/azure-interactive-auth.md).

references

Summary by CodeRabbit

  • New Features

    • Added Azure interactive browser authentication using authorization code + PKCE.
    • Supports silent sign-in from cached credentials, MFA, and Azure CLI compatibility.
    • Added the azure/interactive provider and browser sign-in prompts.
  • Bug Fixes

    • Prevented Azure CLI token-cache corruption for guest users.
    • Preserved authentication details across Azure sign-in methods.
  • Documentation

    • Updated Azure authentication guides, examples, and provider references.
    • Added guidance for Conditional Access scenarios, troubleshooting, and interactive authentication.
refactor: consolidate file locking @osterman (#2856)

what

  • Consolidate production file locking behind pkg/cache.FileLock, covering cloud configuration, Helm repositories, and workdir metadata.
  • Add explicit lock-path and nonblocking shared-read APIs with Unix and Windows coverage.

why

  • Preserve each caller's existing lock paths and timeouts while removing duplicated gofrs/flock acquisition and release logic.
  • Standardize Windows graceful degradation and prevent contended metadata reads from falling back to unlocked access.

references

  • None.

Summary by CodeRabbit

  • Bug Fixes

    • Improved reliability when credential, configuration, cache, repository, and metadata files are accessed simultaneously.
    • Added consistent lock timeouts and clearer handling when files are temporarily unavailable.
    • Preserved existing updates, cleanup behavior, and error reporting during file operations.
  • New Features

    • Added non-blocking read-lock support for safer access to shared files.
    • Improved coordination of file access across supported platforms.
docs: add succinct Gomplate datasource example @osterman (#2866)

what

  • Adds a concise "Example: Using a Gomplate Datasource" section to the Datasources documentation, showing how to configure a file:// datasource and reference its values from a component's vars.

why

  • Issue #2650 asked for a practical example of using Gomplate datasources.
  • Two independent PRs (#2748, #2651) were opened to address it, both adding a much longer, AWS-specific walkthrough. Neither was updated after review feedback asking for something more succinct.
  • This lands a minimal, general-purpose example directly, superseding both.

references

Summary by CodeRabbit

  • Documentation
    • Added an advanced example showing how to configure and use a Gomplate file datasource in a Terraform component template.
    • Documented rendering the component with atmos describe and viewing the resulting YAML output.
    • Clarified the distinction between Gomplate datasources and Atmos !include functions.
feat(store): add atmos store CRUD CLI and type: store workflow step @osterman (#2858)

what

  • Adds atmos store — a new CLI command group (set/get/delete/list) for raw CRUD access to any store backend configured under stores: in atmos.yaml (AWS SSM, AWS Secrets Manager, HashiCorp Vault, Azure Key Vault, GCP Secret Manager, Redis, Artifactory, 1Password, Keychain, GitHub Actions). Unlike atmos secret, no declaration is required — any key can be read, written, or deleted directly by name, optionally scoped to a stack and component.
  • Adds a type: store workflow step that writes a value from a workflow, custom command, or hook — usable automatically as a hook too via the existing generic kind: step bridge, with no extra wiring.
  • Both close the write-side gap next to the existing read-only !store/!store.get YAML functions, so pipeline metadata (an image tag from a build step, a build number, a deployment marker) can be handed off to a completely different stack or component.
  • Includes unit tests for the new pkg/store.Service facade, the cmd/store command family, and the store step handler, plus CLI/website docs, a changelog post, and a roadmap milestone.

why

  • Store backends previously supported reads only (!store/!store.get); the only existing write path was a single Terraform-output-specific hook (kind: store), so anything else needing to be written into a store meant scripting around Atmos with a cloud CLI.
  • This gives Atmos a native, declaration-free CRUD surface and a first-class workflow step for the common build → push → record-value → later-read pattern, without requiring the value to be formally declared as a secret.

references

  • N/A

Summary by CodeRabbit

  • New Features

    • Added experimental atmos store commands to set, get, list, and delete values across configured backends.
    • Added scoping, interactive or stdin input, deletion confirmation, raw output, multiple formats, key enumeration, and secret-value masking.
    • Added type: store workflow steps for writing templated values.
    • Added Terraform output and refresh lifecycle hooks for store integrations.
  • Documentation

    • Added CLI, configuration, workflow, hooks, and usage documentation with examples and backend limitations.
  • Tests

    • Added comprehensive coverage for store commands, workflow behavior, and Terraform hooks.
Add tfmigrate support for Terraform components @osterman (#2534)

what

  • Add atmos terraform migrate plan, apply, and list for running user-authored tfmigrate migrations in Terraform component context.
  • Add kind: tfmigrate lifecycle hooks with dynamic/static modes, toolchain resolution, Terraform/OpenTofu exec path wiring, and same-identity auth handling.
  • Export stack/component/workspace-scoped history variables and supported Terraform backend settings so users can configure durable tfmigrate history storage.
  • Update schemas, PRD, command docs, hook docs, roadmap, and changelog for the new migrate command family and history persistence limitation.

why

  • Terraform state migrations need to run after Atmos auth, source/workdir provisioning, generated files, init, and workspace selection so automation matches normal Terraform operations.
  • Rerun-safe automation depends on durable tfmigrate history storage, so Atmos now documents and exposes the values users need without taking on history persistence in v1.

references

Summary by CodeRabbit

  • New Features
    • Added experimental atmos terraform migrate plan, apply, and list commands.
    • Added tfmigrate lifecycle hooks with dynamic and explicit execution modes.
    • Added migration history support for local, S3, and GCS backends.
    • Added affected-component workflows and configurable migration output.
  • Bug Fixes
    • Improved hook validation, dry-run handling, terminal color precedence, and provider resolution.
    • Missing migration directories now safely produce no-op results.
  • Documentation
    • Added CLI guidance, migration patterns, advanced examples, and help pages.
  • Tests
    • Added comprehensive unit, integration, and end-to-end coverage.
feat(vendor): native component updater PR workflow @osterman (#2756) Atmos CI

validation

Manually tested end-to-end as a real user — following --help/docs, in isolated sandboxes, and against a real repository (cloudposse/infra-live):

  • Opened two real pull requests exercising both the default and the full vendor.ci.pull_request config surface (title/body templates, labels, draft, reviewers, assignees) — #1701, #1702 (left as drafts, not merged).
  • That testing surfaced and fixed 5 real bugs along the way:
    • --pull-request created a pull request but never printed its URL in the default table output.
    • atmos.yaml's vendor.update.*/vendor.ci.* config (groups, execution mode, PR title/labels/draft/reviewers) was silently ignored — read from the wrong viper instance instead of the parsed config.
    • --all double-counted every component when a repo vendors exclusively via component.yaml and leaves an unused component type (e.g. packer) unconfigured.
    • A pull request's link was discarded entirely when a post-creation step failed (hit for real: GitHub rejecting a review request from the PR's own author).
    • SBOM's oci-artifacts coverage entry always claimed "complete" regardless of whether any OCI artifact existed in the project.
  • Also manually verified atmos vendor verify/clean/--refresh-lock/--lock-enforcement (all three modes) and atmos sbom generate (CycloneDX vs. SPDX, NTIA mode, experimental gating, upload-outside-CI) in isolated sandboxes.
  • Added ATMOS_PRO_GITHUB_TOKEN (the token github/sts mints) to the Component Updater's GitHub token precedence, so atmos auth exec --identity <github-sts-identity> -- atmos vendor update --pull-request gets a token that triggers downstream Actions workflows on the PR it opens — unlike the default GITHUB_TOKEN, which GitHub excludes from re-triggering workflows.

references

  • Component Updater PRD

Summary by CodeRabbit

  • New Features
    • Added experimental atmos sbom generate with provenance/NTIA modes, SPDX/CycloneDX output, and optional CI artifact upload.
    • Added native atmos vendor update --pull-request workflows with component/group selection and deterministic PR publishing.
    • Added atmos vendor verify, atmos vendor clean, and vendor.lock.yaml drift protection.
    • Added lock refresh/enforcement options, semver-range resolution, and improved source provenance metadata.
  • Bug Fixes
    • Improved cancellation handling and transient OCI decompression recovery.
  • Documentation
    • Expanded SBOM, vendoring, lockfile, and component-updater guidance.
feat(workflow): support tags and labels selectors @zack-is-cool (#2857)

what

  • Enable --tags and --labels on atmos workflow, forwarding them to nested type: atmos steps alongside optional --stack.
  • Preserve selector forwarding for parallel and matrix workflow controls.
  • Document the feature, publish its changelog post, and add its workflow-roadmap milestone.

why

  • Target existing workflows by component metadata without duplicating workflows or reconstructing their commands manually.

references

validation

  • go test ./internal/exec -run TestExecuteWorkflow_ForwardsCommandLineFilters -count=1
  • go test ./pkg/workflow -run 'TestAppendAtmosStepFlags|TestControlCommandExecutorExecuteAtmos' -count=1
  • go test ./cmd/workflow -run TestWorkflowSelectorFlags -count=1
  • git diff --check

Summary by CodeRabbit

  • New Features

    • Added --tags and --labels selectors to workflow commands.
    • Selectors are forwarded to nested Atmos steps, including parallel and matrix workflows.
    • Selectors can be combined with --stack while preserving workflow ordering and execution behavior.
  • Documentation

    • Updated CLI references with selector options and usage examples.
    • Added workflow selector guidance to the blog and roadmap.
feat: add date-anchored default editions @osterman (#2762)

what

  • Add date-anchored edition: defaults, the --edition override, and atmos describe/list edition commands.
  • Journal default changes and add config, CLI, docs, snapshot, and cast coverage for edition-aware behavior.
  • Make describe component/dependents honor graceful YAML error handling without requiring implicit identity authentication.

why

  • Let projects upgrade Atmos without silently adopting later default changes, while giving operators visibility into effective defaults.
  • Avoid duplicate post-auth error output and let component inspection continue when recoverable YAML values cannot resolve.

references

  • N/A

Summary by CodeRabbit

  • New Features

    • Added experimental date-pinned configuration editions via --edition, ATMOS_EDITION, or atmos.yaml.
    • Added describe edition and list editions commands for inspecting default changes.
    • Added component mock support with --use-mocks.
    • Added configurable component filtering, provenance display, error handling, and help filtering.
  • Bug Fixes

    • Improved table sizing, terminal wrapping, whitespace, tree output, authentication handling, and validation exclusions.
    • Preserved clearer error details and prevented unintended authentication attempts.
  • Documentation

    • Added documentation and examples for editions and updated command and configuration references.
refactor(store): move backends into pkg/store/providers subpackage @osterman (#2575)

what

  • Move the concrete store backend implementations (AWS SSM, Azure Key Vault, Google Secret Manager, Redis, Artifactory) out of pkg/store into a new pkg/store/providers subpackage, keeping pkg/store as a pure interface/type boundary (the Store interfaces, StoreConfig/StoresConfig/StoreRegistry types, auth-config types, error sentinels, and generated mocks).
  • Introduce a self-registering registry: pkg/store owns StoreRegistry, NewStoreRegistry, and a Register(type, factory) API; each backend registers its factory from an init(), so the type switch is replaced by a map lookup. pkg/config blank-imports pkg/store/providers so the built-in backends register at startup (database/sql driver pattern).
  • Update call sites and add a pkg/store/providers exclusion to the provider-agnostic-auth depguard rule so its cloud-SDK imports are permitted (matching pkg/auth/providers).

why

  • Isolates the cloud-SDK-heavy backend code from the store contract, mirroring the established pkg/auth/providers / pkg/secrets/providers layout and making pkg/store a clean type/interface package.
  • The registry pattern removes the awkward split where the factory lived under providers but was named after the StoreRegistry type it returned; pkg/store now owns both the registry type and its construction, and adding a backend is one self-contained file that registers itself.
  • No user-visible change: identical store types resolve, unknown types still return ErrStoreTypeNotFound, identity warnings are preserved. Builds clean, all affected tests pass, and golangci-lint --new-from-rev=origin/main reports zero issues.

references

  • N/A

Summary by CodeRabbit

  • New Features
    • Storage backends now use a consistent registration model, improving support for configured providers and aliases.
  • Bug Fixes
    • Improved handling of missing configuration, invalid values, authentication failures, access errors, and unavailable data.
  • Tests
    • Expanded coverage for provider validation, key behavior, error scenarios, and concurrent registry operations.
  • Documentation
    • Clarified storage registry configuration in the schema.
  • Chores
    • Improved CI reproducibility and license-report generation.
feat(toolchain): add --format=plain/json to atmos toolchain get @osterman (#2845)

what

  • Adds a --format flag to atmos toolchain get with three modes: table (default, unchanged), plain (bare version string only), and json (structured output with tool/version/installed fields, or a full version list under --all).
  • Routes the new plain/json output through the data channel (stdout, pipeable) instead of the styled UI channel (stderr), via new printVersionsPlain/printVersionsJSON helpers in pkg/toolchain/get.go.
  • Rejects --format=plain combined with --all with a new ErrToolchainPlainFormatWithAllFlag sentinel, since there's no single version to print in that case.
  • Adds a changelog post and links it into the toolchain roadmap milestone.

why

  • Extracting a tool's version in scripts/CI previously required regex-scraping the human-styled table output (checkmark indicator, ANSI colors, 2>&1 since it's written to stderr), e.g. atmos toolchain get vale-cli/vale 2>&1 | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1.
  • --format=plain collapses that to version=$(atmos toolchain get vale-cli/vale --format=plain), and --format=json gives scripts structured access to installed status without adding a new dependency.

references

Summary by CodeRabbit

  • New Features
    • Added table, plain, and JSON output formats to atmos toolchain get.
    • Added --format/-f, environment-variable support, and shell completion.
    • Plain output provides version strings; JSON provides structured tool and installation details.
  • Bug Fixes
    • Added validation for unsupported formats and incompatible --all and plain output options.
  • Documentation
    • Updated command documentation, scripting examples, and roadmap information.
feat: dependency-closure selection (--include-dependencies/--include-dependents) + scope --labels/--tags evaluation before filtering @osterman (#2807)

what

Dependency-closure selection (new)

  • Every multi-component terraform selection (--all, --components, --query, -s, --tags, --labels, --affected) accepts two new depth-carrying flags on plan/apply/deploy/destroy/init:
    • --include-dependencies[=N] — also process everything the selection depends on (its prerequisites), N levels deep (bare flag = unlimited).
    • --include-dependents[=N] — the reverse direction. Previously a bool wired only into --affected; now works with every selection and accepts a depth (true/false remain accepted for compatibility).
  • Selectors choose the seed; closure flags expand it: closure members execute even when they don't match the selectors that seeded them, in dependency order (reversed for destroy), with cross-stack edges followed. destroy --include-dependencies warns that it also destroys shared prerequisites.
  • The same flags on atmos list components/stacks/instances preview the exact execution set a bulk run would touch.
  • Depth support in the graph engine: pkg/dependency.Filter gains per-direction depth bounds with a best-depth BFS; the scheduler adapter is restructured to seed-then-expand (tags/labels/query moved from post-filter to seed narrowing, per-node query skip suppressed when the seed already applied it).
  • The three-phase scoped evaluation built for list dependencies is generalized into dependencies.ResolveScopedClosure and shared with the terraform bulk paths, so closure runs fully evaluate only the stacks the closure touches.

Selector purity (new, by design)

  • metadata.tags/metadata.labels are selectors evaluated before auth/templating/YAML functions, so their values must be resolvable without authentication or process execution. Values using !terraform.state, !terraform.output, !store, !store.get, !secret, !aws.*, !emulator, !exec, !random, or template calls to atmos.Component/atmos.Store/atmos.GomplateDatasource/datasources are rejected with a by-design error and migration hint on any command that processes the manifest. Plain strings, simple templates, and local functions (!env, !git.*, !include) remain allowed.
  • Validation parses template actions with the configured delimiters (templates.settings.delimiters) via Go's template parser — no regex heuristics — and tags.SelectorUnresolved is now delimiter-aware too.

Scope-before-evaluate for --labels/--tags (original scope)

  • Early-skip scope check in the shared describe-stacks processor: a component excluded by --tags/--labels skips auth/template/YAML-function evaluation entirely, generalizing the existing -s/--stack early-skip. Now also threaded into list stacks/components/instances (previously they row-filtered after full evaluation).
  • atmos list dependencies computes a lightweight dependency graph, derives the reachable closure, and only fully evaluates the stacks that closure touches — re-converging against the resolved graph so templated same-stack dependency targets still resolve.
  • describe.settings.eager_evaluation remains the rollback switch forcing the old full-evaluation behavior.

Tests and docs

  • Unit tests across pkg/tags (purity + delimiter cases), pkg/dependency (depth/cycles/diamonds), pkg/scheduler/adapters (closure retains non-matching prereqs, destroy ordering, query suppression, depth merging), pkg/list/dependencies (scoped-evaluation convergence with a poisoned unrelated stack), and the list/cmd layers; regenerated help-text golden snapshots.
  • Docusaurus docs for all new flags (terraform + list commands), a Selector Purity section in the stack metadata docs, a changelog blog post (website/blog/2026-07-27-include-dependencies-closure.mdx), and a roadmap entry.

why

  • On a monorepo spanning multiple AWS accounts, atmos terraform ... --all --labels=.../--tags=... and atmos list dependencies --stack <stack> fully evaluated every stack in the repo (templates, YAML functions, auth/backend) before consulting the selector, so an unrelated account's unreachable backend failed the command.
  • Deploying "a stack and everything it needs" (or tearing down "a component and everything that depends on it") required hand-maintained bash wrappers around Atmos, even though the dependency graph and topological scheduler already existed — the selection just never followed the edges.
  • Making tags/labels drive scoping decisions before evaluation requires them to be cheaply resolvable; the purity contract makes that explicit design rather than a silent perf cliff.

references

  • Builds on docs/fixes/2026-06-22-describe-stacks-scope-and-cache-per-component-auth.md.
  • See docs/fixes/2026-07-25-scope-before-evaluate-labels-tags-list-dependencies.md for root-cause and verification detail of the scope-before-evaluate work (its Recommendations section is implemented by this PR's closure flags).

Summary by CodeRabbit

Release Notes

  • New Features

    • Added dependency and dependent expansion with optional depth limits and cross-stack relationships.
    • Added dependency-closure previews for component, stack, and instance selections.
    • Added tag and label filtering across list commands, including vendor tags.
    • Added levels output for shortest dependency distances.
    • Label filters now support key=value and key:value formats.
  • Improvements

    • Scoped evaluation skips unrelated components for filtered selections.
    • Added eager-evaluation controls for bulk selections.
    • Added clearer validation and warnings for unsupported flag combinations and dependency expansion during destroy operations.
    • Expanded CLI documentation and help text for new filtering and dependency options.
test(config): regression test for atmos.d commands merge (#2570) @osterman (#2840)

what

  • Add TestMergeConfig_AtmosDCommandsMerging_TopLevelYamlFunction to pkg/config/config_import_test.go, reproducing the exact minimal repro from #2570: two .atmos.d/ files where the last-processed file has a top-level Atmos YAML function (!repo-root) directly on a command's own field.

why

  • Confirms the reported "commands silently dropped" bug is already fixed incidentally by commit 0b1182c6bb (PR #2677, released in v1.223.0), which strips the commands key from file content before preprocessAtmosYamlFunc runs so it can no longer overwrite the already-merged commands array.
  • Adds a permanent regression guard pinning this exact shape so it can't silently reappear.
  • No production code changes — test-only.

references

fix(test): stop Windows toolchain-vanishing flake in acceptance tests @osterman (#2834)

what

  • Stops TestPrintTelemetryDisclosureOnlyOnce (and its three sibling telemetry disclosure
    tests) from deleting the shared <cache>/atmos root; they now isolate via a per-test
    ATMOS_XDG_CACHE_HOME/XDG_CACHE_HOME redirect instead.
  • Closes a TOCTOU race in internal/exec terraform/tofu version tests: RequireTerraformPath/
    RequireTofuPath now resolve the binary path once and hand it back directly, instead of a
    second independent exec.LookPath call at each call site.
  • Makes requireExecutablePath fail loudly (t.Fatalf) instead of silently returning an empty
    path when ATMOS_TEST_SKIP_PRECONDITION_CHECKS=true and the binary isn't found.
  • Isolates several pkg/toolchain install/uninstall/list tests from the real shared XDG
    toolchain cache directory (Toolchain.InstallPath now points at each test's own t.TempDir()).
  • Widens requireExecutablePath's retry window (2s → 15s) as a defensive backstop, and adds
    permanent forensic instrumentation (executableLookupForensics) that dumps per-PATH-entry
    toolchain directory state on a lookup failure.
  • Adds a docs/fixes/ entry recording the root cause and investigation.

why

  • The Windows "Acceptance Tests" CI job had been flaky for weeks with a recurring
    executable file not found in %PATH% failure for tofu/terraform, always on a different
    victim test in internal/exec each run.
  • Root cause (confirmed via the forensic instrumentation added here): TestPrintTelemetryDisclosureOnlyOnce
    was calling os.RemoveAll on the shared <cache>/atmos root — the same root the toolchain
    install path lives under since #2579 — twice per run (setup + defer), deleting every
    CI-provisioned tool (terraform, opentofu, helm, helmfile) out from under the other
    concurrently-running go test package binaries.
  • The other fixes in this PR are the real-but-secondary hazards found and closed during the same
    investigation, kept together because each one was ruled in/out as a contributing cause before
    the actual root cause was found.
  • Extracted out of #2812 (which bundles an unrelated CI git-clone-bootstrap feature) so this
    CI-reliability fix can ship and be reviewed independently.

references

ci: run required checks for GitHub merge queue @osterman (#2813)

what

  • Run the existing required test, CodeQL, CODEOWNERS, and symlink workflows for merge-group commits.
  • Run golangci lint for merge groups and keep CODEOWNERS validation safe when no pull-request payload is present.

why

  • GitHub merge queues require required checks to report on their synthetic merge commits before a queued pull request can merge.

references

Summary by CodeRabbit

  • Chores
    • Added merge queue support to automated security, testing, linting, CODEOWNERS, and symlink verification checks.
    • Ensured required validations run consistently for merge queue commits, including appropriate handling of CODEOWNERS checks.
Support parent-scoped multi-file stacks @osterman (#2787)

what

  • Support one logical stack across multiple top-level parent manifests without merging parent scopes.
  • Keep each parent's metadata.inherits graph self-contained while canonicalizing equivalent imported duplicates by lexical parent path.
  • Add the top-level stack composition PRD and regression fixtures for naming, conflicts, isolation, explicit imports, and invalid inheritance.

why

  • Multiple top-level files that represented layers of the same logical stack were previously rejected or treated independently.
  • This enables aggregate component discovery without implicitly making another parent's imports part of a manifest's inheritance dependencies.
  • Shared inheritance bases remain intentional and reviewable through each parent's normal import graph.

references

  • docs/prd/top-level-stack-composition.md

Summary by CodeRabbit

  • New Features

    • Added parent-scoped multi-file stack discovery: multiple top-level parent manifests with the same stack identity are composed into one logical stack.
    • Kept parent-specific imports/globals/locals/component config isolated while aggregating components across parents.
    • Enabled controlled component inheritance within the composed logical stack.
  • Bug Fixes

    • Duplicate component configurations now resolve deterministically.
    • Conflicting duplicates and unsupported peer-only inheritance are rejected with clearer diagnostics.
  • Documentation

    • Added a PRD and published a blog post on parent-scoped multi-file stacks.
    • Updated the public roadmap to mark the feature as shipped.
feat(stacks): support global-scope metadata defaults @osterman (#2808)

what

  • Adds support for a restricted allowlist of metadata fields (labels, tags, custom, enabled, locked, terraform_workspace_pattern) at the stack-manifest root, deep-merged into every component's own metadata as a stack-wide default.
  • Merge precedence, lowest to highest: global (stack-wide) → the metadata.inherits base-component chain → the component's own local metadata: block, which always wins.
  • Component-identity fields (component, inherits, type, name, terraform_workspace) remain component-only; setting one of these at global scope is now a hard validation error, both at runtime and via the manifest JSON Schema, instead of a silent no-op.
  • Updates metadata.mdx docs and adds a changelog post explaining the new global scope.

why

  • A stack-wide metadata: block (e.g. in _defaults.yaml) was previously accepted by the schema but never applied — metadata.labels/metadata.tags/etc. set there silently did nothing, which is worse than an error, since users had no signal their config wasn't taking effect.
  • Sharing labels, tags, or a stack-wide lock/enable flag across every component in a stack required copy-pasting the same metadata block into each component definition instead of declaring it once.

references

Summary by CodeRabbit

  • New Features

    • Added stack-root metadata: defaults that are deep-merged into each component’s metadata.
    • Enforced 3-tier precedence: stack defaults → metadata.inherits chain (when enabled) → component-local metadata.
  • Validation & Tests

    • Added schema allowlisting for stack-scope metadata (only labels, tags, custom, enabled, locked, terraform_workspace_pattern).
    • Expanded tests to cover precedence, custom component behavior, and new error paths for invalid global/identity fields.
  • Documentation

    • Updated component metadata docs and added a blog post explaining scope, allowed keys, and merge behavior.
Clarify Atmos CI concurrency guidance @osterman (#2798)

what

  • Add a concise warning about using GitHub Actions concurrency around Atmos/Terraform commands.
  • Fix Markdown indentation in the modernization skill so affected validation passes.

why

  • Concurrency groups are not a FIFO deployment queue, and cancellation can interrupt Terraform work.

references

Summary by CodeRabbit

  • Documentation
    • Clarified GitHub Actions concurrency behavior for Atmos/Terraform runs, including in-progress vs pending handling and pending-run eviction.
    • Added warnings that cancel-in-progress: true can cancel an in-flight Terraform apply and potentially leave remote state locks requiring manual recovery.
    • Documented queue: max limits (up to 100 pending) and noted it can’t be combined with cancel-in-progress.
    • Recommended explicit promotion/deployment workflows for strict execution ordering and updated the modernization checklist wording/formatting.

🚀 Enhancements

fix(output): render concurrent carriage-return updates safely @zack-is-cool (#2860)

what

  • Keep concurrent component output readable when underlying tools emit carriage-return progress updates.
  • Serialize prefixed stdout and stderr writes to their shared terminal.
  • Disable animated output-lookup spinners while concurrent Terraform work is running.
  • Add regression coverage for carriage-return handling and nested spinner suppression.

why

  • Concurrent writers and terminal redraw controls can otherwise reposition the cursor or interleave output, corrupting rendered lines.

references

validation

  • go test ./pkg/scheduler/adapters ./pkg/io -count=1
  • go test ./pkg/terraform/output -run '^TestSuppressSpinnersRestoresNestedScopes$' -count=1
  • pre-commit run --files pkg/scheduler/adapters/terraform.go pkg/terraform/output/executor_utils.go pkg/terraform/output/spinner.go pkg/terraform/output/spinner_test.go

Summary by CodeRabbit

  • Bug Fixes

    • Improved line-prefixed output for Unix, Windows, and standalone carriage-return line endings.
    • Prevented partial lines from being lost during flushing or after write errors.
    • Prevented interleaving of Terraform standard output and error output during concurrent execution.
  • Improvements

    • Suppressed transient spinners and provisioning messages during streamed or concurrent output.
    • Improved spinner cleanup across successful runs, errors, and nested operations.
    • Standardized carriage-return output as newline-delimited, prefixed lines.
    • Routed hook output consistently through the appropriate component streams.
    • Improved synchronization for grouped and concurrent Terraform output.
fix(config): resolve git-root base_path for --config/--config-path @osterman (#2864)

what

  • Fixes pkg/config/load_config_args.go so that loading configuration via --config/--config-path also applies git-root discovery for an empty (or .) base_path, matching the plain auto-discovery flow in LoadConfig().
  • Adds a regression test (TestLoadConfigFromCLIArgs_AppliesGitRootBasePath) that reproduces the bug and verifies base_path now resolves correctly.
  • Bumps the fast-uri transitive dependency (website) from 3.1.4 to 3.1.5 to remediate a high-severity host-confusion vulnerability (GHSA-7p8r-x3mc-p8w7 / CVE-2026-18446), flagged by Dependabot after this branch was pushed.

why

  • loadConfigFromCLIArgs() never called applyGitRootBasePath(), unlike the main LoadConfig() auto-discovery path. As a result, a project with base_path: '' in atmos.yaml resolved correctly via plain auto-discovery but left base_path empty when the identical config was loaded via --config, breaking component/stack path resolution (e.g. atmos terraform test) with Error: failed to find import.
  • The fast-uri bump addresses a live Dependabot alert (a patch-level version bump, not blocked by .github/dependabot.yml's major-version ignore policy) surfaced automatically after pushing this branch.

references

Summary by CodeRabbit

  • Bug Fixes

    • Configuration loaded through command-line options now resolves an empty or "." base path to the Git repository root.
    • Configuration loading continues when repository root discovery encounters an error.
    • Improved ZIP extraction safety by blocking path traversal and preventing writes outside the intended destination.
    • Improved error reporting for ZIP directory and file creation failures.
  • Tests

    • Added coverage for configuration base-path resolution and ZIP extraction security and failure handling.
fix(auth): prevent Azure CLI cache corruption for guest users @aknysh (#2861)

what

  • Skip the Azure CLI cache write-back entirely when credentials originated from the azure/cli provider — az's own cache is authoritative, and writing back what came from az is what corrupted it.
  • Record the originating auth method on AzureCredentials (cli / device_code / oidc) so the write-back can be gated per provider kind.
  • Capture MSAL's real home account ID in the azure/device-code provider (silent and interactive flows) and use it in both Azure CLI cache writers (UpdateAzureCLIFiles and the provider-level updateAzureCLICache), falling back to the previous {oid}.{tenant} derivation when unavailable.
  • Replace the azure/subscription identity's field-by-field credential copy with a struct copy plus explicit overrides, and add a reflection-based regression test that fails if any future AzureCredentials field is dropped by the wrap.
  • Isolate TestNewMSALCache's default-path case from the real ~/.azure.

why

  • After atmos auth login, the Azure CLI cache write-back created an MSAL Account entry with home_account_id derived as {oid}.{target-tenant} and hardcoded account_source: "device_code". For guest (B2B) users the home tenant differs from the target tenant, so az ended up with two Account entries for the same username and failed every subsequent command with Found multiple accounts with the same username (azure-cli#20168) — including az account get-access-token, which the azure/cli provider itself shells out to. In other words, one atmos auth login broke both az and the next atmos login for any guest user.
  • Reproduced and verified end to end against a real tenant where the operator is a B2B guest: before the fix, az loginatmos auth login → az broken; after the fix, az stays healthy, the cache keeps exactly one Account entry, and the persisted credentials carry auth_method so the gate holds across credential caching.
  • The subscription identity's field-by-field copy silently dropped the new fields before they reached the cache writer (found only by the end-to-end test), which is why the copy is now structural and guarded by a reflection test.

references

Summary by CodeRabbit

New Features

  • Azure credentials now retain authentication method and account identity details.
  • Improved support for guest and cross-tenant Azure accounts during authentication and token caching.
  • Subscription-based authentication preserves provider credential settings while applying subscription-specific values.

Bug Fixes

  • Azure CLI authentication no longer unexpectedly modifies CLI credential cache files.
  • Corrected account identification and tenant details for guest-user authentication.
  • Improved cache path handling across different environments.
fix(scaffold): resolve relative write-target directories consistently @osterman (#2855)

what

  • Fixes atmos scaffold generate so a relative target directory (e.g. the CLI's own default ./my-project) works, instead of rejecting every file with path traversal not allowed.
  • validateWriteTarget in pkg/generator/engine/templating.go now resolves the write directory (realDir) through the same ResolveAndCleanBasePath helper already used for the target base (realBase), instead of a bare filepath.EvalSymlinks that stays relative for relative inputs.
  • Adds a regression test, TestProcessFile_RelativeTargetPath, covering a relative targetPath end-to-end (previous tests only exercised absolute t.TempDir() targets, so this case was never caught).

why

  • realBase was always absolutized before comparison, but realDir was resolved with a bare filepath.EvalSymlinks, which returns a relative path unchanged when given a relative input. Comparing an absolute path against a relative one never matched the containment check, so it fired as a false-positive path traversal on every write whenever the target directory was relative — including the command's own default target.
  • Absolute targets happened to work only because filepath.Dir(fullPath) was already absolute in that case, masking the bug.

references

Summary by CodeRabbit

  • Bug Fixes

    • Fixed file generation for relative target paths, such as ./my-project.
    • Improved path resolution while preserving containment and symlink safety checks.
  • Tests

    • Added coverage confirming generated files are written to the expected destination with the correct content.
fix(config): stop recommending deprecated stacks.name_pattern @osterman (#2842)

what

  • atmos aws eks update-kubeconfig and Spacelift stack-name generation now check stacks.name_template before falling back to the deprecated stacks.name_pattern, instead of only supporting the deprecated field.
  • Error messages in pkg/config, errors/errors.go, and pkg/helmfile/cluster.go that previously only pointed users at the deprecated fields now recommend name_template/cluster_name_template.
  • The Getting Started tutorial and the aws eks update-kubeconfig command help/docs no longer teach the deprecated name_pattern/cluster_name_pattern fields.
  • Converted all examples/, demo/, and non-backward-compat-test tests/fixtures scenarios from name_pattern to name_template (a handful of fixtures that specifically test the deprecated field, precedence, or backward compatibility were left untouched on purpose).
  • Added unit tests covering both the new name_template support and continued name_pattern backward compatibility for the two code paths that changed.

why

  • Investigating #2827 ("profile-merged stacks settings are not consistently applied") showed the profile-merge pipeline was already correct and consistent between describe config and list dependencies.
  • The actual bug was that stacks.name_pattern — deprecated in favor of stacks.name_template — was still treated as the primary/only stack-naming mechanism in a couple of code paths and in several error messages, which is what produced the reported inconsistency and general user confusion about which field to use.
  • name_pattern continues to work unchanged for backward compatibility; only the recommended path and documentation change.

references

Summary by CodeRabbit

  • New Features
    • Added Go-template support for stack, Spacelift stack, and EKS cluster naming using context variables.
  • Compatibility
    • Existing pattern-based naming remains supported as a deprecated fallback.
    • Template settings take precedence when both options are configured.
  • Documentation
    • Updated configuration guidance, examples, and error messages to promote template-based naming.
  • Tests
    • Added coverage for naming precedence, fallback behavior, template errors, duplicate names, and profile-based stack discovery.
fix(toolchain): accept semver release-candidate strings in --use-version @osterman (#2841)

what

  • Fix isValidSemver() in pkg/toolchain/version_spec.go so it accepts semver pre-release and
    build-metadata suffixes (e.g. 1.225.0-rc.3, 1.2.3+build.5), by delegating to the
    already-vendored Masterminds/semver/v3 library instead of a hand-rolled digit-only check.
  • Add regression tests for the fix in pkg/toolchain/version_spec_test.go (unit-level
    isValidSemver/ParseVersionSpec cases) and pkg/version/reexec_test.go (end-to-end at the
    --use-version entry point).
  • Add a fix record at docs/fixes/2026-07-31-use-version-release-candidate-semver.md.

why

  • atmos --use-version=1.225.0-rc.3 failed with invalid version output format, even though
    1.225.0-rc.3 is a spec-compliant semver string. The version parser split on . and required
    every part to be pure digits, which rejects any release-candidate/pre-release version.
  • Tracing the install pipeline confirmed the parser was the only place the bug lived — the rest
    of the install path already handles arbitrary explicit versions (including prereleases)
    correctly, so no other changes were required.

references

Summary by CodeRabbit

  • New Features

    • --use-version now accepts release-candidate versions such as 1.225.0-rc.3, including versions with build metadata.
    • Version specifications support standard semantic version formats while retaining support for latest.
  • Bug Fixes

    • Improved version validation to reject malformed or overly specific versions and correctly handle pre-release identifiers.
  • Documentation

    • Added guidance covering supported semantic version formats and release-candidate usage.
fix(dag): stop concurrent map crash in bulk terraform commands @osterman (#2831)

what

  • Fix a fatal error: concurrent map iteration and map write crash in DAG-scheduled bulk terraform commands (terraform <cmd> --all/--affected/--query) at higher --max-concurrency.
  • ProcessComponentConfig now shallow-clones the component section before any downstream code mutates it, so concurrent workers never write into the map tree owned by the shared FindStacksMap cache.
  • Apply the same shallow-clone-before-mutate fix to two adjacent cache-corruption sites in the describe-stacks processor (deleting imports, and terraform_workspace_pattern/terraform_workspace_template, from cache-owned maps in place).
  • Add regression tests (internal/exec/process_stacks_shared_cache_test.go) that fail pre-fix both deterministically and under -race.
  • Bump the brace-expansion pnpm.overrides (website) to 1.1.18/2.1.4, patching CVE-2026-14257 / GHSA-mh99-v99m-4gvg (high-severity DoS via unbounded expansion length), reported by Dependabot alert #261.

why

  • FindStacksMap caches processed stack config and returns it by reference on cache hits, shared across all goroutines within a process. ProcessStacks and mergeGlobalAuthConfig write top-level keys into that shared component section, while findComponentInStacks has every DAG worker iterate every stack's component section (not just its own) looking for a match — so one worker's write races with another worker's read/iteration of the same cached map, crashing exactly as reported.
  • The describe-stacks processor had the identical hazard in two more places (both mutate cache-owned maps in place), corrupting the cache for every subsequent ProcessStacks call in the same process even outside the crash path.
  • The brace-expansion bump addresses an open, high-severity Dependabot alert; deferring it risks a DoS crash if attacker-influenced input reaches an affected glob/brace-pattern code path in the docs site tooling.

references

Summary by CodeRabbit

  • Bug Fixes

    • Prevented concurrency-related crashes when processing multiple stacks or components in parallel.
    • Preserved cached configuration data during stack and component processing.
    • Improved template handling for computed Terraform and Atmos sections.
    • Ensured generated Spacelift and Atlantis names are available during template evaluation.
    • Corrected describe output to include referenced imports consistently.
    • Improved propagation of configuration and template-processing errors.
  • Documentation

    • Clarified dependency advisory exceptions and their removal criteria.
    • Documented the concurrency crash fix and validation coverage.
Support explicit CI git checkout and bundle Docker CLI @osterman (#2812)

what

  • Add clone-local --ci and ATMOS_CI controls for no-argument CI checkout, including explicit opt-out behavior.
  • Replace hand-rolled argv/flag parsing in cmd/root.go for CI git-clone bootstrap detection with Cobra-identity + the existing cmd/git flag-handler infrastructure (resolveCICloneMode), removing ~140 lines of bespoke parsing.
  • Fix atmos git clone's no-arg CI checkout to resolve the branch from the CI provider's parsed short name instead of the raw ref, which previously failed real branch/PR checkouts.
  • Fix a Windows CI acceptance-test flake: a telemetry test was deleting the shared Atmos cache root mid-suite, which since #2579 also holds the toolchain install tree.
  • Add Docker CLI support to the official Atmos image and update CI, command, and modernization guidance.
  • Cover selector precedence, bootstrap gating, and invalid environment input.

why

  • Enables checkout bootstrap before repository configuration is available while preserving explicit control.
  • Keeps CI bootstrap detection consistent with the repo's flag-handler architecture instead of a parallel hand-rolled path.
  • The raw-ref checkout bug meant the documented CI checkout replacement for actions/checkout (docs/prd/git-ops.md) never actually worked for a real branch or PR.
  • The shared-cache-root deletion was causing multi-week Windows CI flakiness unrelated to this PR's own diff, blocking merge.
  • Removes the need for Docker installation steps in Atmos container jobs.

references

  • N/A

Summary by CodeRabbit

  • New Features

    • Added --ci support for atmos git clone, configurable with ATMOS_CI.
    • No-argument cloning can now automatically use the current CI checkout.
    • Explicit CLI settings take precedence over environment configuration.
  • Bug Fixes

    • Fixed CI checkout failures caused by using full ref paths instead of branch names.
    • Improved handling of invalid CI configuration values.
  • Documentation

    • Updated Git clone, GitHub Actions, and CI setup guidance.
    • Added notes covering Docker support and CI bootstrap behavior.
fix(config): stop silently dropping malformed atmos.d/.atmos.d files @osterman (#2837)

what

  • atmos.d/ and .atmos.d/ config files that fail to parse now hard-fail LoadConfig with the offending file path and YAML line number, instead of being silently swallowed at debug log level.
  • Applies uniformly to both the normal path (an atmos.yaml was found, its co-located atmos.d/.atmos.d is checked) and the zero-config fallback path (no atmos.yaml anywhere, opportunistic git-root .atmos.d check).
  • atmos version/--version, --help, atmos config validate/atmos validate config/atmos validate schema config, and CI git-clone bootstrap are unaffected — those commands already continue past config-init errors by design.
  • Added unit tests covering malformed YAML in atmos.d/ and .atmos.d/, the exact sort-order repro from the issue (good/broken/good files), and full LoadConfig integration tests for both call paths.
  • Added docs/fixes/2026-07-30-atmos-d-malformed-yaml-silent-drop.md documenting the fix.

why

  • Before this change, a YAML syntax error in atmos.d//.atmos.d/ caused Atmos to exit 0 with no visible error, and because the merge loop bails on the first bad file, every file sorting after the broken one silently never loaded either — whether a setting applied depended on its filename's sort position relative to an unrelated broken file.
  • This is inconsistent with every sibling config source: a malformed root atmos.yaml and malformed profile configs (#2825) already hard-fail. This closes the one remaining silent source, reusing the exact error already produced by the existing merge code (only the swallow points needed to change).

references

Speed up container discovery and harden emulator listings @osterman (#2828)

what

  • Cache automatic Docker/Podman selection, eliminate duplicate probes, and show progress during uncached discovery.
  • Fetch container statuses in one bulk runtime query and recover from stale cached runtimes.
  • Return an empty emulator status list when invoked outside an Atmos stack project.

why

  • Repeated container commands avoid unnecessary runtime checks while still recovering when a runtime changes.
  • List commands now respond predictably when no stack manifests are present.

references

  • None.

Summary by CodeRabbit

  • New Features

    • Added support for deleting cached entries to keep container/runtime selection up to date.
    • Container listings now compute instance status using a single bulk query for improved responsiveness.
  • Bug Fixes

    • Container runtime auto-selection is more resilient: corrupted cache data triggers fresh discovery, and failed runtime operations invalidate cached selections.
    • Emulator listing now returns an empty result (no error) when no stacks/manifests are found.
    • Improved runtime environment propagation when supported, and more reliable auto-start recovery between Docker and Podman.
fix: don't process Go templates in Terraform source code @thejrose1984 (#2830)

what

  • Exclude the component_info section from Go template rendering in both the
    ProcessStacks and describe stacks pipelines
  • component_info stays in the template context, so {{ .component_info.component_path }}
    and friends keep working in stack manifests
  • Add a regression test plus a terraform-source-go-templates fixture whose Terraform
    description contains {{project}}
  • Document the exclusion on the Templates page

why

  • Atmos parses a component's Terraform/OpenTofu source with terraform-config-inspect and
    stores the result — including every variable and output description — in
    component_info.terraform_config. That section is part of the component section Atmos
    serializes and renders as a Go template.
  • A description that legitimately contains double curly braces, such as the GCP resource
    name format projects/{{project}}/locations/{{location}}/services/{{name}}, aborted every
    Atmos command with template: templates-all-atmos-sections:156: function "project" not defined.
  • Terraform code is not Atmos configuration. Atmos templating belongs to the abstraction
    above Terraform modules, so this needs no new config knob — the section is simply never
    rendered.

references

Summary by CodeRabbit

  • Bug Fixes

    • Terraform-derived component_info content is no longer incorrectly processed as Go templates.
    • Terraform strings containing {{...}} and }} are preserved while other stack templates continue to render normally.
  • Documentation

    • Clarified that component_info is protected from template rendering but remains available for use in other templates.
fix(auth): never cache ambient provider credentials in the keyring @thejrose1984 (#2819)

what

Stops the auth manager persisting credentials to the keyring for providers
that re-resolve their principal from the environment on every authentication
(gcp/adc, gcp/workload-identity-federation, azure/cli, azure/oidc, github/oidc).
Such chains are never served from, nor written to, the keyring, and stale
entries are purged so poisoned keyrings self-heal.

why

gcp/adc is documented as stateless, but its short-lived token was cached and
replayed, so after gcloud auth application-default login switched accounts
Atmos kept authenticating as the previous principal. atmos/pro is deliberately
excluded: it mints a single-use token, so cached reuse is load-bearing.

references

Closes #2695
Suggested release label: patch

Summary by CodeRabbit

  • New Features
    • Ambient authentication providers now explicitly declare non-persisted credentials for fresh resolution from current environment on each login.
  • Bug Fixes
    • Prevented stale ambient credentials from being replayed from cache or written to the keychain.
    • auth logout, auth logout provider, and auth logout --all now clear ambient-related keyring entries even without --keychain.
    • Improved ambient-aware --dry-run logout previews to match real cleanup behavior.
  • Documentation
    • Updated ambient provider docs describing token/credential rotation and non-caching behavior.
  • Tests
    • Added expanded ambient-aware coverage across caching and identity/provider/logout-all flows.
fix: close yq/merge concurrency races, route yq logs through Atmos logger @osterman (#2826)

what

  • Route yq's internal (go-logging) diagnostics through the Atmos logger via a new internal/yq package, so they inherit Atmos's formatting, configured log destination, and secret masking instead of writing straight to stderr, unformatted and unmasked.
  • Centralize yq's process-global logger backend and expression-parser init in that same package, shared by both pkg/utils and pkg/yaml, closing a cross-package data race left over after #2822.
  • Fix an unrelated data race in pkg/merge.MergeContext.WithFile, where concurrent per-stack-file goroutines sharing a parent import chain could write into the same backing-array slot at the same time.
  • Bump the website's brace-expansion dependency (via pnpm.overrides) to patched versions, closing Dependabot alert #261.

why

  • yq processes YAML that can carry secrets, so letting its diagnostics bypass Atmos's masking-aware I/O layer was a real leak risk whenever Trace-level logging is enabled.
  • go-logging's SetBackend wraps any plain Backend in an unsynchronized, map-based type unless the backend itself implements Leveled. #2822's mutex-based fix for #2821 only covered pkg/utils, leaving pkg/yaml/edit.go free to mutate the same global state independently — confirmed with go test -race.
  • The MergeContext.WithFile race surfaced incidentally while validating the yq fix under -race (TestExecuteHelmfile_ComponentNotFound), and turned out to be a genuine, separate concurrency bug worth fixing here rather than leaving in place.
  • brace-expansion's expand() bounded the number of results but not their length, letting a small attacker-controlled input crash the Node process with an uncatchable out-of-memory error (CVE-2026-14257). The fix stays within the pinned major lines, so it isn't blocked by dependabot.yml's major-version-bump policy.

references

Summary by CodeRabbit

  • Bug Fixes
    • Improved concurrency safety when creating child merge contexts, preventing sibling interference.
    • Centralized yq diagnostics/logging so it routes to the configured destination, masks secrets, and remains properly silenced during YAML edits.
    • Improved yq evaluation concurrency and isolation with evaluation-scoped logging controls and one-time parser initialization.
    • Updated Podman lifecycle integration tests to skip when runtime start fails.
  • Tests
    • Added race-focused regression tests for merge-context siblings and yq concurrent evaluation/logging behavior.
    • Expanded unit tests for yq backend routing, masking, parser initialization, and evaluation scoping.
  • Documentation
    • Added fix notes for merge-context, yq diagnostics, and Podman test skipping.
    • Reduced CI link-check flakiness by excluding specific flaky GitHub blob URLs.
fix(config): validate invalid configuration @osterman (#2825)

what

  • Make built-in configuration validation run after configuration decoding fails, and include the affected file in parser errors.
  • Validate every YAML file discovered through recursive profile configuration discovery, including nested profile files.
  • Add coverage for command selection, fallback logging, schema validation, and profile discovery.

why

  • atmos config validate must diagnose invalid Atmos configuration instead of being blocked by the same decoding failure it is intended to report.

references

  • Zack profile/workdir reproduction.

Summary by CodeRabbit

  • Bug Fixes
    • Made built-in configuration validation commands run even when main configuration loading fails.
    • Excluded generated/irrelevant discovery fragments to prevent incorrect config merging.
    • Improved schema validation output by including the affected file path in errors.
    • Adjusted schema checks for the embedded built-in config schema so unnecessary checks are skipped.
  • Tests
    • Added/extended coverage for built-in config/schema matching, validation behavior, and profile stack override scenarios.
Add env-step export controls to workflows and hooks @osterman (#2814)

what

  • Add default-on process export control for type: env steps while preserving template assignment.
  • Propagate exported values through workflows, custom-command steps, and ordered step hooks.
  • Add behavior-focused regression coverage, documentation, and a fix record.

why

  • Separate template state from child-process state so template-only values are explicit and subprocess propagation is consistent.

references

Summary by CodeRabbit

  • New Features
    • Added an export option for workflow and task env steps to control whether values propagate to later child-process environments (default: true).
  • Bug Fixes
    • Improved env propagation and precedence so later steps can reliably resolve {{ .env.NAME }} and so exports are isolated across retries and ordered hooks.
    • Ensured export: false keeps values template-only (not set for later subprocess environments).
  • Documentation
    • Updated workflow/task and hook docs, plus schemas, to clarify scoping and precedence for env step exports.
  • Tests
    • Added unit and end-to-end coverage for propagation, template-only behavior, and hook/step isolation.
fix(container): apply Buildx driver and cache configuration @osterman (#2815)

what

  • Propagate resolved Buildx driver and cache settings from workflow container builds to Docker.
  • Support cache settings for Buildx Bake, reject ineffective non-Buildx cache/driver configuration, and stabilize builder option ordering.
  • Add workflow, component, command-argv, registry-cache integration, and CI coverage.

why

  • Prevent native workflow builds from silently using Docker's default builder and bypassing remote cache settings.

references

Summary by CodeRabbit

  • New Features

    • Added full propagation for Buildx driver and registry-backed build cache, including bake cache cache-from/cache-to.
  • Bug Fixes

    • Strengthened build configuration validation: driver/cache settings now require a buildx-compatible engine (unless using bake).
    • Improved Buildx builder creation behavior for consistent driver option ordering.
  • Tests / CI

    • Added remote registry cache integration coverage (gated) and expanded local/unit test coverage for Buildx args and cache wiring.
    • Enhanced fake Docker runtime verification and improved a few flaky test synchronizations.
fix(utils): initialize yq globals once instead of per evaluation @arcaven (#2822) Concurrent stack file processing can end an Atmos run with `fatal error: concurrent map writes`. That is a runtime fatal error rather than a panic, so the global handler added in #2334 cannot intercept it and no retry inside Atmos can either. `go test -race` flags the cause on `main` from a 20 line test, and the fix keeps the hot path read-only.

what

  • configureYqLogger no longer calls logging.SetLevel on every EvaluateYqExpression and EvaluateYqExpressionWithType call. The default level is installed in init(), and later calls rewrite it only when the wanted level is not already installed, under a mutex.
  • yq's process-global expression parser is initialized under a sync.Once rather than being lazily assigned by every Evaluate call.
  • Adds TestEvaluateYqExpression_ConcurrentCallsAreRaceFree, which fails under -race without either change.

why

Both globals were written on every evaluation, from the per stack file goroutines that processYAMLConfigFileWithContextInternal spawns:

  • logging.SetLevel writes an unsynchronized map inside go-logging, and yq reads that same map from every decoder through Logger.Debugf. The Go runtime checks concurrent map access, so this is the one that reaches users as a crash.
  • yqlib.InitExpressionParser assigns the exported global yqlib.ExpressionParser behind a plain nil check. That write is a pointer, so the runtime does not catch it and it corrupts quietly instead.

On unmodified main at fc4960a the new test reports Found 2 data race(s). With the change, pkg/utils, pkg/yaml/... and internal/exec all pass, the last two unchanged here but sharing the same yq globals.

Two notes for reviewers:

  • Installing the level in init() is what removes the write entirely for a non-Trace run. --logs-level Trace still performs one write on the first transition, which could in principle race a yq read already in flight. Hoisting the call to configuration load would close that as well; I did not want to reach into pkg/config uninvited.
  • pkg/yaml/edit.go:71 calls logging.SetLevel on every evaluateWithOptions, so it writes the same map. I have not measured whether that path runs concurrently, so I left it alone rather than guess. Happy to fold it in here, or to file it separately, whichever you prefer.

Cost: yqlib.InitExpressionParser measures about 0.4ms once, and the sync.Once keeps that off commands which never evaluate an expression.

references

  • Closes #2821
  • #2347 looks like the same class of failure in the same goroutine fan-out, for the shared merged context
  • #2334 added the global panic handler that cannot catch this one

Summary by CodeRabbit

  • Bug Fixes

    • Improved reliability when evaluating yq expressions concurrently.
    • Prevented unexpected yq logging changes during parallel evaluations.
    • Ensured expression parsing is initialized consistently before use.
  • Tests

    • Added coverage for concurrent evaluations, including result consistency and logging behavior.

Don't miss a new atmos release

NewReleases is sending notifications on new releases.