[ANN] OCurrent 0.1 (CI/CD pipeline eDSL)

OCurrent 0.1 has just been released to opam-repository.

OCurrent is an OCaml eDSL intended for writing build/test/deploy pipelines. It is being used as the engine for ocaml-ci and the docker-base-images builder (used to build the OCaml Docker images, such as ocurrent/opam:alpine-3.10-ocaml-4.08). Other good uses might be building and redeploying a Docker service or a unikernel whenever its source repository changes. It can be run locally as a single Unix process.

An OCurrent pipeline is written as an OCaml program, but the OCurrent engine ensures that it is kept up-to-date by re-running stages when their inputs change. A web UI is available so you can view your pipeline and see its current state.

OCurrent can statically analyse the pipelines before they have run, allowing it to run steps in parallel automatically and to display the whole pipeline. It does this using a light-weight alternative to arrows, which doesn’t require programming in an awkward point-free style. See
CI/CD Pipelines: Monad, Arrow or Dart? for more about that.

The basic functionality can be extended using “plugins” (just normal OCaml libraries). Plugins are available for interacting with Docker, Git, GitHub and Slack. These are in separate packages (e.g. current_github) to avoid having the base package pull in too many dependencies).

There is also an optional Cap’n Proto RPC interface, in the current_rpc opam package. This is used, for example, by citty to provide a TTY interface to ocaml-ci.

The OCurrent wiki contains examples, and documentation on the various plugins.

Here’s an example pipeline (from the base image builder):


For those curious about the relation to the existing CI used in opam-repository, then it is no coincidence that @talex5 is the author of both :slight_smile:

This DSL is the next iteration of the datakit-ci, but specialised to be faster and simpler for extending with OCaml and more complex workflows that our OCaml Platform tools need these days (like ocamlformat linting, or dune expect promotion, or odoc cross-referenced doc generation). We are planning a smooth migration next year over to the new system, but wanted to release this early to show you some of the pieces going into this new iteration. I am particularly excited about the new tty-based interface that saves an awful lot of clicking around on web UIs for CI results…


Just today saw an amazing project - docker-slim, which might be handy for your ocurrent Docker images generation. They claim significant container size reduce:

Node.js application images:

  • from ubuntu:14.04 - 432MB => 14MB (minified by 30.85X )
  • from debian:jessie - 406MB => 25.1MB (minified by 16.21X )
  • from node:alpine - 66.7MB => 34.7MB (minified by 1.92X )
  • from node:distroless - 72.7MB => 39.7MB (minified by 1.83X )

Python application images:

  • from ubuntu:14.04 - 438MB => 16.8MB (minified by 25.99X )
  • from python:2.7-alpine - 84.3MB => 23.1MB (minified by 3.65X )
  • from python:2.7.15 - 916MB => 27.5MB (minified by 33.29X )
  • from centos:7 - 647MB => 23MB (minified by 28.57X )
  • from centos/python-27-centos7 - 700MB => 24MB (minified by 29.01X )
  • from python2.7:distroless - 60.7MB => 18.3MB (minified by 3.32X )

Ruby application images:

  • from ubuntu:14.04 - 433MB => 13.8MB (minified by 31.31X )
  • from ruby:2.2-alpine - 319MB => 27MB (minified by 11.88X )
  • from ruby:2.5.3 - 978MB => 30MB (minified by 32.74X )

Golang application images:

  • from golang:latest - 700MB => 1.56MB (minified by 448.76X )
  • from ubuntu:14.04 - 531MB => 1.87MB (minified by 284.10X )
  • from golang:alpine - 258MB => 1.56MB (minified by 165.61X )
  • from centos:7 - 615MB => 1.87MB (minified by 329.14X )

Haskell application images:

  • (Scotty service) from haskell:8 - 2.09GB => 16.6MB (minified by 125.32X )

I created an issue in the repository for you to consider this possibility.

docker-slim is pretty much the opposite of what a CI/CD system needs: it does runtime analysis within a container to remove unused dependencies. So it’s something that an end user application would use, not images for CI/CD.

current.mli contains an Unit module (type t = unit), which appears to be designed to pass to functors expecting a comparable/hashable type, with a note saying: “Missing from the OCaml standard library”. Have you considered proposing it for inclusion in the standard library?

I have a more high-level question, which is: I would be interested in a somewhat generic “job” abstraction, that I could use to describe computations, and then run them (benefitting from parallelism for computations that release the runtime lock), facilities to persistent intermediate results (or error logs), and to rerun jobs (recomputing only the parts that have changed). Your DSL is currently specialized to the CI/CD use-case, but how far is it from a generic “job” DSL?
(My question also sounds like Incr(emental), and maki, and a build system. Is there a clean reusable abstraction that I could use, rather than an encoding in some more complex system?)

1 Like

current.mli contains an Unit module ( type t = unit ) […] Have you considered proposing it for inclusion in the standard library?

Looks like Unit has been added to the stdlib already (since 4.08) :slight_smile:

Your DSL is currently specialized to the CI/CD use-case, but how far is it from a generic “job” DSL?

It’s already pretty generic and should do what you want. At the moment, it works best when the jobs are relatively large (e.g. each job takes seconds to hours to run) and re-evaluating what it needs to do therefore doesn’t add much overhead. Ideally it would use something like incremental/react internally to make that more efficient.

Doesn’t dune also ship such a framework internally?

Yes, but I have never looked at the details, how nice this part of the API is (if it’s just a deep layer in the middle of dune, it doesn’t need to be made particularly nice), and as far as I know (I asked a couple times) there are no plans to make it an independent library for other use-cases.