Add single-label lookups with optional defaults @osterman (#3097)
## what-
Add
!labels key [default]with missing-key errors, explicit fallbacks, and inheritance coverage. -
Document label access and an Atmos Pro example that consumes resolved labels; update agent skills, the feature announcement, and roadmap.
-
Allow macOS Colima startup 12 minutes for provisioning and its VM restart, and give the enclosing setup step 55 minutes to cover both attempts.
why
- Read individual labels in stack configuration with explicit missing-key behavior.
- Prevent CI from killing Colima during its second boot: the passing sibling job used 7m37s of the former 8-minute startup cap.
references
- CI fix validated with actionlint, Bash syntax checks, ShellCheck, timeout-budget checks, repository lint, pre-commit hooks, and local CodeRabbit review (zero findings).
- Historical changelog audit moved to #3099; validated with local CodeRabbit review, Go build and focused tests, lint, and website build.
Summary by CodeRabbit
-
New Features
!labelscan retrieve an individual metadata label with an optional fallback value.- Bare
!labelscontinues to return the complete label map. - Label values resolve from component metadata after stack defaults and inheritance.
- Label lookups can be used in workflow inputs for component- and stack-specific runner selection.
-
Documentation
- Expanded YAML function, metadata, workflow, migration, and roadmap documentation with syntax, examples, and fallback behavior.
- Added a blog post detailing single-label lookups.
fix(ci): stop Mergify updating bot PRs once approved @osterman (#3112)
## what- Add
#approved-reviews-by=0to the Mergify rule Keep Dependabot/Renovate PRs up to date with main, so Mergify stops runningupdateon a bot PR once it has an approval.
why
Since main moved to GitHub's native merge queue (ruleset "Merge Queue", 2026-08-06), this rule fails on essentially every bot PR that gets enqueued — 6 of the last 12 (#3066, #3067, #3071, #3086, #3091, #3098):
Base branch update has failed
protected branch '' check failed: A pull request for this branch has been added to a merge queue. Branches that are queued for merging cannot be updated.
Mechanism:
-
Mergify's
updateaction mergesmaininto the PR's head branch. GitHub locks that branch while the PR sits in the native queue. -
Mergify's built-in "skip if queued" guard is
queue-position = -1, which only reflects Mergify's own queue. There is no Mergify condition for GitHub's native queue, and nothing lands on the head SHA either (no check-run or commit status is created on enqueue), so there is nothing else to condition on. -
The rule wakes on PR webhooks, not promptly on pushes to
main. On the failing PRs the branch had been behind for 50–70 min unnoticed; Mergify re-evaluated ~2 min after the approval/enqueue events, when the branch was already locked:PR approved enqueued update failed merged #3091 02:20:02Z 02:20:09Z 02:21:44Z 02:40Z #3098 02:22:21Z 02:22:28Z 02:24:37Z 02:43Z #3067 20:35:15Z 20:35:32Z 20:37:46Z 21:25Z #3066 20:35:30Z 20:35:35Z 20:38:05Z 21:25Z
Why approval is the right gate: the "Required Pull Request Reviews" ruleset requires 1 approval with no bypass actors, so GitHub cannot enqueue a PR without a current approval. Approval is therefore a strictly earlier signal than enqueue, with no timing dependency (an Actions workflow on pull_request: enqueued toggling a label would be racing that same ~2 min window). After approval the merge queue owns freshness by re-testing against current main.
Side benefit: the ruleset has dismiss_stale_reviews_on_push: true, so today a Mergify update landing after approval dismisses the approval and forces a re-review. That stops.
Context: this rule was added in #2752 because main then had strict: true (require branches up to date). That protection is gone with the merge queue, so the alternative of deleting the rule outright is also valid; this keeps pre-review freshness for bot PRs.
The failures were cosmetic (Mergify is not a required status check; the PRs merged via the queue), but they show up as a red check on every bot PR.
references
- #2752 (introduced the rule)
- Mergify
updateaction docs: "You do not need to use this action if you use the merge queue."
Summary by CodeRabbit
- Chores
- Updated automated dependency pull request handling so branches are refreshed only after receiving at least one approval.
- Added guidance explaining the approval requirement before entering the merge queue.
fix(ci): invalidate CloudFront cache on website deploys @osterman (#3103)
## what- Add a
cloudfront-distribution-id/cloudfront-invalidation-pathsinput and invalidation step to the shared.github/actions/s3-deploycomposite action. - Wire it into
website-deploy-prod.yml(invalidates/*on the prodatmos-docs-site-spadistributionE2WVYUGW40ZPA8) andwebsite-preview-deploy.yml(invalidates only/pr-<N>/*on the dev distributionE1U29O8X09M5UR, so one PR's deploy never busts every other open PR's preview cache). - Add
cloudfront.amazonaws.com:443to both workflows' Harden Runner allowlist. - Add a fix-log entry documenting the root cause and validation.
why
- atmos.tools threw a browser
ChunkLoadError(404 on a hashed JS bundle). Root cause:website-deploy-prod.ymlrunsaws s3 sync --deletebut never invalidated CloudFront, and the distribution's legacy per-behavior TTL (min_ttl: 60) floors edge caching regardless of originCache-Control. A client hitting a stale edge-cachedindex.htmlwithin that window got 404s on chunks a newer deploy had already deleted. - This is a different bug than the 2026-09-08 concurrent-deploy race (
docs/fixes/2026-09-08-website-deploy-race-serialize.md) — confirmed via GitHub Actions run history that the existingconcurrency:guard is working correctly. - The deploy IAM roles already grant
cloudfront:CreateInvalidation/cloudfront:GetInvalidationscoped to their own distribution (confirmed live viaaws iam get-role-policy, not just theinfra-liveTerraform source) — it was simply unused, so this fix is fully contained incloudposse/atmoswith noinfra-livechange needed. - Manually ran the invalidation against prod as immediate remediation for the live incident; confirmed it reached
Completed.
references
docs/fixes/2026-09-09-website-cloudfront-stale-chunk-invalidation.mddocs/fixes/2026-09-08-website-deploy-race-serialize.md(related prior incident, confirmed not the same root cause)
Summary by CodeRabbit
-
Bug Fixes
- CloudFront caches are now invalidated after production website deployments, helping prevent users from receiving stale pages or broken asset references.
- Preview deployments invalidate only their corresponding preview path, reducing unintended cache refreshes.
-
Documentation
- Added documentation describing the stale-cache issue, its resolution, and validation steps.
Link changelog announcements to usage documentation @osterman (#3099)
## what- Link 259 existing changelog posts to usage documentation and correct stale links.
- Add missing diagnostics and component-mocks references, clarify process-metrics limits, and require usage links in changelog authoring guidance.
why
- Make announcements lead to usable instructions independently of the label lookup feature.
references
- Split from #3097; validated with local CodeRabbit review, changed-file lint, website build, and link checks.
feat(vendor): add Azure DevOps pull-request provider @jorrite (#3048)
## WhatAdds pkg/git/providers/azuredevops as a second PullRequestPublisher implementation alongside the existing GitHub provider, so atmos vendor update --pull-request can target Azure DevOps Repos.
- New
azuredevopsprovider, registered viainit()underci.pull_request.provider: azuredevops. schema.VendorPullRequestConfiggainsorganization/project/repositoryfields, since Azure DevOps addresses a repository with three segments instead of GitHub's owner/repository pair.pkg/config'sbridgeVendorUpdaterConfignow syncs these three fields into viper along with the rest ofvendor.ci.pull_request.*— they were declared on the schema and read bycmd/vendor/update.gobut never actually bridged, soprovider: azuredevopsalways failed configuration validation regardless of whatatmos.yamldeclared (found by testing against a real Azure DevOps org).PullRequestOptionsgains an additiveNamespace []stringfield to carry the extra segment (nil for GitHub; the GitHub provider now rejects a non-emptyNamespaceinstead of silently ignoring it).- Authenticates via
AZURE_DEVOPS_EXT_PAT(HTTP Basic, empty username). - Same create-or-update-active-PR reconciliation as the GitHub provider: lists active pull requests filtered by source/target branch, updates the existing one's title/description in place if found, otherwise creates a new one.
- Labels apply the same way as GitHub's provider. Reviewers don't: Azure DevOps' reviewers API takes an identity GUID, not a plain username, so each configured
reviewersentry (a display name, account name, or email) is resolved through the Identities API before being applied. A group match or an ambiguous match fails loudly instead of guessing; only individuals are supported today. Confirmed against a real Azure DevOps org, resolving both a display name and an email correctly. - Assignees aren't supported by Azure DevOps pull requests, so a non-empty
assigneesfails loudly instead of being silently dropped. - The default PR body's "Atmos CI" badge is now provider-supplied instead of hardcoded centrally: a new optional
atmosgit.PullRequestBodyBadgerinterface lets aPullRequestPublisherreturn its own badge, since each forge's pull request markdown has its own quirks (raw HTML support, image hosting requirements, light/dark switching); a publisher that doesn't implement it gets a static text-link fallback. GitHub keeps its existing raw-HTML<picture>badge with light/dark switching. Azure DevOps uses a plain, sized markdown image (![]()with Azure DevOps' own=WIDTHxHEIGHTsizing syntax) instead of raw HTML, which doesn't render in Azure DevOps pull request descriptions at all — confirmed against a real pull request. - Documentation update to
website/docs/cli/configuration/vendor.mdx, a changelog post, and a roadmap milestone.
Why
The PullRequestPublisher interface was explicitly designed to support more providers than GitHub, but nothing had implemented a second one yet. Teams hosting components in Azure DevOps Repos had no way to use the Component Updater's automated PR workflow at all.
References
- Interface:
pkg/git/pull_request.go - Existing implementation this mirrors:
pkg/git/providers/github/pull_request.go - Follow-up filed for a separate, pre-existing bug found while testing this against a real org (not caused by this PR, reproduces on
mainfor any git-ref-versioned vendor source): #3076
Summary by CodeRabbit
-
New Features
- Added Azure DevOps Repos support for
vendor update --pull-request. - Supports creating or updating pull requests, labels, reviewers, and organization/project/repository configuration.
- Added Azure DevOps authentication through
AZURE_DEVOPS_EXT_PAT. - Pull request bodies now support provider-specific CI badges.
- Added Azure DevOps Repos support for
-
Bug Fixes
- Added validation for incomplete repository settings, invalid namespaces, unsupported assignees, and unresolved reviewers.
- GitHub now rejects unsupported namespace configuration.
-
Documentation
- Added Azure DevOps configuration guidance and release documentation.
feat(website): Atmos Pro cast download/embed components @osterman (#2739)
## what- Adds
CastProDownload, a "Download ▾" split-button component offering rendered GIF/MP4/SVG/WEBM artifacts of any.castfile in a public GitHub repo, via the new Atmos Pro cast-rendering service. - Adds
CastProEmbed, an<iframe>wrapper for the service's hosted HTML player, for embedding a cast player without hosting the source file locally. - Adds a shared
CastProArtifactmodule: a pure URL builder (buildArtifactUrl/buildEmbedUrl, with unit tests) and auseCastArtifacthook that follows the render service's three response shapes — an already-rendered artifact (triggers a native download), a still-rendering one (polls onRetry-After, capped at ~60s), and a hard error (surfaces the JSON error message). - Adds a
/cast-pro-demopage exercising both components against a realcloudposse/atmoscast path, and atest:cast-pro-artifactnpm script.
why
- Atmos Pro added a public, CORS-enabled service that renders any
.castrecording in a public GitHub repo to GIF/MP4/SVG/WEBM, or to a hosted HTML player. This lets atmos.tools offer downloads/embeds of casts without needing them committed as static assets first, unlike the existingCastPlayercomponent. - This PR lands the reusable building blocks and a demo page; wiring the components into specific docs/blog pages is left for follow-up, so it's labeled
no-release.
references
- Atmos Pro cast-rendering endpoint:
https://atmos-pro.com/casts/{owner}/{repo}/{ref}/{path}.cast.{gif|mp4|svg|webm}
Summary by CodeRabbit
-
New Features
- Added Cast Pro sharing controls for copying demo and embed links.
- Added downloads in GIF, MP4, SVG, and WEBM formats with progress polling and inline error feedback.
- Added an embeddable Cast Pro player with responsive 16:9 display.
- Added a Cast Pro demo page showcasing embedding and artifact downloads.
- Added cast actions to the file browser when source information is available.
-
Tests
- Added coverage for URL generation, validation, supported formats, and embed links.
-
Documentation
- Added usage and response-handling documentation for Cast Pro features.
docs: flag Homebrew FIPS gap in fips-140-mode PRD @osterman (#3074)
## WhatDocuments a Homebrew-specific gap in docs/prd/fips-140-mode.md's "Where It's Wired In" table: the atmos formula in Homebrew/homebrew-core builds with a plain go build and no GOFIPS140, so brew install atmos produces a binary reporting "fips": false in atmos version --format=json, while GitHub Release binaries (built via .goreleaser.yml, which does set GOFIPS140=latest) correctly report "fips": true.
Why
atmos version --format=json showing fips: false looked like GoReleaser had regressed. Investigation confirmed GoReleaser and the local atmos build path (magefiles/build.go) both set GOFIPS140=latest correctly. The actual gap is Homebrew's from-source build, which lives entirely outside this repo. The PRD's wiring table previously claimed "every distinct Go-toolchain build invocation in the repo sets GOFIPS140" without mentioning that Homebrew isn't covered by that claim at all (it's not a build invocation in this repo), so this was an unflagged blind spot.
A fix is proposed upstream: Homebrew/homebrew-core#302847 (draft, pending Homebrew maintainer review — outside this repo's control).
References
docs/prd/fips-140-mode.mdinternal/exec/version.go(isFIPSBuild()— readscrypto/fips140.Enabled()at runtime).goreleaser.yml(setsGOFIPS140=latestfor release binaries)- Upstream fix: Homebrew/homebrew-core#302847
Summary by CodeRabbit
-
Documentation
- Documented the Homebrew distribution path and its alignment with FIPS 140-enabled GitHub Release binaries.
- Added troubleshooting documentation for a race condition affecting dependent-description tests.
-
Bug Fixes
- Improved consistency for Homebrew installations by ensuring they use FIPS 140-compatible build settings.
- Prevented intermittent test failures caused by concurrent output handling.
docs: add homebrew skill for atmos formula PR workflow @osterman (#3081)
## WhatAdds .claude/skills/homebrew/SKILL.md, an agent skill covering the Homebrew/homebrew-core formula PR workflow for atmos: the real PR template, AI/LLM disclosure rules, the 50-character commit-subject limit, and how to run brew install --build-from-source / brew test / brew audit --strict / brew style locally via a disposable tap without a full homebrew-core clone.
Why
A prior attempt at a Homebrew formula PR (Homebrew/homebrew-core#302847) was auto-closed by BrewTestBot for looking AI-generated and skipping the real PR template. This skill exists so the next attempt (fixing that PR, per its own "do not open a new PR" instruction) follows homebrew-core's actual conventions instead of repeating the same mistake.
References
.claude/skills/homebrew/SKILL.md- Closed PR this skill was written in response to: Homebrew/homebrew-core#302847
Summary by CodeRabbit
-
Bug Fixes
- Fixed secret command behavior so component selection consistently uses the explicitly selected stack, including across repeated commands and shell completion.
- Improved required-flag validation when retrieving secrets without a component.
-
Reliability
- Added bounded retries for transient Terraform provider registry connection failures during initialization.
- Improved resilience when describing affected components during intermittent network failures.
-
Documentation
- Updated Homebrew guidance with portable, cross-platform commands.
- Documented fixes for intermittent Windows CI network and Terraform registry failures.
fix(ci): restore-only Go caches with a real warmup writer @osterman (#3077)
## what- Replace
actions/setup-go's built-in cache with a restore-only custom cache (restore-keys fallback, ImageOS-free key) in.github/actions/setup-go-cache; no CI job ever saves Go caches anymore - Make
setup-go-cache-warmup.ymlthe sole cache writer: adds a Linux leg, runs the real compiles (go build ./...,mage acceptance:precompile, and a-race/CGO warm for the race job's namespace), saves under run-unique keys, and triggers on go.mod/go.sum pushes tomain - Move the Build linux leg and the race job off
extras=s3-cacheso all Linux jobs share the GitHub cache backend; upgrade the race job torunner=xlarge - Fix
kubernetes-e2enullifying its restored cache with aGOCACHEoverride - Give native-ci's ~700MB provider-mirror cache a main-scope writer (new push-triggered
warm-cachejob); PR runs are now restore-only - Cache the
custom-gclbinary (full golangci-lint rebuild every run today), the pnpm store in both website workflows, and add restore-only toolchain caches tolint,hooks-tflint,floci,floci-go, andrace - Roll the toolchain cache key automatically on
.tool-versionschanges (hashFilesinatmos.yamlci.cache.key)
why
Storage-level caching works (92 entries / ~99GB active, no eviction thrash), but the caches were functionally hollow: setup-go entries are immutable per go.sum hash, and the old warmup only ran go mod download — so whenever it saved first, the frozen entry had an empty GOCACHE and every later build compiled from scratch while logging a cache hit. Measured live on run 34179823625: 504s go build in Build(linux), a 27-minute race job, and 1-1.8GB re-saved per platform per PR after every dependency merge. The Linux split-brain (build job on RunsOn S3, shards on GitHub cache) meant the warmest cache in the run was invisible to the ten Linux acceptance shards.
Deliberately not done: relocating atmos's own cache root to the Windows work disk — the acceptance shards assert XDG-default paths, and the cache's saver and restorers must agree on the root.
Post-merge verification: dispatch setup-go-cache-warmup.yml, confirm multi-GB go-cache-* entries on refs/heads/main, then compare Build(linux) go build time (expect ~504s → well under 2 min) and race-job duration (expect ~27m → ~10-15m).
references
- docs/fixes/2026-09-07-ci-go-cache-restore-only-warmup-writer.md (full investigation and change record)
- docs/fixes/2026-08-19-restore-go-build-cache-in-ci.md, docs/fixes/2026-08-31-terraform-registry-cache-windows-runner-degradation.md (prior incidents this builds on)
🤖 Generated with Claude Code
Summary by CodeRabbit
-
CI Improvements
- Improved Go, Terraform, provider, and toolchain cache restoration for faster, more consistent checks.
- Added main-branch cache warmups and reduced unnecessary cache writes.
- Improved linting and test performance through reusable cached tools and increased capacity for race-enabled testing.
- Cache invalidation now responds automatically to pinned tool version changes.
-
Website Builds
- Added pnpm dependency caching to production and preview builds.
-
Bug Fixes
- Acceptance checks now retry transient shard-listing failures while continuing to fail promptly on permanent errors.
-
Documentation
- Documented the updated CI caching behavior and verification process.
feat(version): add yaml manager and json format field to Version Tracker @osterman (#3069)
## what- Adds an optional
formatfield to thejsonversion-tracker manager'soptions.setentries (pkg/version/managers/json/json.go): a Go template (Sprig string functions + Atmos template functions) rendered against the resolvedmanager.VersionRef, whose output replaces the verbatim value before it's written into the target JSON field. - Adds a new
yamlfile manager (pkg/version/managers/yaml/) that writes locked values into YAML files at configured field paths, reusing Atmos's own format-preserving YAML editor (pkg/yaml, the engine behindatmos config set/atmos stack set) so comments, anchors/aliases, and key order on untouched fields survive the write. It supports the same optionalformatfield as thejsonmanager, and the same dot-notation path syntax asatmos config set/atmos stack set(sources[0].version,metadata."weird.key"), which is distinct from thejsonmanager's sjson/gjson dialect. - Extracts the
format-template rendering and duplicate-path-check logic shared by both managers intopkg/version/managers/format.goandpkg/version/managers/pathutil.go, and updates thejsonmanager to use them (no behavior change). - New sentinel errors for both managers (
ErrVersionJSONFormatInvalid,ErrVersionYAML*) so a badformattemplate or an invalidoptions.setconfiguration is rejected outright instead of silently writing garbage or an empty string. - Unit tests for both managers, including a
yaml-specific test that a fixture with comments and an anchor/alias survives an edit untouched, and a test that a bare-digit version string round-trips as a YAML string rather than silently becoming an int. - Docs (
website/docs/cli/configuration/version/files.mdx): aformatexample/section forjson, and a new "Updating YAML Files" section foryaml. - Two blog posts (
json-manager-format,version-tracker-yaml-manager) and two roadmap milestones announcing both features.
why
- The
jsonmanager always wrote a locked dependency's resolved value byte-for-byte. For a dependency sourced fromgithub-releases/github-tags, that value is the raw git tag (e.g.v1.228.0) — wrong when the target field expects bare semver, such as a plugin manifest'sversionfield. Reshaping happens at the write site (setEntry), not on the sharedmanager.VersionReftype or the lock file, since the same locked dependency may feed multiple write sites that want different shapes, andversions.lock.yamlshould stay the untouched, auditable record of what was resolved against upstream. - Neither the existing
templatemanager (renders a whole*.tmplsource to a sibling file) normarker(a plain comment-annotated token rewrite) patches a single field in an existing, hand-maintained YAML document while preserving comments and anchors. Theyamlmanager fills that gap by reusing Atmos's already-shipped format-preserving YAML editor instead of building new YAML-parsing logic.
references
- Found while finishing #2895, after two related
json-manager Version Tracker gaps shipped in #2900 and #2966.
Summary by CodeRabbit
-
New Features
- Added a YAML version manager that updates configured fields while preserving comments, key order, and formatting.
- YAML and JSON entries support optional templates and custom delimiters for transforming resolved versions.
- YAML updates support dot-notation paths, missing simple keys, and digest-pinned values.
- Registered YAML management with
track apply.
-
Bug Fixes
- Improved error messages for invalid templates, duplicate paths, unsupported targets, and malformed configurations.
- Failed updates now identify changes that were withheld.
-
Documentation
- Added YAML configuration guidance, examples, roadmap details, and blog posts covering YAML management and JSON formatting.
test: parallelize blocker-free tests in the race job's slowest packages @osterman (#3078)
## what- Add
t.Parallel()to every top-level test andt.Runclosure in the test files that have no parallelism blockers, across the packages that dominate the CI race job's wall clock:- Batch 1: all of
pkg/describe(46/49 tests);pkg/toolchainitself is excluded — itsTestMainroutes UI output into one captured buffer, so any parallel test that emitsui.*output races on it, and its heavy tests must stay serial regardless - Batch 2: 201 more files —
internal/exec(76),pkg/runner/step(29),pkg/ai/tools/atmos(29),pkg/config(22),pkg/pro(15),pkg/toolchain/installer(10),cmd/terraform(8),pkg/terraform/ui(7),cmd/toolchain(3),pkg/toolchain/registry/aqua(2)
- Batch 1: all of
- Leave three
pkg/describetests serial with explained//nolint:paralleltestdirectives (they invoke sibling test functions directly with their own*testing.T, so a secondt.Parallel()panics) - Cap test-process parallelism now that intra-package parallelism is real: the race sweep passes
-parallel=4(ATMOS_TEST_RACE_PARALLEL), and every acceptance-shard test process gets-parallel/-test.parallel= half the runner's cores (ATMOS_TEST_PARALLEL) — without it the 3-vCPU hosted macOS runners oversubscribed and several shards ran 50-150% slower (e.g. shard 1: 665s → 1134s) while 4-vCPU Linux/Windows were unchanged - Enable the
paralleltestlinter scoped (via the existingpath-exceptidiom in.golangci.yml) to exactly the converted files so they don't regress;tparallel(already on) caught seven parent tests deferring cleanup while running parallel subtests, nowt.Cleanup - Fix a pre-existing Windows flake surfaced by the wider run:
cmd/stack's fixture copy read thebasicscenario's gitignoredterraform.tfstate.d/…/terraform.tfstatewhile a shard-mate was writing it ("another process has locked a portion of the file"); the copy now skips Terraform runtime artifacts via the shared sandbox helper's predicate, exported astesthelpers.IsTerraformArtifact
why
The CI race job runs the whole ./... suite un-sharded, so its ~27-minute wall clock floors on the slowest packages — pkg/toolchain (556s), pkg/describe (327s), internal/exec (323s), cmd (247s), measured from run 34179823625. Go parallelizes across packages but runs tests within a package serially unless they call t.Parallel(). Locally pkg/describe drops from 18.1s to ~7-10s.
Method: mechanical conversion, then per-package go test -count=2 and go test -race as the arbiter — any file whose tests failed, panicked, or raced was reverted (96 files in the local pass, 13 more after CI runs exposed what local validation could not: package-cache resets (detectionCache, ClearBaseComponentConfigCache) and global-registry writes (atmosio.RegisterSecret) that the mutex makes race-clean but not order-safe, gomonkey code patching in TestGetAffectedComponents (skipped locally under -race and on darwin/arm64), and logical races on tests that swap package-level seams such as os.Stdin, executeTerraformLint, renderAndDeliver, tfoutput.SetDefaultExecutor, SetLastAuthContext, SetLastMergeContext, and one that hits pkg/terminal's unsynchronized lazy viper init). Files skipped or reverted use t.Setenv/t.Chdir (directly or via fixture helpers), mutate package globals (mock getters, SetAtmosConfig, viper, data.InitWriter/ui.InitFormatter, shared cobra command trees), share package caches, or raced under -race. All of cmd/list stays serial: its initTestIO/pkg/flags bindFlagToViper path races on package-level parsers. Those need dependency-injection refactoring before they can go parallel — that's the remaining lever on the race job's floor.
Two genuine production concurrency bugs surfaced by -race during this work (not fixed here): pkg/ui/markdown.NewRenderer races on glamour's shared style bytes when called concurrently (any parallel errors.Format), and the lazy viper.BindEnv init in pkg/ui/theme/pkg/terminal is unsynchronized (concurrent-map panic).
Validated: every converted package green under -count=2 and -race (zero data races), go vet clean, paralleltest lint clean over all scoped packages. Companion to #3077, which warms the race job's build cache and upsizes its runner.
references
- #3077 (CI cache overhaul this pairs with)
- docs/fixes/2026-09-01-race-detector-ci-job-timeouts.md (prior race-job timeout incident)
🤖 Generated with Claude Code
fix(ci): serialize website deploys and fix Sentry loader key @osterman (#3072)
## what- Add a
concurrencygroup towebsite-deploy-prod.yml(cancel-in-progress: false) so pushes tomaindeploy one at a time. - Add a per-PR
concurrencygroup towebsite-preview-deploy.ymlfor the same reason. - Pass the Sentry Loader public key instead of the full DSN to
docusaurus-plugin-sentry. - Record the timeline in
docs/fixes/2026-09-08-website-deploy-race-serialize.md.
why
atmos.tools went blank tonight. The live index.html referenced runtime~main.99cb7223.js and main.7524adc2.js, both 404.
The merge queue landed #3024 and #3058 on main three minutes apart. Each push triggered Website Deploy Prod, and the workflow had no concurrency group. Both runs execute aws s3 sync --delete against the single shared origin prefix, and Docusaurus content-hashes every bundle, so two builds never share asset names.
| Time (UTC) | Run | Action |
|---|---|---|
| 02:27:30 | A (#3024) | uploads main.7524adc2.js, runtime~main.99cb7223.js
|
| 02:27:55 | A | uploads index.html referencing A's bundles
|
| 02:27:58 | B (#3058) | --delete removes A's two bundles; uploads its own
|
B's sync plan was computed before A's index.html landed, so B never rewrote it. Result: A's index.html pointing at bundles B deleted, and both runs green.
cancel-in-progress stays false on purpose. Aborting a run mid-sync leaves a half-uploaded site, which is the same failure. With a group, the in-flight deploy finishes, the newest pending run queues behind it, and older pending runs are superseded.
Separately, the same console showed the Sentry loader script CORS-blocked. docusaurus-plugin-sentry v2 builds https://js.sentry-cdn.com/${DSN}.min.js, so the option must be the Loader Script public key, not the DSN. The corrected URL returns 200; the old one returned 404. This did not cause the outage.
references
- Deploy runs that raced: 34179330617 (#3024) and 34179504121 (#3058)
actionlintpasses on both workflows; the Sentry head tag was rendered locally through the installed plugin and produces the correctedsrc.
🤖 Generated with Claude Code
Summary by CodeRabbit
-
Bug Fixes
- Improved production deployments by preventing overlapping releases from racing or overwriting one another.
- Improved pull request preview deployments by isolating concurrent builds and skipping invalid preview runs.
- Updated Sentry configuration to use the public loader key, improving client-side error reporting setup.
-
Documentation
- Added an incident report documenting the deployment race, its resolution, validation, and follow-up actions.
feat(website): add Security & Trust page with live OpenSSF data @osterman (#3024)
## what- Adds a new
/securitypage to atmos.tools showing our OpenSSF Scorecard and OpenSSF Best Practices badge results. - Data is fetched live at site build time via a new Docusaurus plugin (
website/plugins/fetch-security-posture) — no client-side runtime dependency on the third-party APIs, and the build never fails if either API is temporarily unavailable (each dataset degrades gracefully with a "verify directly" fallback link). - The page leads with a prominent "verify this yourself" callout linking to the official Scorecard viewer and Best Practices project page, followed by the badge status, overall score with a build-time timestamp, and the full 18-check table.
- Adds a "Security" link to the footer's Resources column.
why
- Enterprises evaluating Atmos for adoption want to check our security posture, and a static badge image can go stale or feel unverifiable.
- Pulling the live JSON from the authoritative sources (
api.scorecard.dev,bestpractices.dev) and linking straight back to them lets evaluators verify the numbers themselves instead of trusting a cached badge. - This mirrors the existing build-time data-fetch pattern already used for GitHub stars and the latest release (
website/plugins/fetch-github-stars,fetch-latest-release), so it fits the codebase's established conventions rather than introducing a new fetch mechanism.
references
- Related:
cloudposse/atmosrecently earned a passing OpenSSF Best Practices badge (project #14393). - Upstream: comment on ossf/scorecard#3678 documenting that Scorecard can't yet detect a merge queue as a substitute for the "up-to-date branches" setting, including the Rulesets API call that exposes it. This is the gap behind the Branch-Protection finding the page annotates.
Summary by CodeRabbit
- New Features
- Added a Security page displaying OpenSSF Scorecard results and Best Practices status.
- Added score gauges, risk indicators, expandable check details, verification links, repository metadata, and scan timestamps.
- Added contextual explanations for checks whose scores may not reflect actual risk.
- Added a fallback message when security data is unavailable.
- Added automatic retrieval of security posture data during website builds.
- Added a Security link to the website footer.
- Documentation
- Added an announcement introducing the Security & Trust page and security reporting resources.
🚀 Enhancements
fix(examples): use real Infracost CLI @osterman (#3093)
## what- Replaces the public Infracost emulator with a pinned real Infracost CLI dependency and API-key documentation.
- Moves deterministic cost output into a dedicated cast fixture and regenerates both affected casts.
- Fixes the custom-command hook script path from the component working directory.
why
- Public examples must run the real integrations they demonstrate; deterministic stand-ins belong only to cast fixtures.
references
- N/A
Summary by CodeRabbit
-
New Features
- Added a complete NAT gateway fixture for the Infracost hook demonstration.
- Added configuration for Terraform plans with deterministic cost reporting.
-
Documentation
- Updated the Infracost example to use the real CLI, including version setup and API key requirements.
- Updated custom hook instructions to use reliable component-relative script paths.
-
Bug Fixes
- Improved cache action compatibility with older bootstrap CLI versions.
- Prevented intermittent package repository errors during automated builds and tests.
-
Demo Updates
- Refreshed hook recordings and aligned demo fixtures with current configurations.
fix(scaffold): write conflict markers on manual merge, fix init --update, fix --force+--update @jorrite (#3047)
## whatatmos scaffold generate --update(andatmos init --update, sharing the same engine) with the default--merge-strategy=manualnow writes real<<<<<<</=======/>>>>>>>conflict markers plus every non-conflicting change on a real merge conflict, instead of discarding the whole merge and writing nothing.atmos init --updatenow pins its initial--gitcommit to.atmos/init/metadata.yaml, the same wayatmos scaffold generatealready does, and reads it back as the merge base instead of defaulting to liveHEAD.--forcecombined with--updateis no longer a silent no-op: an unset--merge-strategynow defaults totheirsin that combination, and an explicitly-passedours/manualtogether with--force --updateis now a clear validation error instead of silently doing nothing.- Re-running
--updateagainst a file already left with unresolved conflict markers now fails fast with a specific message instead of an opaquethree-way merge failed. - Reworded every
--force-suggesting error hint across the merge engine to describe what--forceactually does now.
why
- The manual-merge-conflict bug is the still-open half of #2912. The issue was closed as resolved by #2989, but that PR fixed a different bug reported in the same thread (silent base-ref pinning) and never touched the manual-merge-strategy code path — confirmed by reproducing the original issue's exact repro steps live on current
mainbefore this fix. - Field-testing that fix surfaced that
atmos init --updatehas the exact base-ref-pinning bug #2989 fixed foratmos scaffold generate, because the fix lived only incmd/scaffoldand was never ported tocmd/init— the two commands' base-ref resolution had drifted apart. This PR extracts the shared logic intopkg/generator/gitinit.goso it can't drift apart a second time. --forcebeing silently ignored under--updatemade several existing error hints false, and left users with no way to push through a conflict without hand-editing the file.- The unresolved-markers re-run scenario is a new consequence of the conflict-marker fix (previously nothing was ever written on conflict, so it was impossible) — it's fixed in the same PR rather than shipped as a known gap.
See docs/fixes/2026-09-04-scaffold-init-update-merge-fixes.md for full context, the complete list of changed files, and how each fix was validated (unit tests plus live end-to-end verification against a built binary).
references
- Closes #2912
- Fix record:
docs/fixes/2026-09-04-scaffold-init-update-merge-fixes.md
Summary by CodeRabbit
- Bug Fixes
init --updateandscaffold generate --updatenow use the correct target and previously pinned merge base.- Merge conflicts are saved with clear conflict markers instead of failing without preserving results.
- Re-running updates on files with unresolved conflicts now provides a clear error.
--force --updatedefaults to using the template version; incompatible strategies are rejected.- Initial Git metadata is now preserved for future updates.
- Error messages provide clearer guidance for resolving conflicts and overwriting files.
- Dry-run updates no longer modify files when conflicts occur.
fix(ci): test summary/JUnit dropped OpenTofu runs and late diagnostics @osterman (#3082)
## Summaryatmos terraform test --cidiscarded every run and file event fromtofu test -json: OpenTofu emits onetest_run/test_fileevent per subject carrying onlystatus, with noprogressfield, and the parser only accepted events withprogress: "complete". Thetest_summaryevent has the same shape in both tools, so badge counts stayed right while the results table came out empty and<component>.junit.xmlreportedtests="0"on a passing run.- Both Terraform and OpenTofu emit an assertion-failure
diagnosticafter the run's final event; the parser only attached diagnostics that arrived before it, so failing runs lost their message andfile:line(and with them the::errorannotation and the summary's Details column) under Terraform too. - Fix:
testEventCompleteaccepts a bare terminalstatusas final;attachLateDiagnosticsreconciles diagnostics that arrive after their run. The stop-gapbackfillMissingTestJSONRunsguard stays as a last resort but now warns loudly if it ever fires. - Also closes out a related, already-fixed emulator-endpoint report: confirmed via
git merge-base --is-ancestorand a fresh Docker repro that #2942/#2960 are intact and shipped inv1.228.0; re-verification note appended to the existing fix doc.
Why
The report came from a repository whose toolchain pins tofu. This repo's own examples/terraform-tests fixture is Terraform-only (it uses variable blocks that OpenTofu rejects), so eleven local repro runs never hit it; diffing raw -json streams from both tools on a minimal module exposed the missing progress field and the diagnostic ordering.
Verification
- New regression tests use verbatim OpenTofu 1.12.5 streams; they failed before the parser change with every run reported as
run detail unavailable (pass)and no file/line. - End-to-end under OpenTofu (
components.terraform.command: tofu, one passing + one failing run,GITHUB_ACTIONS=true … --ci): JUnittests="2" failures="1"with real names andline="12"on the failure; summary lists both runs withtests/min.tftest.hcl:12in Details;::error file=…,line=12annotation emitted. - Terraform path unchanged:
examples/terraform-testsstill yieldstests="4"with all real run names.
References
docs/fixes/2026-09-08-ci-test-json-opentofu-runs-dropped.mddocs/fixes/2026-08-19-emulator-endpoint-job-container-network-join.md(re-verification note)
Summary by CodeRabbit
-
Bug Fixes
- Fixed CI test reporting for OpenTofu, including summaries, JUnit results, and assertion diagnostics.
- Improved handling of missing test-run records with safeguards against excessive synthesized entries.
- Clearly indicate when test results are incomplete due to truncation.
-
Documentation
- Added re-verification details for emulator networking and endpoint connectivity in the published release image.
-
Chores
- Updated website package version overrides.
fix(ci): keep pinned atmos schema snapshots durable across deploys @osterman (#3092)
## what- Fix pinned atmos schema URLs (e.g.
atmos.tools/schemas/atmos/atmos-manifest/1.228.0/atmos-manifest.json) 404ing shortly after every release. - Backfilled the pinned
atmos-manifest/atmos-configsnapshots for all 7 releases affected since the pinning feature shipped (v1.224.0-v1.228.0) directly in prod S3 (already live, no PR needed for that part). - Consolidated
.github/scripts/s3-deploy-with-charset.shand the 4 near-duplicate schema generate/publish steps inwebsite-deploy-prod.ymlinto two reusable composite actions:.github/actions/s3-deployand.github/actions/publish-atmos-schema. - Bumped 5 vulnerable transitive npm deps in
website/(js-yaml,svgo,joi,colord) viapnpm.overrides, closing 7 open Dependabot alerts — all within-major patch bumps.
why
website-deploy-prod.ymlonly regenerated the pinned per-release schema snapshotif: github.event_name == 'release', but every deploy (including ordinary pushes tomain) ranaws s3 sync --deleteagainst the same S3 prefix. Since a pinned snapshot from an older release was absent from that run's local build,--deletepruned it — so a pinned URL survived only until the next routine deploy. The docs' own example version (1.219.0) had already succumbed to this.- The fix excludes pinned schema paths from
--deleteand publishes each release's new snapshot via a direct, non-deletingaws s3 cp, decoupling it from the routine sync's deletion scope. - The composite-action refactor was requested to consolidate CI logic that had grown into 5 near-identical steps plus a loose
.github/scripts/*.sh, per this repo's "CI scripts live in local actions" convention. - The npm bumps address a
git push-triggered security-remediate pass; the current branch already had an open diff, so the fixes landed here directly.
references
- Related to #2592 (original pinned-schema feature this PR fixes a regression in)
Summary by CodeRabbit
-
New Features
- Added automated generation and publishing of Atmos schemas, including version-specific snapshots for production releases.
- Added shared website deployment automation for synchronizing content to S3 while preserving required metadata.
-
Improvements
- Standardized production and preview website deployments around reusable automation.
- Improved consistency of schema publishing across release and non-release deployments.
fix(auth): clear Azure realm MSAL cache on logout so re-login is fresh @aknysh (#3087)
## whatatmos auth logout(including--all --force) now removes the realm-scoped Azure MSAL token cache (~/.azure/atmos/{realm}/msal_token_cache.json) in addition to the keyring entry and the Atmos device-code token it already cleared.- The shared Azure CLI cache (
~/.azure/msal_token_cache.json) is intentionally preserved — it is co-owned with a user's ownaz loginsession. - Applies to the
azure/device-codeandazure/interactiveproviders (interactive embeds device-code);azure/cliandazure/oidcnever create a realm MSAL cache and are unaffected.
why
- After a role or PIM change, users found that
atmos auth logoutfollowed byatmos auth logindid not pick up the new access — they kept getting403 AuthorizationFailed(e.g.Microsoft.ContainerService/managedClusters/read), andatmos auth whoamishowed the credential still expiring on the old session's clock. - Root cause: the device-code/interactive providers build their MSAL client from the realm cache and attempt a silent token acquisition before any interactive flow. Logout removed the Atmos device-code token (
~/.cache/atmos/azure-device-code/<provider>/token.json) but never the realm MSAL cache — a different file — so the next login silently re-minted a token from the stale cached account/refresh token, which predated the PIM elevation. - The only workaround was to delete the realm cache by hand (
rm ~/.azure/atmos/<realm>/msal_token_cache.json). This makes logout actually forget the session, so re-login is genuinely fresh.
tests / validation
- Reproducing tests written first and confirmed failing on the unpatched code, now passing: logout removes the realm MSAL cache for both device-code and interactive providers, preserves the shared
azcache, and treats a missing cache as a clean no-op. Added a directRemoveMSALCacheunit test (realm removal, missing-file no-op, empty-realm safety guard, realos.Removefailure) and aLogouterror-path test. - Changed functions fully covered (
resolveMSALCachePath100%,NewMSALCache100%,Logout100%,RemoveMSALCache90.9% — only the untestableos.UserHomeDirerror return remains). go test ./pkg/auth/...green;atmos fix lint(patch-scoped CI gate) reports 0 issues.
references
- Fix doc:
docs/fixes/2026-09-08-azure-logout-msal-cache-stale-session.md - Follows the Azure auth line: #2862 (
azure/interactive), #2861 / #2890 (Azure CLI cache fixes)
Summary by CodeRabbit
-
Bug Fixes
- Azure logout now removes realm-specific cached authentication data for device-code and interactive sign-ins.
- Shared Azure CLI authentication remains available after logout.
- Logout succeeds cleanly when no realm-specific cache exists and reports cache-removal failures when they occur.
-
Documentation
- Added documentation describing Azure logout behavior, cache handling, provider differences, and validation results.
fix: preserve prompted component/stack across profile-fallback re-exec @osterman (#3080)
## WhatCarries interactively-resolved component/stack values through a profile-fallback re-exec, so the re-exec'd child process doesn't re-prompt for values the user (or an earlier prompt) already supplied.
ReExecContext (pkg/auth/profile_fallback.go) distinguishes "resolved via prompt" from "supplied on the command line" via ComponentPrompted/StackPrompted flags on schema.ConfigAndStacksInfo — only prompted values are injected into the child's re-exec argv, since command-line-supplied values are already present in os.Args and re-adding them would duplicate a positional argument.
Why
When an invalid identity config triggers the interactive profile fallback (auth.MaybeOfferProfileFallbackForIdentity), the process re-execs itself with the newly-picked profile. Previously, if component/stack had just been resolved via an interactive prompt (not passed as CLI args), the re-exec'd child had no record of that and prompted the user again for the same values — a redundant, confusing extra step in an already-interruptive flow.
References
pkg/auth/profile_fallback.go(ReExecContext,maybeOfferProfileFallback,reExecWithProfile)internal/exec/utils_auth.go(offerIdentityProfileFallback,resolveIdentityConfigError)cmd/terraform/shared/execution.go(promptMissingComponent/promptMissingStacknow set the*Promptedflags)pkg/schema/schema.go(ConfigAndStacksInfo.ComponentPrompted/StackPrompted)
Summary by CodeRabbit
-
Bug Fixes
- Preserved component and stack values selected through interactive prompts during authentication and profile fallback.
- Prevented unnecessary repeated prompts when authentication requires a profile-based retry.
- Ensured backend create, update, delete, describe, and list commands consistently retain prompted values throughout execution.
-
Tests
- Added coverage for prompted-value tracking, authentication retries, profile fallback, and backend command behavior.
fix(terraform-ui): clean up terraform plan --ui output @osterman (#3083)
## what- Fixes deprecation-warning log lines from
atmos terraform plan --uithat embedded a raw blank line from Terraform's multi-paragraph diagnostic detail, making the trailingaddress=/file=/line=metadata appear to jump to the far right of the terminal and wrap onto its own line. - Fixes the plan summary/dependency tree silently disappearing after a successful plan with no error or explanation.
- Fixes broken indentation for nested arrays (2+ levels deep, e.g. CloudFront's
ordered_cache_behavior) in the plan tree's JSON attribute-diff rendering. - Adds a
# (N unchanged attributes hidden)footer to the plan tree, mirroring Terraform's own plan-output convention. - Hardens a previously-unbounded subprocess call in
tests/cli_describe_identity_test.gowith a timeout.
why
- The deprecation-warning bug was in
extractFirstSentence: it fell through to returning Terraform's entire multi-paragraph diagnostic detail (including the embedded blank line) instead of a single clean sentence, and was also picking the generic "derived from a deprecated source" lead-in over the actual, specific deprecation message. It now stops at the first paragraph break and prefers the specific, actionable final sentence. - The missing plan summary was a real bug, not cosmetic: the second
terraform show -json <planfile>subprocess (used to build the tree/summary after a successful plan) never received the component's effective environment (TF_DATA_DIR, credentials) the way the original plan/apply subprocess does, so it could fail independently of the plan itself — and that failure was silently discarded with zero indication anything went wrong. It now inherits the same environment, and any remaining failure is logged with the real subprocess stderr instead of a bare "exit status 1". - The broken array indentation was a reproducible bug in the
jsoniterlibrary'sMarshalIndent, confirmed against a minimal standalone repro;ConvertToJSONnow marshals compact withjsoniterand re-indents with stdlibencoding/json, which isn't subject to the bug. - The unchanged-attributes footer closes a parity gap where the diff-only attribute list gave no sense of how much of the resource actually stayed the same, unlike Terraform's native output.
- The test hardening turns a previously-observed subprocess hang (which took down the entire test binary via
go test's global timeout) into a fast, attributable failure on just the affected subtest.
references
- N/A
Summary by CodeRabbit
-
New Features
- Plan summaries now show hidden unchanged attributes with correct singular and plural wording.
- Terraform subprocesses honor the configured environment and provide clearer failure details.
- Diagnostic messages are rendered as concise, single-line summaries without boilerplate.
-
Bug Fixes
- Warnings are now logged when plan summaries cannot be rendered.
- Sensitive information in Terraform errors is masked.
- Nested JSON arrays are formatted correctly.
- Identity description tests now fail promptly instead of hanging.
fix(secret): stop leaking the resolved --stack into viper's override layer @osterman (#3075)
## whatparseScopeStack(cmd/secret/shared.go) andparseInitScope(cmd/secret/init.go) no longer write the resolved stack into viper's override layer withviper.Set. A stack chosen at the interactive prompt is now recorded on the command's own--stackflag via a smalladoptPromptedStackhelper, which the component completion still sees through viper's flag binding.- New regression tests in
cmd/secret/stack_override_test.go: the exact failing sequence (import --stack prod, thenset --stack devmust load withdev), the prompted-stack propagation through the flag binding, and the empty-choice no-op. - Fix record:
docs/fixes/2026-09-07-secret-stack-viper-override-leak.md.
why
[race] non-acceptance test suitefailed on amainpush (run 34179504147) and on #3069 (run 34181209571), each time in one of the two global-scopesecret settests withno global declaration was found in the stack. Replaying the CI shuffle seeds locally reproduces it deterministically; unshuffled runs pass.- Root cause: a viper override outranks every later flag parse for the life of the process. After any
secretcommand ran with--stack prod, the next command's--stack devresolved toprod, its stack filter matched nothing, and the lookup failed. Bisected toTestRunSecretImport_FromStoreMode→ global-scope set; a probe showed the pflag reset to""whileviper.GetString("stack")stayed"prod"even afterSetDefault, i.e. the override layer. - The override only existed to make a prompted stack visible to the component prompt's completion. Setting the flag gives the same visibility, is reset with every other flag, and removes a real in-process footgun for anything that runs more than one
secretcommand per process.
references
- Failing jobs: https://github.com/cloudposse/atmos/actions/runs/34179504147/job/101915514726 (main), https://github.com/cloudposse/atmos/actions/runs/34181209571/job/101920782810 (#3069)
Summary by CodeRabbit
-
Bug Fixes
- Fixed an issue where interactively selected secret stacks could carry over to subsequent commands.
- Secret commands now preserve required-flag errors when no stack is selected.
- Empty interactive stack selections no longer modify command state or secrets.
- Improved error messages when stack flag handling fails.
-
Tests
- Added coverage for repeated commands, interactive stack selection, missing flags, and non-interactive initialization.
-
Documentation
- Documented the secret stack selection and command-isolation fix.
fix(source): retry DNS/connect failures for JIT sources; clone JIT test fixtures locally @osterman (#3073)
## whatisRetryableGitError(pkg/downloader/get_git.go) now recognises OS-level resolver and connect failures in git's own wording —could not resolve host,no such host,could not connect to server,failed to connect to,network is unreachable,name resolution— with tests for each plus a guard that a real403from the remote is still not retried.- JIT source provisioning (
pkg/provisioner/source/vendor.go) always passes a retry policy to the git getter: the source's ownretry:when set, otherwise a bounded default of 3 attempts with exponential backoff (1s→8s, 20% jitter). Only transient transport failures are retried; auth failures and missing refs still fail fast, andmax_attempts: 1opts out. Theterraform/helmfile/packersourcedocs describe the default. - The
TestJITSource_*andTestJITSource_MetadataComponentSubpath*acceptance tests clone from a throwaway localgit::file://repository (tests/jit_source_local_repo_test.go) instead ofgithub.com— same go-getter git path (clone,ref=,depth=1,//subpath), no network, runs on all three OSes. The rewrite happens in each test's sandboxed copy of the fixture; the checked-in fixture keeps its real-world URIs.RequireGitHubAccessis dropped from the two tests that used it. - Fix record:
docs/fixes/2026-09-07-jit-source-network-flakes.md.
why
Acceptance Tests (windows, shard 2/10)on #3069 failed inTestJITSource_PackerOutputwithFailed to connect to github.com:443 after 61 ms: Could not connect to server, right after the same job's checkout had hitCould not resolve host: github.com. On Windows, harden-runner's agent fronts DNS with a local proxy and installs a firewall allow-rule per resolved IP; under a test shard's process churn it occasionally drops a query or races the allow-rule. These blips recur several times a week (see thedocs/fixes/entries from 2026-08-10, 2026-09-02, 2026-09-03) and nothing in the affected PRs touches source provisioning.- Two gaps turned a one-second blip into a red build: the git retry predicate didn't match either message, and JIT provisioning had no retry unless a source configured one — so a user's
terraform planwith a JIT source fails on the same blip on a laptop or in their CI. Retrying only transport-level failures, with a bounded budget, fixes the user-facing behaviour without masking real errors. - The JIT tests exist to cover the provisioning path, not GitHub reachability. A Gitea/testcontainers stand-in was rejected because GitHub's Windows and macOS runners can't run Linux containers — the fixture would skip exactly where the flake happens.
references
- Failing job: https://github.com/cloudposse/atmos/actions/runs/34172538919/job/101897823452 (from #3069)
- Prior records:
docs/fixes/2026-09-02-vendor-pull-dns-resolution-flake.md,docs/fixes/2026-09-03-harden-runner-windows-dns-restore-race.md
Summary by CodeRabbit
-
Bug Fixes
- Git-based source downloads now automatically retry transient DNS, connection, and network failures up to three times with exponential backoff.
- Missing references and authentication failures are not retried, except for a short retry window when credentials are brokered by Atmos.
- Existing custom retry settings remain unchanged, including the option to disable retries.
-
Documentation
- Documented default retry behavior across Helmfile, Packer, and Terraform source configuration.
fix(ci): never create a bulk-run status without a real component @osterman (#3058)
## what- Guard
createCheckRun/updateCheckRunand hardenFormatStatusContextso a CI status can never be created or referenced without a real, resolved component and stack. - Add new
before.terraform.{plan,apply,destroy}.aggregateCI hook events, fired once the component graph is resolved and filtered, that create one real pending check-run per component up front for--affected/--allruns — mirroring the existing after-aggregate path. - Add the missing per-node before-CI-hook for
deploy(it already had a per-node after-hook but no before counterpart), gated so it never fires if a user before-hook aborts the node. - Make the after-aggregate CI resolver run via
deferinExecuteTerraformso it's guaranteed to fire even if a later step errors and returns early.
why
atmos terraform deploy --affected --upload-status(and--all, and plan/apply/destroy) created a pending GitHub commit status namedatmos/deploy/<stack>/— with no component, because bulk selection resolves its component list after the global before-hook already fired. That status was never updated, leaving PR checks stuck on "Waiting for status to be reported" forever, even though the run succeeded.- A CI status is always about a specific component; the fix makes that an enforced invariant rather than an incidental convention, and gives bulk runs the same real, resolvable pending-status UX that single-component runs already have.
references
- Closes #3007
Summary by CodeRabbit
-
New Features
- Added Terraform CI hooks that create pending checks for each resolved component before plan, apply, or destroy operations begin.
- Added aggregate before-operation hooks for Terraform plan, apply, and destroy workflows.
- CI checks now include resolved stack and component context.
-
Bug Fixes
- Prevented malformed check runs and status contexts when required context is missing.
- Ensured CI results are finalized after later Terraform processing errors.
- Improved handling of CI hook failures without interrupting Terraform execution.
- Platform and race-condition test safeguards can no longer be bypassed by the skip environment setting.
🤖 Automatic Updates
build(deps): bump github.com/containerd/containerd/v2 from 2.3.3 to 2.3.5 in the go_modules group across 1 directory @[dependabot[bot]](https://github.com/apps/dependabot) (#3098)
Bumps the go_modules group with 1 update in the / directory: [github.com/containerd/containerd/v2](https://github.com/containerd/containerd).Updates Sourced from github.com/containerd/containerd/v2's releases.
Welcome to the v2.3.5 release of containerd!
The fifth patch release for containerd 2.3 contains various fixes
and updates including security patches.
Please try out the release binaries and report any issues at
https://github.com/containerd/containerd/issues.
... (truncated)
github.com/containerd/containerd/v2 from 2.3.3 to 2.3.5
Release notes
containerd 2.3.5
Security Updates
Highlights
Image Distribution
Runtime
Snapshotters
Contributors
Commits
1294c24 Merge pull request #14092 from samuelkarp/prepare-release-2.3.5
db68d72 Prepare release notes for v2.3.5
be419b0 Merge commit from fork
84ea25b Merge commit from fork
5db0399 Merge pull request #14094 from k8s-infra-cherrypick-robot/cherry-pick-14085-t...
9f6be86 Fix data races and a deadlock in the byte stream helpers
9ec55f0 cri: cancel ExecSync IO drain on context cancellation
c535779 archive: skip redundant opaque whiteout walks
3684f86 Merge pull request #14063 from k8s-infra-cherrypick-robot/cherry-pick-14057-t...
7459b1f Merge pull request #14062 from k8s-infra-cherrypick-robot/cherry-pick-14012-t...
Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
@dependabot rebasewill rebase this PR@dependabot recreatewill recreate this PR, overwriting any edits that have been made to it@dependabot show <dependency name> ignore conditionswill show all of the ignore conditions of the specified dependency@dependabot ignore <dependency name> major versionwill close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself)@dependabot ignore <dependency name> minor versionwill close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself)@dependabot ignore <dependency name>will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself)@dependabot unignore <dependency name>will remove all of the ignore conditions of the specified dependency@dependabot unignore <dependency name> <ignore condition>will remove the ignore condition of the specified dependency and ignore conditions
You can disable automated security fix PRs for this repo from the Security Alerts page.
fix(deps): update module github.com/gofrs/flock to v0.13.1 @[renovate[bot]](https://github.com/apps/renovate) (#3091)
This PR contains the following updates:| Package | Change | Age | Confidence |
|---|---|---|---|
| github.com/gofrs/flock | v0.13.0 → v0.13.1
|
Release Notes
gofrs/flock (github.com/gofrs/flock)
v0.13.1
What's Changed
- chore: update Path method comment by @thiagola92 in #129
- chore(deps): bump the github-actions group with 2 updates by @dependabot[bot] in #130
- chore(deps): bump golang.org/x/sys from 0.37.0 to 0.38.0 in the gomod group by @dependabot[bot] in #131
- chore(deps): bump golang.org/x/sys from 0.38.0 to 0.39.0 in the gomod group by @dependabot[bot] in #132
- chore(deps): bump the github-actions group with 5 updates by @dependabot[bot] in #133
- chore(deps): bump golang.org/x/sys from 0.39.0 to 0.40.0 in the gomod group by @dependabot[bot] in #134
- chore(deps): bump the github-actions group with 3 updates by @dependabot[bot] in #135
- chore(deps): bump golang.org/x/sys from 0.40.0 to 0.41.0 in the gomod group by @dependabot[bot] in #136
- chore(deps): bump the github-actions group with 3 updates by @dependabot[bot] in #137
- chore(deps): bump golang.org/x/sys from 0.41.0 to 0.42.0 in the gomod group by @dependabot[bot] in #138
- chore(deps): bump the github-actions group with 2 updates by @dependabot[bot] in #139
- chore(deps): bump golang.org/x/sys from 0.42.0 to 0.43.0 in the gomod group by @dependabot[bot] in #140
- chore(deps): bump the github-actions group with 2 updates by @dependabot[bot] in #141
- chore: improve workflows by @ldez in #142
- chore(deps): bump github/codeql-action from 3.35.3 to 4.35.2 in the github-actions group by @dependabot[bot] in #143
- chore(deps): bump golang.org/x/sys from 0.43.0 to 0.45.0 in the gomod group by @dependabot[bot] in #144
- chore(deps): bump the github-actions group across 1 directory with 3 updates by @dependabot[bot] in #145
- chore(deps): bump the github-actions group with 4 updates by @dependabot[bot] in #147
- chore(deps): bump golang.org/x/sys from 0.45.0 to 0.46.0 in the gomod group across 1 directory by @dependabot[bot] in #146
- chore(deps): bump golang.org/x/sys from 0.46.0 to 0.47.0 in the gomod group by @dependabot[bot] in #149
- chore(deps): bump the github-actions group across 1 directory with 6 updates by @dependabot[bot] in #150
- chore: update linter by @ldez in #154
- chore(deps): bump github.com/stretchr/testify from 1.11.1 to 1.12.1 in the gomod group across 1 directory by @dependabot[bot] in #153
New Contributors
- @thiagola92 made their first contribution in #129
Full Changelog: gofrs/flock@v0.13.0...v0.13.1
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 github.com/hashicorp/terraform-config-inspect digest to 75d64de @[renovate[bot]](https://github.com/apps/renovate) (#3054)
This PR contains the following updates:| Package | Type | Update | Change |
|---|---|---|---|
| github.com/hashicorp/terraform-config-inspect | require | digest | 2fb54c2 → 75d64de
|
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 module github.com/gabriel-vasile/mimetype to v1.4.15 @[renovate[bot]](https://github.com/apps/renovate) (#3079)
This PR contains the following updates:| Package | Change | Age | Confidence |
|---|---|---|---|
| github.com/gabriel-vasile/mimetype | v1.4.13 → v1.4.15
|
Release Notes
gabriel-vasile/mimetype (github.com/gabriel-vasile/mimetype)
v1.4.15: fix int overflow on 32bit arch
What's Changed
This release fixes an integer overflow panic on 32 bit architectures and retracts the previous release.
- cdf: fix slice index panic on 32bit arch by @gabriel-vasile in #829
- fuzz: fuzz for 32 bit too to avoid panics like #829 by @gabriel-vasile in #830
- readme: add back contributing section, for #827 by @gabriel-vasile in #831
Full Changelog: gabriel-vasile/mimetype@v.1.4.14...v1.4.15
v1.4.14: pyc, pcap, cycloned, gedcom support
Media type changes
- newly added:
application/x-bytecode.python,application/vnd.tcpdump.pcap,application/vnd.cyclonedx+xml,application/vnd.cyclonedx+json,text/vnd.familysearch.gedcom - superseded and demoted to aliases:
image/vnd.mozilla.apngsuperseded byimage/apng
video/x-matroskasuperseded byvideo/matroska
application/x-rar-compressedsuperseded byapplication/vnd.rar
Full changelog
- scan: fix FullWord combined with other flags by @gabriel-vasile in #763
- shebang: individual flags for each signature by @gabriel-vasile in #764
- apk: add support with zipflinger virtual entry by @dmlambea in #761
- apng: search acTL for several chunks by @gabriel-vasile in #770
- CycloneDX: add support for JSON and XML types by @gabriel-vasile in #771
- pyc: add support; close #416 by @gabriel-vasile in #773
- mp3: harden ID3v2 check by @gabriel-vasile in #783
- MP3: add a frame parser and do linear search for headers by @gabriel-vasile in #778
- chore: go fix by @zdiff in #797
- csv: drop DropLastLine for better performance by @gabriel-vasile in #799
- ndjson: fix edge-case with new-lines by @gabriel-vasile in #803
- all: increase defaultLimit from 3072 to 4096 by @gabriel-vasile in #796
- rar: use vnd.rar instead of unregistered name by @gabriel-vasile in #809
- Add header "Message-ID" to eml hints to correctly detect type in enron email dataset by @zhel1 in #791
- ttf: use layout tables for detection too; fix #808 by @gabriel-vasile in #810
- ttf: add more missing tables by @gabriel-vasile in #811
- cdf: add parser for more reliable results by @gabriel-vasile in #804
- pcap: add support by @gabriel-vasile in #817
- all: run go fix by @gabriel-vasile in #818
- mkv: use IANA registered media type by @gabriel-vasile in #819
- docs: mention String() can return different values by @gabriel-vasile in #820
- charset: stop checking for ascii if utf8 is satisfied by @gabriel-vasile in #768
- Add support for text/vnd.familysearch.gedcom content type by @gorodet-sky in #789
New Contributors
- @zhel1 made their first contribution in #791
- @gorodet-sky made their first contribution in #789
Full Changelog: gabriel-vasile/mimetype@v1.4.13...v1.4.14
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 module github.com/charmbracelet/x/ansi to v0.11.8 @[renovate[bot]](https://github.com/apps/renovate) (#3070)
This PR contains the following updates:| Package | Change | Age | Confidence |
|---|---|---|---|
| github.com/charmbracelet/x/ansi | v0.11.7 → v0.11.8
|
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.