Live data from Hacker News

Modern CI is too complex and misdirected

gregoryszorc.com

71–80 of 184 posts

Re: Modern CI is too complex and misdirected

#71
post #11

It's weird that people keep building DSLs or YAML based languages for build systems. It's not a new thing, either - I remember using whoops-we-made-it-turing complete ANT XML many years ago. Build systems inevitably evolve into something turing complete. It makes much more sense to implement build functionality as a library or set of libraries and piggyback off a well designed scripting language.

Joe Beda (k8s/Heptio) made this same point in one of his TGI Kubernetes videos: https://youtu.be/M_rxPPLG8pU?t=2936 I agree 100%. Every time I see "nindent" in yaml code, a part of my soul turns to dust.

I wish more people who for some reason are otherwise forces to use a textual templating system to output would remember that every json object is a valid yaml value, so instead of fiddling with indent you just ".toJson" or "| json" or whatever is your syntax and it pull get something less brittle.

(Or use a structural templating system like jsonnet or ytt)

Re: Modern CI is too complex and misdirected

#72
post #9

This makes no sense to me. Modern build systems have reproducible results based on strict inputs. Modern CI/CD handles tasks that are not strictly reproducible. The continuous aspect also implies its integrated to source control. I guess I don't understand the post if its not just semantic word games based on sufficient use of the word sufficient. Maybe the point is to talk about how great Taskcluster is but the only…

Many CI systems try to strictly enforce hermetic build semantics and disallow non-idempotent steps from being possible. For example, by associating build steps with an exact source code commit and categorically disallow a repeat of a successful step for that commit.

Re: Modern CI is too complex and misdirected

#73
post #65

The biggest problem with any CI system is that you need an execution environment . Changing this environment should be the same as changing the code. Docker (or rather podman) has given us the tools to do this. Now if CI systems would allow me to build that container image myself, I could pretty much guarantee that local build/tests and CI build/tests can run inside the same environment. I hacked something like this…

Docker / containers are necessary but not sufficient. For example, in a machine learning CI / CD system, there could be a fundamental difference between executing the same step, with the same code, on CPU hardware vs GPU hardware.

Re: Modern CI is too complex and misdirected

#74
Re: correct language/abstraction

At the highest level you want a purely functional DSL with no side effects. Preferably one that catches dependency cycles so it halts provably.

On the lowest level, however, all your primitives are unix commands that are all about side effects. Yet, you want them to be reproducible, or at least idempotent so you can wrap them in the high level DSL.

So you really need to separate those two worlds, and create some sort of "runtime" for the low level 'actions' to curb the side effects.

* Even in the case of bazel, you have separate .bzl and BUILD files. * In the case of nix, you have nix files and you have the final derivation (a giant S expression) * In the case of CI systems and github actions, you have the "actions" and the "gui".

Re: CI vs build system, I guess the difference is that build systems focus on artifacts, while CI systems also focus on side effects. That said, there are bazel packages to push docker images, so it's certainly a very blurry line.

Re: Modern CI is too complex and misdirected

#75

I think that modern CI is actually too simple. They all boil down to "get me a Linux box and run a shell script". You can do anything with that, and there are a million different ways to do everything you could possibly want. But, it's easy to implement, and every feature request can be answered with "oh, well just apt-get install foobarbaz3 and run quuxblob to do that." A "too complex" system, would deeply integrate…

Hard agree. I've been using gitlab CI/CD for a long time now. I almost want to say it's been around longer or as long as docker? It has a weird duality of running as docker images, but also really doesn't understand how to use container images IN the process. Why volumes don't just 1:1 map to artifacts and caching, always be caching image layers to make things super fast etc.

"I almost want to say it's been around longer or as long as docker?"

I had to look it up but GitLab CI has been around longer than Docker! Docker was released as open-source in March 2013. GitLab CI was first released in 2012.

Re: Modern CI is too complex and misdirected

#76
post #43

Earlier quoted context omitted.

Joe Beda (k8s/Heptio) made this same point in one of his TGI Kubernetes videos: https://youtu.be/M_rxPPLG8pU?t=2936 I agree 100%. Every time I see "nindent" in yaml code, a part of my soul turns to dust.

> Every time I see "nindent" in yaml code, a part of my soul turns to dust. Yup. For this reason it's a real shame to me that Helm won and became the lingua franca of composable/configurable k8s manifests. The one benefit of writing in static YAML instead of dynamic , is that regardless of primary programming language, everyone can contribute; more complex systems like KSonnet start exploding in first-use complexity.

