DIY bare bones CI server with Bash and Nix
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 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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/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 (example project) through the browser.
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.
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.