feat(ci): migrate demo-localstack to Floci (rename to demo-floci) @osterman (#2599)
what
- Replace the LocalStack image with Floci (
floci/floci:1.5.23, pinned) for the AWS-emulator demo - Rename the example
examples/demo-localstack→examples/demo-floci, and the CI job[localstack] demo-localstack→[floci] demo-floci - Rename all emulator-facing names inside the example: mixin (
stacks/mixins/floci.yaml), YAML anchor (&floci_url), auth identity (floci-superuser), custom commands (atmos floci up|down|restart|reset|status), compose service/container - Decouple the
vendor-globstest from this demo: it vendors**/demo-localstack/*from origin/main at test time, so a rename would break it in this PR (main has nodemo-flociyet) and on every unmerged branch afterward. It now targetsexamples/demo-helmfile(identical root file set), keeping it green before and after merge - Drop LocalStack-specific service-container config Floci doesn't need (
SERVICES,DEBUG, docker.sock mount, 4510-4559 port range — this demo only touches STS/IAM/S3) - Update devcontainer bootstrap, examples index, and website file-browser plugin references
why
LocalStack EOL'd Community Edition: the OSS repo was archived in March 2026, the unified image now requires an account + auth token, and hosted infrastructure is being dismantled — the localhost.localstack.cloud DNS breakage fixed in #2598 was collateral from that wind-down. Staying on the unpatched 2023-era localstack:1.4.0 image means depending on a dead project whose vendor is actively turning things off.
Floci is the community's drop-in replacement (MIT, no auth token, same 4566 edge port and credential pattern) and is already used by the Terraform DAG scheduler integration tests (tests/terraform_floci_dag_test.go).
merge checklist (branch protection)
[localstack] demo-localstack is a required status check on main. Merge order:
- Merge #2598 first (under the current rule)
- Update the required check on
main:[localstack] demo-localstack→[floci] demo-floci - Merge this PR
- Other branches pick everything up by merging
main(which they already need for the #2598 DNS fix)
verification
- CI: the Floci-backed demo job passed in 1m50s (vs LocalStack's ~3m; Floci's native binary boots in ~26ms vs ~15s)
- Local: full
atmos test(validate + plan/apply/destroy × 3 stacks) passed againstfloci/floci:1.5.23under podman vendor-globsCLI test executed for real against remotemain(not skipped) with the newdemo-helmfileglob — passed
references
- Stacked on #2598 (localhost endpoint fix — the DNS root-cause fix this builds on)
- LocalStack Community EOL: https://blog.localstack.cloud/the-road-ahead-for-localstack/
- Floci: https://github.com/floci-io/floci
Summary by CodeRabbit
-
New Features
- Added demo-floci example and Atmos CLI commands to manage the Floci emulator (start/stop/restart/reset/status).
-
Documentation
- Updated example READMEs and compose guidance with Floci setup, port/credentials, and Terraform tips.
-
CI
- CI workflows switched demo coverage from LocalStack to Floci.
-
Tests
- Added offline test for demo-floci and updated demo-related test expectations.
-
Chores
- Updated local demo startup script, demo manifests, and website tags to use Floci.
feat(testing): add pact consumer contracts for Atmos Pro API @goruha (#2588)
what
- Add pact consumer contract tests for all 8 Atmos Pro API endpoints in
pkg/pro/ - Add
github.com/pact-foundation/pact-go/v2as a dev dependency - Generate
pacts/atmos-AtmosPro.json— the consumer contract file checked into version control - Add a "Pact Contract Testing" section to the README with setup and usage instructions
- Add Spec Kit workflow artifacts (
.specify/,specs/) and related Claude skills
why
- Consumer contract tests run the real
AtmosProAPIClientagainst a pact mock server, so any change to request/response shapes inpkg/pro/is caught locally before it reaches the live Atmos Pro API - Tests are isolated behind
//go:build pactand never run in the standardgo test ./...suite, keeping the default CI pipeline unaffected - Checking
pacts/atmos-AtmosPro.jsoninto version control makes API surface drift visible in PR diffs
references
- Pact Go documentation
- Covered endpoints:
UploadAffectedStacks,LockStack,UnlockStack,ExchangeOIDCToken,GetGitHubOIDCToken,UploadInstances,UploadInstanceStatus,CreateCommit
fix(ci): use localhost endpoints in LocalStack demo to avoid DNS hangs @osterman (#2598)
what
- Switch the
demo-localstackexample's AWS provider endpoints fromhttps://localhost.localstack.cloud:4566tohttp://localhost:4566 - Enable
s3_use_path_style: trueso S3 operations don't depend on wildcard*.localhost.localstack.cloudDNS/TLS - Add
skip_requesting_account_id: trueso provider configure doesn't block on identity lookups - Update the example README to document the settings and the expected "AWS account ID not found for provider" warning
why
The [localstack] demo-localstack CI job started hanging until the 20-minute timeout across all branches beginning 2026-06-10 ~02:18 UTC, with no corresponding code change (identical commits both passed and failed; the LocalStack image is pinned at 1.4.0 with the same build hash in green and red runs).
Root cause: localhost.localstack.cloud is a public DNS record hosted by LocalStack that resolves to 127.0.0.1. After LocalStack EOL'd Community Edition (repo archived March 2026), their DNS zone was restructured on 2026-06-08 18:53 UTC (SOA serial; the record is now a freshly delegated Route53 subzone). GitHub's Azure runners began intermittently failing to resolve the name ~31h later as resolver caches expired. The Terraform AWS provider treats DNS failure as retryable and backs off past the job timeout — hanging silently before its first API call.
Evidence:
- Every failed run hangs at the identical point: after
terraform workspace new dev-demo, before the provider's first API call. The LocalStack container log shows exactly one completed request (sts.GetSessionToken => 200— issued by atmos auth, which useshttp://localhost:4566and always succeeds). The provider's first call vialocalhost.localstack.cloudnever arrives. - A/B proof: on
osterman/fix-post-merge-sha, a run with the old endpoints timed out at 13:43 UTC; the identical change in this PR passed at 13:53 UTC (run 27281214186).
Extending the timeout would not help — the provider's retry backoff exceeds any reasonable limit when DNS is failing.
references
- Failing runs (examples): 27248701025, 27283701712, 27282384395, 27300390150 — all cancelled at the 20-min
[localstack]job timeout - Green run with this exact change: 27281214186
- LocalStack Community EOL: https://blog.localstack.cloud/the-road-ahead-for-localstack/ (the DNS zone change itself is unannounced)
Summary by CodeRabbit
- Documentation
- Updated LocalStack example documentation and configuration for improved CI compatibility, clarifying expected Terraform behavior and configuration settings.
🚀 Enhancements
fix(exec): preserve empty lists as empty (not null) in YAML function processing @AleksandrMatveev (#2603)
what
- Fix empty-list stack variables (e.g.
attributes: []) being written asnullin the generated*.terraform.tfvars.json. - The
[]anybranch ofprocessNodesWithContextnow seeds its accumulator withmake([]any, 0, len(v))instead of a nil slice, so empty lists stay empty (non-nil). - Add a regression test (
TestProcessNodesPreservesEmptyLists) covering top-level, nested, and!unset-emptied lists, asserting they remain non-nil and serialize to JSON[]rather thannull.
why
- An empty list collapsed to a nil slice during YAML-function processing. A nil slice renders as
[]in YAML (soatmos describelooked correct) but marshals tonullin JSON, so the generated tfvars contained"attributes": null. - Terraform/OpenTofu then fails on that value in functions that reject null where a list is expected:
Error: Invalid function argument while calling concat(seqs...) Invalid value for "seqs" parameter: argument must not be null. - This is a regression introduced when the
!unsetYAML function was added: the slice branch was changed from index-assignment into a pre-sized slice toappendonto a nil slice, to support dropping items. Bisected to commit28678366. The fix keeps the!unsetskip semantics while restoring non-nil empty-list behavior.
Minimal repro — stack var empty_list: []:
| before | after | |
|---|---|---|
empty_list in generated tfvars
| null
| []
|
| non-empty lists | preserved | preserved |
references
- Regression introduced by the
!unsetYAML function feature (commit28678366)
Summary by CodeRabbit
-
Bug Fixes
- Empty lists are now preserved and serialized as JSON arrays ([]) rather than null across top-level, nested, and unset-cleared lists.
-
Tests
- Added a regression test to verify empty-list preservation and updated CLI snapshot expectations to use empty arrays instead of null.
Fix optional toolchain signature 404 handling @osterman (#2601)
what
- Treat missing toolchain signature and attestation evidence as skipped under
signatures: when_availableinstead of failing installs. - Pre-download remote SLSA and Minisign sidecars so missing files follow the same optional policy as other signature evidence.
- Add regression coverage for the tflint GitHub attestation 404 case, required-mode failures, missing SLSA/Minisign/Cosign evidence, and invalid signature failures.
why
- Keeps package verification non-breaking by default while still failing when available evidence does not validate.
- Fixes installs like
terraform-linters/tflint, wheregh attestation verifycan return HTTP 404 because no attestation exists even though checksum verification can pass. - Preserves strict behavior for
signatures: required.
references
- Reported from a
terraform-linters/tflintinstall on Atmos 1.220.0 wheregh attestation verifyreturned HTTP 404 for missing attestations.
Summary by CodeRabbit
-
New Features
- Fetch remote signature sidecars automatically with temporary storage and cleanup.
- Treat certain transient attestation service stream errors as retryable.
-
Bug Fixes
- Better handling of missing or unavailable signature/provenance/attestation evidence — mark as skipped when optional, fail when required.
- More consistent enforcement across verification paths.
-
Tests
- Added tests covering skip vs. required behaviors and retry classification.
fix(ansible): forward `-- ` passthrough to ansible-playbook @mtb-xt (#2594)
what
- Fix
atmos ansible playbook <component> -s <stack> -- <ansible-args>failing with a misleadingUnknown command \`` error. playbookCmdnow uses a separator-aware argument validator instead ofcobra.ExactArgs(1): it requires exactly one positional component, counting only the arguments before a--separator.- Adds regression tests for the
--passthrough path.
why
cobra.ExactArgs(1)counts every positional argument, including the ansible-playbook passthrough tokens captured after--. Any invocation using--(e.g.-- --check,-- --tags web,-- --limit host) therefore tripped argument validation with 2+ "positional" args, and the rootUsageFuncrendered that failure as anUnknown commanderror naming the component.- This made it impossible to forward native
ansible-playbookflags, even though the command help documents[options]and points at the ansible-playbook CLI docs, and the executor (buildCommandArgs) already appendsinfo.AdditionalArgsAndFlagsto the command. cobra.ArgsLenAtDash()returns-1when no--is present, so the validator still checks the full slice for the common case and keeps the original "exactly one component" contract.
Before
$ atmos ansible playbook myapp -s prod -- --check
Error: Unknown command `myapp` for `atmos ansible playbook`After
$ atmos ansible playbook myapp -s prod --dry-run -- --check
ansible-playbook --extra-vars @/.../prod-myapp.ansible.vars.yaml -i inventory/hosts.yml --check playbook.ymlreferences
- No existing issue; reproducible on every release that ships
atmos ansible playbook(verified on v1.220.0). - Ansible check mode and other passthrough flags: https://docs.ansible.com/ansible/latest/cli/ansible-playbook.html
Summary by CodeRabbit
-
New Features
- Forward additional args after
--verbatim to the underlying playbook command; positional validation ignores these passthrough args.
- Forward additional args after
-
Bug Fixes
- Require exactly one component before
--and return clear validation errors when missing or duplicated.
- Require exactly one component before
-
Tests
- Added comprehensive tests for separator-aware parsing, validation, and passthrough behavior, including prompt-aware validation scenarios.