Random question on Friday so I bet this is the rig...
# random
t
Random question on Friday so I bet this is the right place. You need to build CICD pipeline to compile, test, dockerize, sign and push to storage from scratch, no questions asked. It has to be generic to support multiple coding langs and their ecosystems in a single monorepo. Approach is to solve it like a Software Engineer - you want to run CICID, compile, test and change locally, like any SWE would do, and then run it on GH Actions, GitLab or whatever, treating the SVC platform as a shim or mere adapter to your CICD at best. What would you do? Am I insane nothing like it exists?
e
Lol, you just described atmos 😃
But joking aside, this was a bit the driver behind Earthly CI and Dagger HQ
However, the way we use atmos now is to build/compile everything.
Including building docker images and pushing to (multiple) registries
The idea is everything should work locally as well as it does in CI
šŸŽÆ 2
That's why we have atmos toolchain for installing tools
That's why we have atmos custom commands, which support parallelism, matrixes, and docker builds
Atmos has outgrown infrastructure and that's now just one of the things it does.
Here's an example of how we build/test/deploy from atmos. And it works in CI as much as it works locally.
Atmos also supports now custom components, so you can create a component for a NextJS app, for example, and define the custom commands you use to deploy it.
Also, atmos supports running conatiners now, which is an alternative to docker compose for local development
t
Well šŸ’© I have a feeling that will be a hard sell within my teams.
Was looking into Dagger, however the requirement to run Dagger Engine with privileged flag would be even harder to sell
e
It would be helpful for us to understand what makes atmos a hard sell? Is it documentation, learning curve, YAML, or something else?
t
To be honest, personal (not mine) taste and lack of technical depth. I've started poc-ing with atmos ci to see it in action.
e
The reason I ask is I have heard the push back before, but struggle to ever get the constructive criticism we can use to make it better. Our entire goal is provide a supreme, best-in-class DX, while also not limiting what power users can do with it.
t
Yeah I get that. I'm gonna build with it and then in case I got constructive criticisms I will share. To me not being able to develop and test CICD code locally drives me nuts and that will be my main angle to sharpen my knives.
So far I see I might need to add artifact registry support for GCP. If that's the case, happy to contribute.
e
Yes, would definitely accept the GCP support
t
Great, 'cause most likely I gonna need that quite soon.
e
We've just rounded out the Azure support with feature parity. GCP is mostly there, but not with the native CI pieces. The good news is is it's just an interface that gets extended, so it's very easy to add. Main reason for not supporting it is just testing.
šŸ‘Œ 1
t
Great, then I'm gonna start familiarise myself with contribution guides.
e
(actually, I think @Andriy Knysh (Cloud Posse) will be doing some more work on the Azure integration with some work he is doing now)
Join us in #C07RMALBX7V
šŸ‘€ 1
Happy to iterate on any of these pieces with so you get a successful pilot.
Also, recommend the atmos skills.
atmos ai skill install
t
Thanks, let me wire those into my factory.
a
@toka thank you for looking into it. We are constantly improving Atmos and we release every week (AWS, Azure, GCP, Helm, toolchain, UI/UX, workflows, CI, etc.). Your contributions will be reviewed fast. Also, let us know if you need help with any of what you want to implement
šŸ» 1
one more note: • When we need to fix something, we create a fix doc in the folder github.com/cloudposse/atmos/…/fixes • When we want to implement a new feature, we create a PRD in the folder github.com/cloudposse/atmos/…/prd • Or, for a new feature, if you want to be more advanced, you can use the specs (instead of PRDs) github.com/cloudposse/atmos/…/specs (see developer.microsoft.com/blog/spec-driven-development-spec-kit, github.com/github/spec-kit, learn.microsoft.com/en-us/training/modules/spec-driven-development-github-spec-kit-enterprise-developers all these docs serve two main purposes: • For humans, to have (dated) history of what was implemenetd/fixed, when, why, and by whom • For agents, it serves as long-term durable memory (you can point an agent at the docs, and it will understand what was done and if it's related to a new feature you are implementing; sometimes it overlaps with what was done before)
šŸ‘€ 1
t
I'm using OpenSepc daily. Here it's Spec Kit? Asking if I get it right.
Thanks @Andriy Knysh (Cloud Posse) for excellent pointers.
e
We have't formally adopted speckit in atmos, but some features have gone out that way. We have slightly less formal process, it's its in
docs/prds
.
a
@Erik Osterman (Cloud Posse) we need to review both and implement some guidance github.com/github/spec-kit/discussions/1536
e
@Igor Rodionov was also looking into switching to openspec
😃
we were mostly using PRDs, but the industry is moving to wards SDD
t
From my teams experience I can share, we have started with SpecKit and moved to OpenSpec for good. OpenSpec proven itself to be easier to maintain over time, especially when design or implementation details change over time.
a
thanks for the info, very useful
t
In general I'm looking to wire CICD for monorepo into ArgoCD and more.
e
Have you seen this demo? atmos.tools/examples/local-gitops
This shows how atmos can help you easily publish to a git repo kubernetes manifests, rendered helm charts, etc.
This demo actually brings up a local k3s cluster so you can test it locally
Under the hood, atmos supports git operations atmos.tools/examples/gitops
t
Looks cool; I'm not sure how/if making commits fits into my case here. How would you wire cicd for apps, backends etc? So far the way I've found is through extending Atmos with custom commands to build, test etc.
However, 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? Am I reading that right?
e
Looks 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?
So 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.
However, 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.
Most new component types such as helm and kubernetes have the native CI support implemented. Plus the forth coming Cloud Formation support.
If there's a specific gap you encounter, please bring it up and we'll see about closing it
t
With 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 it
I'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.
e
The following should be possible in 1.131.0 or 1.132.0. I think it addresses exactly what you're looking to solve.
Copy code
commands:
  - 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.
Notice, there's no bash-fu
It uses internal atmos capabilities to build containers
It supports CI calls, that degrade gracefully if executed locally
scripts can be inline, or external files, or even remote files
And it supports tests
t
Great minds think alike
e
It also supports an interpretive mode, so you can
#!/usr/bin/env atmos
and write CLI apps
THe key is since it's all in atmos, there's no other modules, libs, etc that need to be installed. This reduces supply chain risk and also increases reliability from failures in transient dependencies.
šŸ† 1
It exposes a comprehensive "deployment language" for most scenarios.
t
Any advice how to approach solving CI now before it becomes available? in a way to avoid painting myself into a corner now. And is there anything I can do to contribute to accelerate development?
e
What is your timeline?
I would model it out now, with custom commands for each phase/operation. E.g. build, test, deploy, validate. Use shell for now, or use python interpreter for faster port to starlark (a pythonesque languge). This should make an AI rewrite quite easy. can you confirm you're current use-case is with GHA + local exec? E.g. GitLab interface for CI is not built out (and supports a fractions of what GitHub Actions supports).
t
To short, a week, two at best. But I don't need to have everything polished, I can cover 1st and 2nd base and improve/extend along the way.
e
Here's how we use python today in our custom commands
t
can you confirm you're current use-case is with GHA + local exec
GHA + 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.
Copy code
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.
e
aha, that's helpful
nx
is also part of the inspiration for what we're building
t
yeah I can see that
the thing is I'm not going to even try convince whole eng department to switch from Nx to something else without clear need
e
Yep, keep nx though for js/typescript projects
t
Rust, C#, Go but yes
šŸ‘ 1