github cloudposse/atmos v1.226.0-rc.5

pre-release3 hours ago
Add task-runner dependencies, freshness checks, and preconditions to custom commands and workflows @osterman (#2882) ## what
  • Adds dependencies.commands/dependencies.workflows to custom commands and workflows: named, parameterized, concurrent-by-default dependency ordering across units, with automatic dedup of identical invocations.
  • Adds inputs/artifacts step fields: skip a step when its declared sources haven't changed since the last successful run (implicit when: checksum.changed), exposing checksum.changed/timestamp.changed/sources/artifacts as when: CEL facts.
  • Adds preconditions step field: skip a step when a required tool is already on PATH (implicit when: "!preconditions.success"), resolved via exec.LookPath — no shell involved. Pluralized (preconditionpreconditions) to match the block-of-checks convention already used by inputs/artifacts/dependencies.
  • Adds continue: always step field, mirroring GitHub Actions' continue-on-error: a step's own failure is forgiven, later steps still run, overall exit status unaffected.
  • Fixes type: parallel/type: matrix steps silently failing in custom commands (only workflows supported them before).
  • Adds platforms via when: CEL facts (os/arch/platform), native per-command aliases:/internal:, and a values: constraint on flags/arguments with an interactive picker.
  • Fixes Windows-specific bugs in the new step types: shell child-process argument quoting for parallel/matrix, and verbatim CmdLine construction for cmd.exe /C.
  • Fixes a cluster of concurrency and correctness bugs surfaced during implementation and review: dependency-scheduling and freshness-check race conditions, hashfile collisions, non-atomic multi-line step output, diamond-dependency de-duplication, wrong-binary resolution in workflow command dependencies, and a UnitDependencies string-shorthand schema gap.
  • Remediates 3 Dependabot security alerts surfaced while this branch was open: js-yaml, mermaid, and a nanoid infinite-loop DoS (GHSA-2v37-7h3g-55p8 / CVE-2026-67213).
  • Relocates cmd/custom_command_dependency_adapter.go and cmd/custom_command_values.go into pkg/taskgraph/adapters and pkg/flags respectively, so this logic is unit-testable in isolation instead of coupled to cmd's live command registry.
  • Adds Docusaurus docs for every new field/fact and updates the JSON Schema (atmos/manifest, config/global, stacks/stack-config) accordingly.

why

Atmos workflows and custom commands already covered most of what a task runner needs, but a handful of real gaps kept teams running go-task alongside Atmos: no dependency ordering between named commands/workflows, no up-to-date checking, no continue-on-error, no precondition shortcut, and custom commands couldn't even use parallel/matrix steps — the exact recipe the project's own go-task migration guide recommends for concurrent dependents. This closes those gaps using the existing when:/CEL condition engine and scheduler rather than inventing a second mechanism.

references

  • Blog post: website/blog/2026-08-05-taskfile-convergence.mdx

Summary by CodeRabbit

  • New Features

    • Added command and workflow dependencies with parallel execution, deduplication, parameterized inputs, and configurable failure handling.
    • Added freshness checks for sources and artifacts, tool preconditions, platform-aware conditions, and optional failure continuation.
    • Added parallel and matrix control steps for custom commands.
    • Added command aliases, hidden internal commands, and constrained argument and flag values.
  • Bug Fixes

    • Improved Windows shell quoting, dependency error handling, freshness evaluation, and workflow validation.
  • Documentation

    • Added configuration guides, examples, and release documentation for the new task-runner capabilities.
feat(provisioner): Azure (azurerm) backend auto-provisioning @aknysh (#2911) ## what

Adds automatic provisioning for the azurerm Terraform state backend — the Azure counterpart to the existing S3 backend provisioner. When provision.backend.enabled: true on a component using an azurerm backend, Atmos creates the resource group (if missing), storage account, and blob container before terraform init, with opinionated secure defaults.

Previously, atmos terraform backend create returned create not implemented for backend type: azurerm and provision.backend.enabled silently skipped for Azure.

What gets created (hardcoded secure defaults)

  • Resource group in the identity's location (or reuses an existing group's location)
  • Storage account: StorageV2 / Standard_LRS, TLS 1.2 minimum, HTTPS-only, public blob access blocked
  • Entra ID hardening: shared-key access disabled when the backend sets use_azuread_auth: true
  • Blob versioning + 30-day soft delete (the S3-versioning analog)
  • Private container; Name + ManagedBy=Atmos tags

No lock resource is created — the azurerm backend serializes concurrent writes with native Azure Blob Storage blob leases (the DynamoDB / native-S3-locking analog, built into Blob Storage).

Design

  • Self-registers create/delete/exists/name into the shared backend registry via init(), so the before.terraform.init hook and atmos terraform backend create/delete pick up azurerm with no wiring changes to the hook or CLI.
  • A narrow azureBackendAPI interface hides the ARM SDK pollers behind synchronous methods for testability, mirroring the S3 client factory and the existing azurerm state-reader wrapper. A test-injectable client factory (SetAzureBackendClientFactory/ResetAzureBackendClientFactory) mirrors SetS3ClientFactory.
  • Location is sourced from the active Azure identity (or an existing resource group), never from the backend block — it is not a valid azurerm backend argument and Terraform would reject it in backend.tf.json.
  • Adds armresources + armstorage SDK deps (azcore/azidentity/azblob were already present).

Also fixes: azurerm backend init under Atmos-managed CLI auth

Field-testing the provisioner end-to-end surfaced a pre-existing bug that blocked azurerm backends whenever an Atmos profile was active. For CLI / device-code / interactive auth, Atmos exported both ARM_SUBSCRIPTION_ID and ARM_TENANT_ID to the Terraform subprocess. OpenTofu's azurerm backend authenticates via the Azure CLI and runs az account get-access-token --subscription <id> --tenant <id>, which the CLI rejects with Please specify only one of subscription and tenant, not both. It fails at argument validation — before the CLI even checks the session — so terraform init cannot list existing workspaces or read state at all:

Error: Failed to get existing workspaces: error listing blobs:
AzureCLICredential: ERROR: Please specify only one of subscription and tenant, not both

The tenant is now exported only for OIDC (service-principal / federated) auth, which needs it and does not shell out to the Azure CLI. On the CLI path only ARM_SUBSCRIPTION_ID is exported; the tenant is already fixed by the MSAL session Atmos seeds (and the active subscription), and the azurerm/azapi/azuread providers auto-detect it. oidc.go re-adds the tenant in its OIDC override, so service-principal auth is unchanged.

  • pkg/auth/cloud/azure/env.go: PrepareEnvironment gates the tenant export on UseOIDC.
  • pkg/auth/providers/azure/oidc.go: OIDC override re-adds ARM_TENANT_ID / AZURE_TENANT_ID.
  • Test updates assert the tenant is omitted on the CLI/device-code/interactive path and present for OIDC.

why

Brings Azure to feature parity with AWS for backend bootstrapping and eliminates the chicken-and-egg problem of needing remote state before Terraform can run — replacing the bespoke cold-start storage-account component teams currently hand-roll on Azure.

The CLI-auth fix is what makes the provisioned backend actually usable under Atmos-managed Azure auth: without it, terraform init against any azurerm backend fails while a profile is active.

references

  • New PRD: docs/prd/azurerm-backend-provisioner.md
  • Fix doc: docs/fixes/2026-08-11-azurerm-backend-cli-subscription-tenant-conflict.md
  • Mirrors: docs/prd/s3-backend-provisioner.md

test

  • Unit tests (pkg/provisioner/backend/azurerm_test.go, azurerm_wrappers_test.go): table-driven, mocked azureBackendAPI and Azure SDK fake servers — config extraction/precedence, use_azuread_auth parsing, full-create, resource-group reuse, existing-account warning, location-required error, every error path, existence checks, delete safety, registry wiring, and the ARM passthrough wrappers (404→exists mapping, poller completion, error propagation). 95.1% package coverage.
  • Auth fix tests (pkg/auth/cloud/azure/{env,setup}_test.go, pkg/auth/providers/azure/{cli,device_code,oidc}_test.go): both branches of the tenant gate verified — omitted for CLI/device-code/interactive, present for OIDC. env.go and oidc.go PrepareEnvironment are both 100% covered.
  • go build ./..., go vet, gofmt, and all pre-commit hooks (go-fumpt, golangci-lint, go.mod tidy) pass.

[!NOTE]
The auto-provisioned account uses secure-but-simple defaults (Standard_LRS, Microsoft-managed keys, public network access with Entra ID/RBAC gating) intended for dev/test/bootstrap — not production. For production, import the resources into a managed module (e.g. Azure/avm-res-storage-storageaccount); the provisioner is idempotent, so provision.backend.enabled: true can be left in place.

Summary by CodeRabbit

  • New Features

    • Added automatic Azure azurerm backend provisioning with resource groups, storage accounts, and private blob containers.
    • Applies secure defaults, blob versioning, soft-delete retention, native lease locking, and idempotent operations.
    • Added force-protected deletion while preserving resource groups.
    • Integrated Azure backends with discovery and lifecycle commands.
  • Bug Fixes

    • Improved Azure CLI, device-code, and OIDC environment handling to prevent authentication conflicts.
  • Documentation

    • Added configuration, migration, usage, FAQ, and production limitation guidance.
    • Updated backend provisioning documentation and roadmap.

🚀 Enhancements

fix(terraform): prevent concurrent output corruption @zack-is-cool (#2898) ## What

Prevent concurrent Terraform runs from interleaving provisioner and lifecycle UI with component-prefixed output.

Why

JIT provisioning, backend provisioning, post-init provider locking, and clear or spin step hooks could bypass the scheduler's concurrent-output suppression. Terminal control sequences could corrupt output from other components.

Validation

  • go build ./...
  • atmos lint --changed
  • Focused scheduler, hooks, runner-step, provisioner, source, workdir, and Terraform-init tests.
  • Full internal/exec suite completed in a clean worktree.
  • Full CLI suite completed with an extended timeout. Remaining failures require external GitHub access for tenv, a non-linked Git worktree for one sandbox test, and an environment without inherited GitLab tokens.

Summary by CodeRabbit

  • New Features
    • Improved concurrent Terraform runs by suppressing transient spinners, hook output, and terminal-line updates.
    • Standardized output routing across initialization, provisioning, hooks, and post-initialization steps.
    • Preserved provisioning status messages and warnings through configured output streams.
  • Bug Fixes
    • Prevented transient progress indicators from interfering with concurrent execution output.
    • Improved handling of missing execution contexts.
  • Documentation
    • Added guidance for managing transient Terraform output during concurrent runs.
fix: preserve trailing newlines in text-based 3-way merges @jorrite (#2891) ## what
  • Fix atmos scaffold generate --update unconditionally stripping the trailing newline from every file it 3-way-merges, whether or not the file actually changed.
  • pkg/generator/merge/text_merger.go: TextMerger.Merge() now appends one newlineSeparator ("\n") to each of ours/base/theirs before handing them to diff3.Merge, so diff3's guaranteed loss of exactly one trailing newline cancels out and the original count survives.
  • pkg/generator/merge/text_merger_test.go: consolidated the trailing-newline regression coverage into a single table-driven test, TestTextMerger_TrailingNewlinePreservation, asserting exact byte-for-byte output across a no-op merge (0/1/2/3 trailing newlines, plus an internal blank line) and a genuine template change (theirs with 0/1/2 trailing newlines).
  • No change to conflict detection, threshold behavior, or ConflictStrategy handling — out of scope, and unaffected since the appended newline is identical across all three inputs.

why

  • TextMerger.Merge() delegates the actual 3-way merge to epiclabs-io/diff3, which reads each of base/ours/theirs line-by-line via bufio.Scanner (ScanLines, Go's standard-library default split function) and rejoins the merged lines with strings.Join(lines, "\n").
  • ScanLines strips every line's terminator — including the last — and gives no way to tell afterward whether the original input ended with a trailing newline or not. Concretely: for content ending in N trailing newlines, the round-trip through GetLines + Join always reconstructs exactly N-1 (it loses exactly one, regardless of how many there were; for N = 0 there was nothing to lose in the first place). Verified directly: generating a file with 3 trailing newlines and running --update with nothing changed on the template side reproducibly comes back with 2.
  • Appending one newline to each input before the merge bumps every input's count to at least 1, so that guaranteed loss of exactly one cancels out and the original count is preserved — for both the no-op case and genuine changes, since whichever side's content ends up dominating a given region carries its own (now-restored) newline count through, independent of the others.
  • This must be applied to all three inputs, not just theirs: appending it only to theirs makes an otherwise-identical ours/theirs pair (a very common no-op shape) differ by one trailing newline as far as diff3 is concerned, which turns a no-op into a spurious detected change/conflict instead of fixing anything.

references

Summary by CodeRabbit

  • Bug Fixes

    • Improved text merging to preserve the exact number of trailing newlines.
    • Prevented unintended changes to blank lines and end-of-file newline states during conflict-free merges.
    • Ensured merged content remains byte-for-byte consistent when no substantive changes are made.
  • Tests

    • Added coverage for varying trailing-newline counts, blank lines, and template-only changes.
  • Documentation

    • Documented the trailing-newline preservation fix and its impact on text-based scaffold merges.
fix(scaffold): preserve source in scaffold config @jorrite (#2869) ## what
  • Fix atmos scaffold generate/atmos init recording a dangling, already-deleted temp-directory path in spec.source of .atmos/scaffold.yaml whenever the template source is remote (git::... or a bare https://... URL).
  • In the filepkg/generator/source/resolver.go: in resolveRemote(), set conf.Source = src (the original source string the caller passed in) after loading the template configuration from the temporary download directory, instead of leaving Configuration.Source as whatever LoadConfigurationFromDir was given (the temp dir itself).
  • pkg/generator/source/resolver_test.go: added/extended tests asserting Configuration.Source holds the original source for both local and remote paths, plus a dedicated regression test (TestResolve_RemoteRecordsOriginalSource) that fails on the pre-fix code and passes after.
  • No change to local source handling (resolveLocal), which already recorded the correct value.

why

  • For a remote scaffold source, resolveRemote() downloads the template into os.MkdirTemp("", "atmos-scaffold-"), then loaded the config with that temp dir passed in as the "source" — so Configuration.Source, and
    therefore the persisted spec.source, ended up holding something like /var/folders/xx/.../atmos-scaffold-1234567890. That directory is removed by cleanup() immediately after the command finishes, so the recorded provenance is a dangling reference to nothing as soon as generation completes — useless for anything that might want to read it back later (e.g. a future --update/re-resolve flow), and directly contradicts SaveProjectRecord's own doc comment: "spec.source and spec.baseRef record provenance for future updates."
  • Local sources (a relative/absolute path, or file://...) were correct only by accident of not having a temp-dir indirection step in resolveLocal, not because anything special-cased provenance for them.
  • Reproduced directly: atmos scaffold generate "git::https://.../scaffold-template.git" ./out --defaults, then cat ./out/.atmos/scaffold.yaml shows a /var/folders/...//tmp/... path for spec.source, and that path no longer exists on disk.

references

  • No upstream GitHub issue — checked issue search and the web for cloudposse/atmos + spec.source/scaffold, nothing matched as of 2026-08.

Summary by CodeRabbit

  • Bug Fixes

    • Remote scaffolds now retain their original source URI after resolution instead of displaying a temporary download location.
    • Local scaffold operations consistently preserve the original source path.
    • Improved source tracking for downloaded scaffold files, making configuration provenance clearer and more reliable.
  • Documentation

    • Added details about the source-tracking fix, including expected behavior and validation coverage.
fix(version): exclude draft GitHub releases from Version Tracker resolution @osterman (#2900) ## what
  • pkg/github.GetReleases now excludes draft GitHub releases unconditionally, alongside the existing prerelease filter. This fixes the Version Tracker's github-releases datasource resolver, atmos version list, and GetReleaseVersions, which all share this function.
  • Removes the deprecated atmos version track render subcommand. It was marked Deprecated/Hidden in the same commit that introduced it and has never had a non-deprecated existence in any release, so no migration path is needed. The shared renderTemplate helper moves into apply.go, its only remaining consumer.
  • Fixes pre-existing EditorConfig indentation drift (3-space list/fence indents instead of the required 2-space multiple) in docs/prd/atmos-version-management.md, surfaced once the file was touched by this branch's --affected validation.
  • Expands the version.files and !version function docs with worked before/after examples for the marker and github-actions file managers (including SHA pinning) and adds Helm/Container-component examples alongside the existing Terraform one.

why

  • atmos version track with datasource: github-releases and desired: latest could resolve to an unpublished draft release instead of the actual latest published release, whenever the GitHub token had repo write access (e.g. secrets.GITHUB_TOKEN in a repo's own CI). Reproduced against cloudposse/atmos itself: atmos version track lock resolved to v1.226.0, which gh release view v1.226.0 --repo cloudposse/atmos --json isDraft confirmed was a draft, when the real latest published release was v1.225.0. Unlike prerelease, there's no legitimate case for ever resolving to a draft, so it's excluded unconditionally rather than gated behind a new opt-in policy field.
  • atmos version track render was superseded by apply/the file-managers architecture within its own introducing PR and has been carried as dead weight across multiple releases; removing it avoids maintaining a command with no live users.
  • The docs updates make the Version Tracker's file-manager and !version behavior easier to learn from concrete examples rather than a single terse case.

references

  • N/A

Summary by CodeRabbit

  • New Features

    • Version tracking now excludes draft releases when resolving available versions.
    • Expanded documentation for version markers, Dockerfiles, GitHub Actions, Terraform, Helm, and container images.
  • Bug Fixes

    • Prevented draft GitHub releases from being selected as version candidates.
    • Improved reliability of version-related documentation and examples.
  • Removed

    • Removed the deprecated version track render command and associated rendering behavior.

🤖 Automatic Updates

fix(deps): update github.com/epiclabs-io/diff3 digest to 3b16698 @[renovate[bot]](https://github.com/apps/renovate) (#2917) This PR contains the following updates:
Package Type Update Change
github.com/epiclabs-io/diff3 require digest 280ec183b16698

Configuration

📅 Schedule: (UTC)

  • Branch creation
    • At any time (no schedule defined)
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

fix(deps): update kubernetes monorepo to v0.36.3 @[renovate[bot]](https://github.com/apps/renovate) (#2918) This PR contains the following updates:
Package Change Age Confidence
k8s.io/apimachinery v0.36.2v0.36.3 age confidence
k8s.io/client-go v0.36.2v0.36.3 age confidence

Release Notes

kubernetes/apimachinery (k8s.io/apimachinery)

v0.36.3

Compare Source

kubernetes/client-go (k8s.io/client-go)

v0.36.3

Compare Source


Configuration

📅 Schedule: (UTC)

  • Branch creation
    • At any time (no schedule defined)
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about these updates again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

Don't miss a new atmos release

NewReleases is sending notifications on new releases.