github cloudposse/atmos v1.218.0-rc.3

pre-release4 hours ago

🚀 Enhancements

fix(ci): classify output-only plan summaries @osterman (#2407) ## Summary

Fixes CI summary classification for output-only Terraform/OpenTofu plans on the stdout fallback path. These plans already had HasChanges=true, but the rendered summary could still use the no-change heading and badge because the template context only inferred output changes from parsed output values.

Root Cause

JSON plan parsing has structured output_changes, but stdout parsing only sees Changes to Outputs:. The parser set the changed-result text but did not preserve an explicit output-change signal for the template context.

Changes

  • Add TerraformOutputData.HasOutputChanges.
  • Set it from JSON plan output changes and stdout Changes to Outputs: detection.
  • Use the explicit signal when building the Terraform summary template context.
  • Add an anonymized end-to-end regression test for the after.terraform.plan summary path.
  • Document the fix under docs/fixes.

Validation

  • go test ./pkg/ci/plugins/terraform -run TestOnAfterPlan_OutputOnlyStdout_RendersOutputChangeSummary -count=1
  • go test ./pkg/ci/plugins/terraform -run 'OutputOnly|OnAfterPlan|Template' -count=1
  • go test ./pkg/ci/plugins/terraform -count=1
  • pre-commit hooks during commit, including Go build, gofumpt, tidy check, and golangci-lint

Summary by CodeRabbit

  • Bug Fixes
    • Fixed CI summary display for Terraform and OpenTofu plans containing output-only changes. Previously, plans that only modified output values incorrectly displayed "No Changes" heading and badge on CI summaries. They now correctly display "Output Changes" heading with appropriate messaging.

Review Change Stack

fix(profile,auth): list table truncation, alignment, and active-profile indicator @osterman (#2365) ## what
  • Profile list table now shows every configured profile instead of just the first row, and flags the currently-active profile with a green dot resolved from --profile / ATMOS_PROFILE / profiles.default.
  • Auth list table (providers + identities) gets the same row-truncation fix, so atmos auth list --format=table no longer drops rows.
  • Profile-fallback path made viable from terraform: TerraformPreHook now offers the profile-switch fallback (matching atmos auth login/exec/shell/env/console/whoami) when the loaded config has no usable auth, and --profile is propagated to auth subcommand config init so the re-exec re-loads the picked profile's identities.
  • profiles.base_path synced to global viper in LoadConfig so the auth profile fallback can discover profiles in custom locations.
  • New exported cfg.GetActiveProfiles(*schema.AtmosConfiguration) helper centralises the resolution order used by both the renderer and any future callers.
  • Regression tests added in pkg/profile/list, pkg/auth/list, pkg/config, pkg/auth, and cmd/auth_config_test.go.

why

  • bubbles/table reserves one of the configured WithHeight() lines for the header — passing len(rows) rendered len(rows)-1 data rows. With 2 profiles, only 1 was visible. The data layer (--format=json) was always correct, so the bug was purely in the table renderer at three call sites.
  • After fixing height, the cursor row (row 0) was rendered with a blue highlight from bubbles' default Selected style. Aliasing s.Selected = s.Cell removed the highlight but re-applied Padding(0,1) to the whole joined row, shifting the cursor row 1 column right of every other row. Using an empty lipgloss.NewStyle() removes the highlight without the double-padding.
  • The active-profile dot was being mangled into in TTY output because bubbles' renderRow calls runewidth.Truncate(value, width, "…"), and runewidth counts ANSI escape bytes as visible width: a pre-styled \x1b[…m●\x1b[0m reports width ~8 and gets truncated to a single . Putting a plain in the cell and post-styling after View() returns sidesteps the bug.
  • The Terraform pre-hook surfaced a bare "no default identity" error in the exact scenarios the auth commands' fallback already handles. Wiring the same fallback in keeps the UX consistent.
  • Without syncing profiles.base_path to the global viper, the fallback could not see profiles in user-configured locations and silently returned no candidates.

references

  • Symptom: running atmos profile list in a directory with multiple profiles displayed only the first one in the table view (JSON output was unaffected).

Summary by CodeRabbit

  • New Features

    • Profile list now displays active profiles with a visual indicator (●).
    • Added support for default profiles in configuration.
    • Profile fallback suggestions when authentication configuration is incomplete.
  • Bug Fixes

    • Fixed table rendering to properly display all rows with multiple entries.
    • Eliminated duplicate help flag output during configuration loading.
    • Improved profile resolution for edge cases in flag parsing.

Review Change Stack

fix(ci): use active atmos identity for S3 planfile uploads @calxus (#2372) ## what
  • Make the S3 planfile store authenticate via the active Atmos auth identity instead of always falling back to the AWS SDK default credential chain.
  • Add an IdentityAwareBackend interface to pkg/ci/artifact so the registry can inject an AuthContextResolver (mirroring the existing pkg/store pattern).
  • Refactor the S3 backend to defer client initialization when an identity is configured and resolve credentials lazily via the injected resolver.
  • Wire ci.createPlanfileStore to propagate info.Identity (the resolved --identity / ATMOS_IDENTITY value) and a resolver built from info.AuthManager.
  • Add unit tests covering registry resolver injection, S3 lazy init paths, and attachIdentity precedence.

why

  • atmos terraform plan was succeeding in CI (the Terraform subprocess inherits the prepared environment) but the subsequent S3 planfile upload failed with IMDS errors because the upload runs in the parent atmos process, which had no awareness of the active identity.
  • Common CI setups assume GitHub OIDC + IAM role assumption is enough — the parent process has no ambient AWS credentials to fall back on, so the failure surfaced as a confusing AWS-side error rather than an Atmos config issue.
  • The fix reuses the same identity resolver pattern already used by SSM / Azure Key Vault / GSM stores, so AWS-store auth wiring stays consistent across the codebase.
  • When no identity is in scope, behavior is unchanged: the default credential chain is still used, so existing setups that relied on ambient credentials are unaffected.

references

Summary by CodeRabbit

  • New Features

    • Artifact storage backends now support identity-aware authentication with lazy credential resolution and the ability to attach or override an auth identity/resolver after store creation.
  • Tests

    • Added tests validating identity propagation, deferred auth initialization, resolver invocation semantics, and correct error handling when auth resolution fails.
fix(ci): wire PR comment posting into terraform plan hook @joshAtRula (#2405) Resolves the regression reported in #2367 where `ci.comments.enabled: true` had no effect: the schema field existed and `ATMOS_CI_COMMENTS_ENABLED` was wired, but no code path ever called the GitHub API to create or update a PR comment. The terraform plugin now posts a PR comment on `after.terraform.plan` whenever comments are enabled and the event is a pull request.

Interface changes:

  • Add Provider.PostComment(ctx, *PostCommentOptions) (*Comment, error) to the CI provider interface, along with CommentBehavior (create / update / upsert) and result types.
  • pkg/ci/providers/github/comments.go: upsert via HTML marker <!-- atmos:ci:{command}:{component}:{stack} --> using Issues.ListComments + EditComment / CreateComment. 403/404 errors carry hints directing users to the permissions: pull-requests: write workflow grant. Pagination-aware marker scan.
  • Generic provider returns ErrCIOperationNotSupported.

Plugin wiring:

  • pkg/ci/plugins/terraform/comments.go: new postComment() handler reuses the summary rendered by writeSummary() so ci.templates.terraform.plan overrides apply to both the job summary and the PR comment body.
  • Hooked into onAfterPlan as warn-only (apply/deploy intentionally excluded for this fix — plan-only scope).
  • Skips silently when the event is not a PR, repo context is incomplete, or the rendered summary is empty.

Schema:

  • CICommentsConfig.Enabled changed from bool to *bool so nil (unset) can default off without colliding with explicit false. This matches the sibling CIChecksConfig.Enabled and prevents upgrading installations from silently starting to post comments.
  • ATMOS_CI_COMMENTS_ENABLED env override updated for pointer-bool semantics; tests extended to cover the unset-nil path.

Errors:

  • Add ErrCICommentPostFailed, ErrCICommentListFailed, ErrCICommentUpdateFailed, ErrCICommentNotFound.
  • Incidentally convert 4 pre-existing dynamic fmt.Errorf sites in pkg/config/utils.go:GetContextPrefix to wrap a new ErrStackNamePatternPartMissing sentinel; gofumpt reformatted those lines, which pulled them into lint's new-from-rev diff. Error message text is preserved so existing tests still match.

Tests:

  • 13 httptest-backed tests for the GitHub comments API covering upsert create, upsert update (marker matches), create-always, update-returns- not-found, input validation, 403/404 hint propagation, pagination, and default behavior.
  • 8 handler tests covering enabled + PR present, skip when disabled, skip when unset (nil), skip when no PR, skip when no repo context, warn-only on API failure, and behavior-flag respect.
  • registry_provider_test and handlers_test mockProvider updated for the new interface method.

Docs:

  • docs/prd/native-ci/providers/github/pr-comments.md: status flipped from Deferred to Shipped with locked-in decisions.
  • implementation-status.md: PR Comments row now Done; v14.0 changelog entry added; totals updated to 148/151.

Closes #2367

what

  • Wires up PR comment posting for the native CI integration, which was previously schema-only (ci.comments.enabled: true did nothing).
  • Adds Provider.PostComment to the CI provider interface with create/update/upsert semantics.
  • Implements the GitHub provider's comment API (pkg/ci/providers/github/comments.go) using HTML-marker-based upsert (<!-- atmos:ci:{command}:{component}:{stack} -->) so repeat runs update the existing comment instead of stacking new ones.
  • Hooks comment posting into onAfterPlan (plan-only, as agreed) and reuses the already-rendered summary so ci.templates.terraform.plan overrides apply to both the GitHub job summary and the PR comment body.
  • Changes CICommentsConfig.Enabled from bool to *bool so unset (nil) defaults to off and upgrading installations don't silently start posting comments.
  • Returns actionable hints on 403/404 errors that point users to the missing permissions: pull-requests: write workflow grant.
  • Adds 4 new sentinel errors (ErrCICommentPostFailed, ErrCICommentListFailed, ErrCICommentUpdateFailed, ErrCICommentNotFound).
  • Incidentally: converts 4 pre-existing dynamic fmt.Errorf sites in pkg/config/utils.go:GetContextPrefix to a new ErrStackNamePatternPartMissing sentinel — error message text preserved so existing tests still match.

why

  • Customer-facing regression in v1.216.0: the docs at https://atmos.tools/cli/configuration/ci/comments promise PR comments, and the schema field + ATMOS_CI_COMMENTS_ENABLED env var suggest the feature works, but no code path ever called the GitHub API. Users configuring ci.comments.enabled: true in a pull_request workflow see the job summary render correctly but no comment on the PR.
  • The PRD (docs/prd/native-ci/providers/github/pr-comments.md) explicitly marked the design as "Deferred" and implementation-status.md tracked it as "Not Started". This PR locks in the design decisions and ships the feature.
  • *bool for Enabled matches the pattern already used by CIChecksConfig.Enabled and gives us room to default off without breaking explicit false values — preventing silent behavior change on upgrade.
  • Reusing the rendered summary for the comment body (rather than rendering a second template) keeps user template customization coherent: one override point (ci.templates.terraform.plan) controls what shows up in both surfaces.
  • 403/404 hinting mirrors the pattern already established in checks.go for the commit status API, since the permissions failure mode is identical and common.

scope

  • Plan-only, deliberately. apply and deploy do not post comments. Review happens on the PR thread for plans; apply/deploy outcomes live in the checks pane. Revisit if users ask.
  • Custom ci.comments.template override is still a follow-up — the comment currently reuses the summary template. The schema field is preserved so this can be wired later without another breaking change.

testing

  • go test -race ./pkg/ci/... ./pkg/config/... ./pkg/schema/... ./errors/... — 2308 tests pass across 19 packages.
  • 13 new httptest-backed tests for pkg/ci/providers/github/comments.go cover upsert create/update, create-always, update-returns-not-found, input validation, 403/404 hint propagation, pagination across list pages, and default-behavior fallback.
  • 8 new handler tests cover enabled + PR present (posts), disabled (skips), unset/nil (skips — upgrade safety), no PR event (skips), no repo context (skips), API failure (warn-only, handler still returns nil), and behavior flag respected.
  • pre-commit run is clean: go-fumpt, go build, go.mod tidy, golangci-lint config verify, golangci-lint, gomodcheck, CLAUDE.md size, trailing whitespace, end-of-files, yaml, large files — all pass.

references

  • closes #2367
  • PRD: docs/prd/native-ci/providers/github/pr-comments.md (status flipped Deferred → Shipped in this PR)
  • Implementation tracker: docs/prd/native-ci/framework/implementation-status.md (PR Comments row now Done; v14.0 changelog entry added)
  • Docs: cli/configuration/ci/comments (no content changes — behavior now matches the doc)
  • Precedent for marker-based upsert: tfcmt

Summary by CodeRabbit

  • New Features

    • Opt-in PR comment posting for CI plan runs on GitHub with marker-based updates; defaults to disabled.
  • Providers

    • GitHub provider: create/update/upsert comment behaviors implemented; generic provider reports comments unsupported.
  • Configuration

    • CI comments setting changed to tri-state (unset vs true/false), with env var support to explicitly set.
  • Tests

    • Extensive tests covering comment behaviors and handler wiring.
  • Documentation

    • Docs and implementation status updated to mark PR comments as shipped.

Review Change Stack

fix(auth/eks): suppress success line for no-op kubeconfig writes @mtb-xt (#2402) ## what - `WriteClusterConfig` now returns `(changed bool, err error)` and detects no-op writes by comparing the serialized kubeconfig against the on-disk file. - `EKSIntegration.Execute` only emits `ui.Success` when the kubeconfig actually changed; otherwise it logs at debug. - The explicit `atmos aws eks update-kubeconfig --integration` command keeps its unconditional success message — the user asked for it explicitly there. - Added three tests: merge no-op, replace no-op, and merge with a changed field (to confirm `changed` flips back to true for real updates).

why

Auto-provisioned EKS integrations re-run on every identity resolution. In a single atmos workflow ... (or template lookup, or !terraform.output evaluation), that can mean hundreds of identity authentications back-to-back, each printing:

✓ EKS kubeconfig: platform-apse2-dev → /home/.../.config/atmos/kube/config

The repeated lines bury real output and errors (one user reported the output completely hiding a credential-chain failure under thousands of identical success lines). The on-disk file was almost always already correct after the first write, so the success line was misleading on top of being noisy.

Now you see the success line once, the first time the kubeconfig is actually written or modified, and quiet debug logs the rest of the time. This also avoids unnecessary mtime / chmod churn on the kubeconfig file.

references

  • Originating discussion: user observed 1000+ ✓ EKS kubeconfig: ... lines from a single atmos workflow plan/dev -f bedrock invocation.
  • Code touchpoints:
    • pkg/auth/cloud/kube/config.goWriteClusterConfig, new writeIfChanged/mergeIfChanged/configsEqual helpers.
    • pkg/auth/integrations/aws/eks.goEKSIntegration.Execute gate.
    • cmd/aws/eks/update_kubeconfig_sdk.go — caller update (still always prints success for the explicit command).

test plan

  • go test ./pkg/auth/cloud/kube/... ./pkg/auth/integrations/aws/... passes.
  • New tests cover merge no-op, replace no-op, and field-change → changed=true.
  • No-op call confirmed to leave the file mtime untouched.
  • Manual: run atmos workflow ... against a stack with auto-provisioned aws/eks integrations and confirm the success line appears once instead of per-component.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Kubeconfig writes now detect structurally identical content and skip unnecessary disk writes.
    • File permission drift is reconciled without rewriting unchanged content.
    • CLI success output is shown only when the kubeconfig was actually modified; no-op runs report the config is already up to date (debug).
    • Preserves third-party kubeconfig entries when managing clusters.
  • Tests

    • Added coverage for no-op merge/replace, permission-reconcile without content change, visible-field change detection, multi-cluster rewrite behavior, and preservation of third-party kubeconfig entries.

Review Change Stack

Don't miss a new atmos release

NewReleases is sending notifications on new releases.