toka
10/02/2026, 8:34 PMErik Osterman (Cloud Posse)
10/02/2026, 9:17 PMErik Osterman (Cloud Posse)
10/02/2026, 9:17 PMErik Osterman (Cloud Posse)
10/02/2026, 9:17 PMErik Osterman (Cloud Posse)
10/02/2026, 9:18 PMErik Osterman (Cloud Posse)
10/02/2026, 9:18 PMErik Osterman (Cloud Posse)
10/02/2026, 9:18 PMErik Osterman (Cloud Posse)
10/02/2026, 9:19 PMErik Osterman (Cloud Posse)
10/02/2026, 9:19 PMErik Osterman (Cloud Posse)
10/02/2026, 9:19 PMErik Osterman (Cloud Posse)
10/02/2026, 9:20 PMErik Osterman (Cloud Posse)
10/02/2026, 9:20 PMErik Osterman (Cloud Posse)
10/02/2026, 9:22 PMErik Osterman (Cloud Posse)
10/02/2026, 9:23 PMErik Osterman (Cloud Posse)
10/02/2026, 9:25 PMtoka
10/05/2026, 7:52 AMtoka
10/05/2026, 8:14 AMErik Osterman (Cloud Posse)
10/05/2026, 11:48 AMtoka
10/05/2026, 11:51 AMErik Osterman (Cloud Posse)
10/05/2026, 11:59 AMtoka
10/05/2026, 12:02 PMtoka
10/05/2026, 12:05 PMErik Osterman (Cloud Posse)
10/05/2026, 12:06 PMtoka
10/05/2026, 12:09 PMErik Osterman (Cloud Posse)
10/05/2026, 12:10 PMtoka
10/05/2026, 12:11 PMErik Osterman (Cloud Posse)
10/05/2026, 12:11 PMErik Osterman (Cloud Posse)
10/05/2026, 12:11 PMErik Osterman (Cloud Posse)
10/05/2026, 12:33 PMErik Osterman (Cloud Posse)
10/05/2026, 12:34 PMatmos ai skill installErik Osterman (Cloud Posse)
10/05/2026, 12:34 PMtoka
10/05/2026, 12:35 PMAndriy Knysh (Cloud Posse)
10/05/2026, 2:11 PMAndriy Knysh (Cloud Posse)
10/05/2026, 2:33 PMtoka
10/05/2026, 3:02 PMtoka
10/05/2026, 3:05 PMErik Osterman (Cloud Posse)
10/05/2026, 3:06 PMdocs/prds.Andriy Knysh (Cloud Posse)
10/05/2026, 3:07 PMErik Osterman (Cloud Posse)
10/05/2026, 3:08 PMErik Osterman (Cloud Posse)
10/05/2026, 3:08 PMAndriy Knysh (Cloud Posse)
10/05/2026, 3:09 PMAndriy Knysh (Cloud Posse)
10/05/2026, 3:09 PMtoka
10/05/2026, 3:12 PMAndriy Knysh (Cloud Posse)
10/05/2026, 3:13 PMtoka
10/05/2026, 3:24 PMErik Osterman (Cloud Posse)
10/05/2026, 6:09 PMErik Osterman (Cloud Posse)
10/05/2026, 6:10 PMErik Osterman (Cloud Posse)
10/05/2026, 6:10 PMErik Osterman (Cloud Posse)
10/05/2026, 6:11 PMtoka
10/07/2026, 3:00 PMtoka
10/07/2026, 3:07 PMErik Osterman (Cloud Posse)
10/07/2026, 3:26 PMLooks cool; I'm not sure how/if making commits fits into my case here.With ArgoCD, do you use the deployment-repo pattern? E.g. a repo that represents the desired state of all manifests?
Erik Osterman (Cloud Posse)
10/07/2026, 3:27 PMSo far the way I've found is through extending Atmos with custom commands to build, test etc.Yes, this is a good, but about to get a SERIOUS boost. We're implementing starlark (a pythonesque language by Google), that exposes all internals of atmos directly with in custom commands, steps, hooks, etc.
Erik Osterman (Cloud Posse)
10/07/2026, 3:28 PMHowever, as far as I can tell from the docs, the native CI parts (job summaries, the markdown templates behind them, PR comments) are only wired to component commands: terraform plan/apply/test, helm/helmfile/kubernetes get summaries only? and PR comments are terraform plan only?It's an interface, which each component can hook into; although we haven't extended it to custom commands, but custom commands that call built-in commands will get the benefit.
Erik Osterman (Cloud Posse)
10/07/2026, 3:29 PMErik Osterman (Cloud Posse)
10/07/2026, 3:29 PMtoka
10/07/2026, 4:41 PMWith ArgoCD, do you use the deployment-repo pattern? E.g. a repo that represents the desired state of all manifests?Partly. I have a deployment repo that Argo CD watches, but it holds config only. Argo CD renders the Helm chart from an OCI registry at sync. Do you mean rendered manifests in the repo or does config-only count as the pattern for you?
If there's a specific gap you encounter, please bring it up and we'll see about closing itI'm still in the process of finding way that suites us best and I'm looking for ways to move away from GH Actions and inline/bash scripts to actually build, compile, test APIs/backends and so on - that's why I'm looking into having a single interface to execute pipeline code and post PR comments and Summaries with templates, or for example how to wire OPA policy checks for those (and again, post output into a comment or summary) instead of wiring PR statuses and checks the usual way.
Erik Osterman (Cloud Posse)
10/07/2026, 5:03 PMcommands:
- name: app build
description: Build and push the container image for a component
arguments:
- name: component
required: true
flags:
- name: stack
shorthand: s
required: true
steps:
- name: build
type: script
interpreter: starlark
script: |
component = ctx.arguments["component"]
stack = ctx.flags["stack"]
def build():
r = atmos.container("build", component, flags = {"stack": stack}, check = False)
if r.exit_code != 0:
return r
return atmos.container("push", component, flags = {"stack": stack}, check = False)
# The image digest summary is already written by the container component in CI.
result = ci.group("build", build)
ok = result.exit_code == 0
ci.output("built", "true" if ok else "false")
ci.comment(
("ā
Built and pushed" if ok else "ā Build failed") + " `" + component + "` @ `" + ci.context.sha[:12] + "`",
key = "app-build:" + component + ":" + stack,
)
if not ok:
fail("build failed")
- name: app test
description: Run tests and the OPA policy check for a component
arguments:
- name: component
required: true
flags:
- name: stack
shorthand: s
required: true
steps:
- name: test
type: script
interpreter: starlark
script: |
component = ctx.arguments["component"]
stack = ctx.flags["stack"]
dependencies.tools("gotestyourself/gotestsum", "1.12.0")
def run_tests():
return exec.run(["gotestsum", "--junitfile", "junit.xml", "--", "./..."],
check = False, output = "capture")
tests = ci.group("test", run_tests)
if fs.exists("junit.xml"): # A compile error produces no report.
steps.junit(files = ["junit.xml"], title = "API tests")
policy = atmos.validate("component", component, flags = {"stack": stack},
check = False, output = "capture")
if policy.exit_code != 0:
ci.annotate("error", "OPA policy failed for " + component + " in " + stack, title = "policy")
ok = tests.exit_code == 0 and policy.exit_code == 0
ci.output("tested", "true" if ok else "false")
body = "\n".join([
"## Test: `" + component + "` in `" + stack + "`",
"",
"| Check | Result |",
"|---|---|",
"| Tests | " + ("ā
" if tests.exit_code == 0 else "ā") + " |",
"| OPA policy | " + ("ā
" if policy.exit_code == 0 else "ā") + " |",
"",
"<details><summary>Policy output</summary>",
"",
"```",
policy.stdout + policy.stderr,
"```",
"</details>",
])
ci.summary(body)
ci.comment(body, key = "app-test:" + component + ":" + stack)
if not ok:
fail("tests or policy failed")
- name: app deploy
description: Preview the deployment on PRs and locally, apply it on pushes
arguments:
- name: component
required: true
flags:
- name: stack
shorthand: s
required: true
- name: apply
type: bool
description: Apply even when this is not a push (for example from a laptop)
steps:
- name: deploy
type: script
interpreter: starlark
script: |
component = ctx.arguments["component"]
stack = ctx.flags["stack"]
key = "app-deploy:" + component + ":" + stack
# Deploy must not trust an earlier job: re-check policy.
policy = atmos.validate("component", component, flags = {"stack": stack},
check = False, output = "capture")
if policy.exit_code != 0:
ci.comment("ā Policy check failed, not deploying `" + component + "`", key = key)
fail("policy check failed")
if ctx.flags["apply"] or ci.context.event == "push":
r = atmos.helm("deploy", component, stack, flags = {"target": "deployment-repo"},
check = False)
ci.comment(("ā
Deployed" if r.exit_code == 0 else "ā Deploy failed") + " `" + component + "`",
key = key)
if r.exit_code != 0:
fail("deploy failed")
else:
# PRs and local runs: show what would change, change nothing.
diff = atmos.helm("diff", component, stack, check = False, output = "capture")
ci.comment("## Deploy preview: `" + component + "` in `" + stack + "`\n\n```diff\n" + diff.stdout + "\n```",
key = key)
- name: app all
description: Run build, test, and deploy in order; stops at the first failure
arguments:
- name: component
required: true
flags:
- name: stack
shorthand: s
required: true
- name: apply
type: bool
steps:
- name: all
type: script
interpreter: starlark
script: |
component = ctx.arguments["component"]
for phase in ["build", "test", "deploy"]:
argv = ["app", phase, component, "--stack=" + ctx.flags["stack"]]
if phase == "deploy" and ctx.flags["apply"]:
argv.append("--apply")
ci.group(phase, lambda: atmos.run(argv)) # Raises on a nonzero exit, so the loop stops there.Erik Osterman (Cloud Posse)
10/07/2026, 5:03 PMErik Osterman (Cloud Posse)
10/07/2026, 5:03 PMErik Osterman (Cloud Posse)
10/07/2026, 5:04 PMErik Osterman (Cloud Posse)
10/07/2026, 5:04 PMErik Osterman (Cloud Posse)
10/07/2026, 5:04 PMtoka
10/07/2026, 5:05 PMErik Osterman (Cloud Posse)
10/07/2026, 5:06 PM#!/usr/bin/env atmos and write CLI appsErik Osterman (Cloud Posse)
10/07/2026, 5:06 PMErik Osterman (Cloud Posse)
10/07/2026, 5:07 PMtoka
10/07/2026, 5:12 PMErik Osterman (Cloud Posse)
10/07/2026, 5:23 PMErik Osterman (Cloud Posse)
10/07/2026, 5:26 PMtoka
10/07/2026, 5:26 PMErik Osterman (Cloud Posse)
10/07/2026, 5:28 PMErik Osterman (Cloud Posse)
10/07/2026, 5:28 PMtoka
10/07/2026, 5:50 PMcan you confirm you're current use-case is with GHA + local execGHA + local exec, monorepo with 5 to 10 (micro)services/APIs backends for starters, with Nx under the hood for project discovery, graph for dependencies, identifying test targets, integration tests, e.g.
nx affected -t lint test build
with IaC via Atmos alongside š .
So basically I would call Nx (or alike) in Atmos CI/custom_commands/steps, because setting and maintaining those is not my responsibility anyway, and then finally use Atmos to build artifacts, sign and push to AR.Erik Osterman (Cloud Posse)
10/07/2026, 11:01 PMErik Osterman (Cloud Posse)
10/07/2026, 11:01 PMnx is also part of the inspiration for what we're buildingtoka
10/08/2026, 8:58 AMtoka
10/08/2026, 8:58 AMErik Osterman (Cloud Posse)
10/08/2026, 12:18 PMtoka
10/08/2026, 12:20 PM