Skip to main content

Atmos Git: A Foundational Capability for GitOps Pipelines

· 7 min read
Erik Osterman
Founder @ Cloud Posse

Atmos now treats Git as a foundational platform capability — on par with Toolchain, Auth, and Hooks — built to enable GitOps workflows where you need to commit artifacts to a source of truth: deployment repos consumed by Argo CD or Flux, provider lock files, rendered manifests. Define managed repositories once in atmos.yaml, then clone, inspect, diff, commit, and push them through a new atmos git command group, or publish generated artifacts automatically on lifecycle events with the new git hook kind.

What Changed

A new top-level git section in atmos.yaml declares managed repositories by logical name:

git:
repositories:
flux-deploy:
uri: https://github.com/acme/flux-deploy.git
branch: main
auth:
identity: platform-admin
commit:
signing: auto
author:
name: atmos[bot]
email: atmos-bot@acme.com

On top of that foundation:

  • atmos git commandsinit, clone, pull, status, diff, commit, push, and list, with --all bulk operations across every configured repository. Clone accepts configured names, plain URLs, or go-getter style URIs (git::https://...?ref=main&depth=1) — the same syntax you already use in vendoring. init, clone, pull, and push pass arguments after -- verbatim to the underlying git invocation, so uncommon git flags still compose with Atmos safety rules.
  • Bootstrap from scratch or from a templateatmos git init creates a configured repository whose remote has no content yet: workdir, initial branch, and remote wired up, ready to commit and push. --from=<uri> seeds it from a template (one fresh initial commit), and --from=<uri> --keep-history migrates an existing repository while keeping the source pullable as an upstream remote.
  • A git hook kind — publish generated files on lifecycle events like after.terraform.apply, committing to the current repository or a managed deployment repository, with templated commit messages and provenance trailers (Atmos-Stack, Atmos-Component).
  • Local Git hook shimsatmos git hooks install wires .git/hooks/* (pre-commit, commit-msg, ...) to Atmos workflows and custom commands, replacing Husky-style glue.
  • Native CI checkout — in GitHub Actions with ci.enabled: true, a bare atmos git clone infers the current repository from CI metadata and clones it into the workspace, replacing actions/checkout. GitHub STS credentials flow to every Git subprocess automatically via GIT_CONFIG_*.

Why This Matters

GitOps workflows have always needed glue: scripts to render manifests into deployment repos, commit them, handle push races, and wire up credentials. Atmos already owned rendering, lifecycle events, and credentials — now it owns the Git operations between them:

  • Safety rules are centralized. Fast-forward-only pulls, no force pushes ever, path-scoped commits that refuse to touch unrelated dirty files, and automatic retry-with-rebase when a concurrent publisher wins the push race.
  • Commits work in CI out of the box. Author identity is injected per invocation (no user.name configuration on runners), and commit signing is configurable per repository.
  • Clones are cache-friendly. Managed repositories live under the Atmos XDG cache root, so the native CI cache restores them across runs — and clone is defined as reconcile (fetch and fast-forward), so a stale restored clone is just a faster clone.

What This Is — and Isn't

Atmos owns the publishing side of GitOps: render → diff → commit → push, with centralized safety rules. Reconciliation stays where it belongs — Argo CD or Flux pulls from the repository, or your CI applies on merge. There are no agents and no drift-correction loop in Atmos itself; it's the producer feeding your reconciler. And this isn't a replacement for the GitHub Actions integration you may already run plan/apply pipelines with — it's the Git plumbing those pipelines use.

How to Use It

A real problem every Terraform team hits: CI runs terraform init, the provider lock file (.terraform.lock.hcl) changes — a provider was upgraded, or hashes for a new platform were added — and that change silently drifts because nothing commits it back. With the git hook kind, Atmos commits it after apply:

components:
terraform:
vpc:
hooks:
commit-lockfile:
events:
- after.terraform.apply
kind: git
# No repository: configured — the hook operates on the
# current repository, the one your pipeline checked out.
commit:
message: "chore: update provider lock file for {{ .component }} in {{ .stack }}"
paths:
- components/terraform/vpc/.terraform.lock.hcl
push: true

When repository: is omitted, the hook targets the current repository — no special name needed. The commit is path-scoped (it refuses to sweep up unrelated dirty files), the author is injected so it works on runners with no Git identity, and if another job pushed first, the push retries with a rebase. If nothing changed, it's a clean no-op.

To publish into a different repository instead — rendered manifests into an Argo CD / Flux deployment repo — add repository: flux-deploy pointing at a configured entry under git.repositories.

Imperatively, the same operations are first-class commands:

atmos git clone --all          # reconcile every configured repository
atmos git diff flux-deploy # preview what would change — before committing
atmos git commit flux-deploy --message="Render argocd for prod" --path=clusters/prod
atmos git push flux-deploy

And in GitHub Actions, the checkout step becomes Atmos-native:

steps:
- uses: cloudposse/github-action-setup-atmos@v2
- run: atmos git clone # replaces actions/checkout
- run: atmos git clone --all # warm all deployment repos

See the atmos git command reference and the Git configuration docs for the full surface, including clone depth controls, signing modes, and push retry tuning.

Bootstrapping a Repository — atmos git init

Cloning assumes the repository already exists. Often it doesn't yet: you're standing up a new GitOps deployment repo for a new cluster, a new tenant, a new environment. atmos git init is clone's counterpart — it creates a configured repository whose remote has no content, on the configured branch, with the remote wired up, ready for atmos git commit and atmos git push:

atmos git init flux-prod        # empty repo, origin -> configured uri, ready to push

The interesting part is --from, which turns init into a repository factory. Point it at a template and Atmos instantiates a fresh repository from it:

# Stamp out a new deployment repo from your org's GitOps template.
# Fresh history: one clean initial commit, no link back to the template.
atmos git init flux-prod --from=https://github.com/acme/gitops-template.git

This is the pattern platform teams reach for constantly: a golden GitOps template (Flux/Argo layout, Kustomize bases, policy guardrails) that every new cluster or tenant repo starts from. Instead of "clone the template, delete .git, re-init, fix the remote, commit" by hand, it's one command — wired to the repository you already declared in atmos.yaml, with the right identity, branch, and signing applied.

And when you're migrating an existing repository to a new home, --keep-history preserves the full commit history and keeps the source reachable as an upstream remote, so you can keep pulling updates from it:

# Migrate, preserving history; pull future template updates with `git pull upstream`.
atmos git init flux-prod --from=https://github.com/acme/old-flux.git --keep-history

Same declarative model as the rest of atmos git: the repository, its identity, and its conventions are defined once in atmos.yaml, and init brings it into existence.

Get Involved

This is the foundation of a larger GitOps story — pull-request-based publishing for protected branches and Kubernetes deployment-repository provisioning are next. Read the PRD in docs/prd/git-ops.md, try it out, and share feedback in GitHub Discussions.