Can just default to something like

  """
  apiVersion: v1
  appName: "blah"
  """.FromYaml().Execute()
or something.

Re: Modern CI is too complex and misdirected

#77
post #45

Earlier quoted context omitted.

Scripting languages aren't used directly because people want a declarative format with runtime expansion and pattern matching. We still don't have a great language for that. We just end up embedding snippits in some data format.

Who are the "people" who really want that, are responsible for a CI build, and are not able to use a full programming language ? I used jenkins pipeline for a while, with groovy scripts. I wish it had been a type checked language to avoid failing a build after 5minutes because of a typo, but, it was working. Then, somehow, the powers that be decided we had to rewrite everything in a declarative pipeline. I still fail…

You can activate typechecking in groovy with @CompileStatic. It's an all or nothing thing though (for the entire file).

Re: Modern CI is too complex and misdirected

#78
post #62
post #52

Earlier quoted context omitted.

I'd say it's not about the capabilities of the language, but the scope of the environment. You need a language to orchestrate your builds and tests (which usually means command execution, variable interpolation, conditional statements and looping constructs), and you need a language to interact with your build system (fetching code, storing and fetching build artifacts, metadata administration). Lua would be a good c…

People hate on it, but do you know what language would be perfect these days? Easy shelling - check. Easily embeddable - check Easily sandboxable - check. Reasonably rich standard library - check. High level abstractions - check. If you're still guessing what language it is, it's Tcl. Good old Tcl. It's just that is syntax is moderately weird and the documentation available for it is so ancient and creaky that you ca…

How is the Windows support? One of my big needs for any general-purpose build system is that I can get a single build that works on both Windows and POSIX. Without using WSL.

That said, you're right, at least at first blush, tcl is an attractive, if easy to forget, option.

Re: Modern CI is too complex and misdirected

#79
post #74

Re: correct language/abstraction At the highest level you want a purely functional DSL with no side effects. Preferably one that catches dependency cycles so it halts provably. On the lowest level, however, all your primitives are unix commands that are all about side effects. Yet, you want them to be reproducible, or at least idempotent so you can wrap them in the high level DSL. So you really need to separate those…

> Re: CI vs build system, I guess the difference is that build systems focus on artifacts, while CI systems also focus on side effects. That said, there are bazel packages to push docker images, so it's certainly a very blurry line.

I think the CI and build system have basically the same goals, but they're approaching the problem from different directions, or perhaps it's more accurate to say that "CI" is more imperative while build systems are more declarative. I really want a world with a better Nix or Bazel. I think purely functional builds are always going to be more difficult than throwing everything in a big side-effect-y container, but I don't think they have to be Bazel/Nix-hard.

Re: Modern CI is too complex and misdirected

#80
post #36

"Bazel has remote execution and remote caching as built-in features... If I define a build... and then define a server-side Git push hook so the remote server triggers Bazel to build, run tests, and post the results somewhere, is that a CI system? I think it is! A crude one. But I think that qualifies as a CI system." --- Absolutely. The advisability of rolling your own CI aside, treating CI as "just another user" ha…

How does Bazel deal with different platforms? For example, run tests on Windows, BSD, Android, Raspberry Pi, RISCv5, or even custom hardware?

Bazel differentiates between the "host" environment (your dev box) the "execution" environment (where the compiler runs) and the "target" environment (e.g. RISCv5)

Edit: there's a confusing number of ways of specifying these things in your build, e.g. old crosstool files, platforms/constraints, toolchains. A stylized 20k foot view is:

Each build target specifies two different kinds of inputs: sources (code, libraries) and "tools" (compilers). A reproducible build requires fully-specifying not just the sources but all the tools you use to build them.

Obviously cross-compiling for RISCv5 requires different compiler flags than x86_64. So instead of depending on "gcc" you'd depend on an abstract "toolchain" target which defines ways to invoke different version(s) of gcc based on your host, execution, and target platforms.

In practice, you wouldn't write toolchains yourself, you'd depend on existing implementations provided by library code, e.g. many many third party language rules here: https://github.com/jin/awesome-bazel#rules

And you _probably_ wouldn't depend on a specific toolchain in every single rule, you'd define a global one for your project.

"platforms" and "constraints" together let you define more fine-grained ways different environments differ (os, cpu, etc) to avoid enumerating the combinatoric explosion of build flavors across different dimensions.

HTH, caveat, I have not done cross-compilation in anger. Someone hopefully will correct me if my understanding is flawed.

Post reply on HN