--- title: DIY bare bones CI server with Bash and Nix date: 2020-11-12 3 layout: post lang: en ref: diy-bare-bones-ci-server-with-bash-and-nix eu_categories: ci --- With a server with Nix installed (no need for NixOS), you can leverage its build isolation for running CI jobs by adding a [post-receive][post-receive] Git hook to the server. In most of my project I like to keep a `test` attribute which runs the test with `nix-build -A test`. This way, a post-receive hook could look like: ```shell #!/usr/bin/env bash set -Eeuo pipefail set -x LOGS_DIR="/data/static/ci-logs/libedn" mkdir -p "$LOGS_DIR" LOGFILE="${LOGS_DIR}/$(date -Is)-$(git rev-parse master).log" exec &> >(tee -a "${LOGFILE}") unset GIT_DIR CLONE="$(mktemp -d)" git clone . "$CLONE" pushd "$CLONE" finish() { printf "\n\n>>> exit status was %s\n" "$?" } trap finish EXIT nix-build -A test ``` We initially (lines #5 to #8) create a log file, named after *when* the run is running and for *which* commit it is running for. The `exec` and `tee` combo allows the output of the script to go both to `stdout` *and* the log file. This makes the logs output show up when you do a `git push`. Lines #10 to #13 create a fresh clone of the repository and line #20 runs the test command. After using a similar post-receive hook for a while, I now even generate a simple HTML file to make the [logs available][ci-logs] through the browser. [post-receive]: https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks [ci-logs]: https://ci.euandreh.xyz/ ## Upsides No vendor lock-in, as all you need is a server with Nix installed. And if you pin the Nixpkgs version you're using, this very simple setup yields extremely sandboxed runs on a very hermetic environment. ## Downsides Besides the many missing shiny features of this very simplistic CI, `nix-build` can be very resource intensive. Specifically, it consumes too much memory. So if it has to download too many things, or the build closure gets too big, the server might very well run out of memory.