sasajuric

sasajuric

Author of Elixir In Action

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 :slight_smile:

Most Liked

sasajuric

sasajuric

Author of Elixir In Action

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:

  1. Implement most of the flow in Elixir
  2. Easily run all the CI steps locally
  3. Write test the CI flow (example)
  4. 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 :slight_smile:

sasajuric

sasajuric

Author of Elixir In Action

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

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!

Where Next?

Popular in Announcing Top

KronicDeth
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine) This is a plugin that adds support for Elixir to JetBrains IntelliJ...
289 36128 110
New
alisinabh
Hey everyone i’ve developed a library for Jalaali calendar for elixir which supports converting Gregorian dates to Jalaali and vice vers...
New
wmnnd
Hi there, for my project DBLSQD, I needed a file storage solution that is a bit more flexible than Arc. Because I thought others might f...
New
Azolo
Hey everyone, I just released WebSockex which is a Elixir WebSocket client. WebSockex strives to work as a OTP special process, be RFC6...
New
josevalim
Yes, yet another parser combinator library! Most of the parser combinators in the ecosystem are either compile-time, often using AST tra...
159 19228 141
New
sorentwo
Hello! tl;dr Announcing Oban, an Ecto based job processing library with a focus on reliability and historical observability. After spen...
985 42920 311
New
ahamez
Hi everyone, I’ve been working on this protobuf library for 3 years. We use it in the company I work for, EasyMile, to communicate with ...
New
tmbb
PhoenixWS - Websockets over Phoenix Channels Source code on Github here: GitHub - tmbb/phoenix_ws: Websockets implemented over Phoenix Ch...
New
type1fool
WebAuthnLiveComponent WebAuthnComponents See this post about renaming the package. Passwordless authentication for Phoenix LiveView app...
New
mikehostetler
I’m excited to announce Jido, a framework providing foundational primitives for building autonomous agent systems in Elixir. While develo...
New

Other popular topics Top

rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list. ...
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod – where is this set? Thanks.
New
ovidiubadita
Hey all, I discovered Elixir and I love it. I always wanted to learn a functional programming and I intended to go for Haskell, but afte...
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
dogweather
I wrote this comment on r/haskell, and it’s not popular there. :wink: But I think I’m on to something… Haskell reminds me of Java, and e...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? Ecto.Repo — Ecto v3.14.0 has exampl...
New
Nvim
Anybody knows a comprehensive comparison of Django and Phoenix, thanks for the help. Where are they similar? Where do they differ the m...
New
msaraiva
Surface is an experimental library built on top of Phoenix LiveView and its new LiveComponent API that aims to provide a more declarative...
564 43622 214
New
siddhant3030
Hi, I have to write a raw query for one of my project. But till now I have used ecto queries and don’t have much experience writing raw ...
New
TunkShif
This post is an instruction guide to help you setup your Neovim for Elixir development from scratch. It includes general information on h...
274 41539 114
New

We're in Beta

About us Mission Statement