sasajuric
CI/CD as a library
I’d like to announce a new library I’ve just released, called ci, which aims to provide CI/CD features as a library. It’s currently pretty basic, but I have plans on expanding it further with support for docker, integration with GitHub, etc. The general roadmap is included in the readme. Any feedback is welcome ![]()
Most Liked
sasajuric
The library intends to complement, or in same cases completely replace, the existing CI systems (GH actions, Travis, Circle, Jenkins, …). In its current version, it only does the complementing part, which admittedly isn’t much, but I think it’s still something worth having.
You can see the concrete example in the way the library itself does CI:
The immediate benefits compared to eg GH actions:
- Implement most of the flow in Elixir
- Easily run all the CI steps locally
- Write test the CI flow (example)
- Due to support for parallel actions, you can get all the errors in a single CI roundtrip. In this example, if the code is not properly formatted, and some test is failing, and there are dialyzer warnings, all these errors will be reported in a single CI run.
The roadmap vaguely hints at future goals: the support for managing docker containers (so we can run dockerized builds), and integration with SCMs (e.g. GitHub, GitLab, …), which would allow us to easily create our own CI/CD platform in Elixir.
Finally, an internal, but a very important goal is to find properly focused abstractions. For example, in the current version we have OsCmd, Job, and Job.Pipeline, all of which can be used beyond just the CI domain. I’d like to keep this approach with the upcoming features too. Ideally, this library would be a set of small helpers, and it just so happens that you can combine them to implement your own CI ![]()
sasajuric
But IMO CI flow is not configuration. It’s an imperative program. Of course, you can always treat a program as a collection of facts (it’s all 0s and 1s after all), but IMO most of the time that’s not very intuitive, and it’s also not flexible. As I’ve said previously in this tread, using a declarative approach to represent an imperative flow may sometimes be useful, but IMO it’s not a sensible default approach.
Yeah, the problem here is that config scripts are too “free-form”, whereas dhall seems to be more constrained. But ultimately, I think that in the context of CI, one major drawback of dhall is that it’s evaluated at “compile-time”, and so, despite it’s very interesting design and Turing completeness, it’s just more of the same from the “modeling imperative as declarative” school of thought.
What if I need to feed the output of one statement to others? Or what if I need to conditionally execute some step, depending on various circumstances (e.g. the outcome of the previous statement, or the branch being tested, or on whether the PR has been approved)? From what I can tell, you can’t make such decisions in dhall (because it transforms imperative to declarative). Of course, one can always do some trickery to make that happen, like pushing imperative logic into the command itself, or using :if properties of the CI engine, piping cmd output to file, …, but this all seems quite clumsy to me.
All this being said, I agree with the following observation:
As usual, we don’t get something for nothing. By using a “full-blown” language (i.e. a Turing complete language with a rich std library & ecosystem) at runtime, we’ve obtained some possibly dangerous power. We can do all sort of things like format the disk, try to steal secrets, issue a DoS attack, etc. Now, in prety much every real-life case I’ve experienced, such problems were more theoretical than practical, since the team was always very small and every team member wore all the hats (frontend, backend, devops, …). Obviously this won’t scale with respect to the CI user base, so doing this approach in e.g. CI as a service, or in larger companies is not something I’d recommend.
But there are many teams which are fairly small and I believe that in such cases, a full-blown imperative using the same language that is used for the implementation of the main product makes more sense. It’s an option which is simpler and more powerful, at the expense of being less secure.
In the cases where Elixir is not a viable option, you can consider other alternatives. One option is to go full declarative, maybe using dhall to generate the final specification. Note that you can still do this with the ci library. You basically need to transform the program into a declarative collection of facts, and then feed that to the engine that runs OS commands inside a Job. All of this can be done as a part of the mix my_app.ci task, invoked directly on the CI machine. The generated config file never needs to be stored on disk, or committed to the repo.
Another option is to use a sandboxed embeddabled language, e.g. Lua. This would allow you to use a TC language at runtime, while still being able to control what can be invoked. Again, this can be combined with the ci library, by importing a set of custom Lua functions which are under the hood using abstractions from the ci to run particular actions.
The ci library is marketed as a CI toolkit, which means that it’s not particularly “opinionated”, or the way I prefer to think about it, it’s not rigid. The library ships with various independent abstractions which you can use however you please. Take the parts that fit you, ignore the things that don’t, wrap any abstraction as you please. Again, you can most certainly build a declarative engine on top of this runtime TC imperative core, but it usually doesn’t work the other way around. If the core is declarative, adding runtime Turing completeness is going to be either impossible, or at the very list difficult and clumsy.
tcoopman
A bit of topic, but it seems that phoenix builds switched to https://earthly.dev/ which also has a way of running CI locally (https://twitter.com/josevalim/status/1346404430275612683). So I agree, running your CI locally is a feature that is so useful!
Popular in Announcing
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #javascript
- #code-sync
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance








