A tool for health-checking and CI-testing OCaml packages from the opam repository.
Packages are build inside inside lightweight containers (runc on Linux, jails on FreeBSD, containerd on Windows). opam packages are built in isolation and subsequently merged to from a switch with the given packages. Caching each build as a layer for efficient incremental rebuilds.
opam install . --deps-only
dune build @install
Or with Docker:
docker build -t day10 .
day10 health-check --cache-dir /path/to/cache --opam-repository /path/to/opam-repository 0install.2.18
Create a JSON file (packages.json) containing {"packages":["pkg1.v1","pkg2.v2",...]}, then:
day10 health-check --cache-dir /path/to/cache --opam-repository /path/to/opam-repository @packages.json
Use --fork N to build packages in parallel with N worker processes.
day10 ci --cache-dir /path/to/cache --opam-repository /path/to/opam-repository /path/to/project
day10 list --opam-repository /path/to/opam-repository
Add --all-versions to list every version rather than just the latest.
| Option | Description |
|---|---|
--cache-dir DIR |
Directory for build layer cache (required) |
--opam-repository DIR |
Path to opam repository (required, repeatable) |
--ocaml-version VERSION |
OCaml compiler version (default ocaml.5.3.0) |
--json FILE |
Write results as JSON |
--md FILE |
Write results as Markdown |
--dot FILE |
Write dependency graph in Graphviz DOT format |
--tag TAG |
Import built layers into Docker with the given tag |
--with-test |
Enable test dependencies |
--dry-run |
Check if layers exist without building |
--log |
Print build logs to stdout |
--fork N |
Run N builds in parallel |
Linux builds use runc to run each package build in an OCI container with an overlay filesystem. The base layer is a Debian image built with Docker.
- Docker (for building the base layer)
- runc
- sudo access (for overlay mounts and runc invocation)
On Debian/Ubuntu:
apt install runc
git clone https://github.com/ocaml/opam-repository /home/mtelvers/opam-repository
opam install . --deps-only
dune build @install
./_build/install/default/bin/day10 health-check \
--cache-dir /home/mtelvers/cache \
--opam-repository /home/mtelvers/opam-repository \
0install.2.18
The first run builds a base Debian container image via Docker, exports it as a root filesystem, and caches it. Subsequent runs reuse the cached base layer.
To run batch health-checks:
make -j$(nproc) all
FreeBSD builds use jails with nullfs and unionfs mounts for filesystem layering. The base layer is the host's own release of FreeBSD, unpacked from its base.txz and brought up to date with freebsd-update.
git clone https://github.com/ocaml/opam-repository /home/mtelvers/opam-repository
opam install . --deps-only
dune build @install
./_build/install/default/bin/day10 health-check \
--cache-dir /home/mtelvers/cache \
--opam-repository /home/mtelvers/opam-repository \
0install.2.18
The first run downloads the base system of the host's own FreeBSD release, brings it up to date, installs opam and day10-install into the jail, creates an opam user, and caches the result as the base layer.
Windows builds use containerd with Windows containers.
dism /online /disable-feature /featurename:Windows-Defender /remove /norestart
curl.exe -L https://github.com/PowerShell/Win32-OpenSSH/releases/download/v9.2.2.0p1-Beta/OpenSSH-Win64-v9.2.2.0.msi -o openssh-win64.msi
start /wait msiexec /q /norestart /i openssh-win64.msi
copy id_ed25519.pub c:\programdata\ssh\administrators_authorized_keys
netsh advfirewall firewall set rule name="OpenSSH SSH Server Preview (sshd)" new profile=any enable=yes
On Windows Server 2025, SSHD is already installed, but not enabled.
sc config sshd start=auto
net start sshd
copy id_ed25519.pub c:\programdata\ssh\administrators_authorized_keys
netsh advfirewall firewall set rule name="OpenSSH SSH Server (sshd)" new profile=any enable=yes
curl.exe -L https://github.com/git-for-windows/git/releases/download/v2.50.0.windows.1/Git-2.50.0-64-bit.exe -o c:\windows\temp\git.exe
start /wait c:\windows\temp\git.exe /VERYSILENT /NORESTART /NOCANCEL /SP- /CLOSEAPPLICATIONS /RESTARTAPPLICATIONS /TASKS="addtopath"
On the last line select ltsc2025 if using Windows Server 2025.
curl.exe https://raw.githubusercontent.com/microsoft/Windows-Containers/refs/heads/Main/helpful_tools/Install-ContainerdRuntime/install-containerd-runtime.ps1 -o install-containerd-runtime.ps1
Set-ExecutionPolicy Bypass
.\install-containerd-runtime.ps1 -ContainerDVersion 2.1.3 -WinCNIVersion 0.3.1 -ExternalNetAdapter Ethernet -ContainerBaseImage mcr.microsoft.com/windows/servercore:ltsc2022
Create C:\Program Files\containerd\cni\conf\0-containerd-nat.conf containing:
{
"cniVersion": "0.3.0",
"name": "nat",
"type": "nat",
"master": "Ethernet",
"ipam": {
"subnet": "172.20.0.0/16",
"routes": [
{
"gateway": "172.20.0.1"
}
]
},
"capabilities": {
"portMappings": true,
"dns": true
}
}curl.exe -L https://github.com/ocaml/opam/releases/download/2.3.0/opam-2.3.0-x86_64-windows.exe -o c:\windows\opam.exe
opam init -y
Installed into the switch, so that it is on the PATH opam env sets, next to
the mingw directory holding the libffi DLL it loads.
git clone https://github.com/mtelvers/hcn-namespace
cd hcn-namespace
opam install .
git clone https://github.com/mtelvers/day10
cd day10
opam install . --deps-only
for /f "tokens=*" %i in ('opam env') do @%i
dune build
Only health-check is supported on Windows: build, exec and ci need the
project's sources mounted into the container, which the Windows backend does
not do yet. Run it from a shell where opam env has been applied, which is
what puts hcn-namespace on the PATH.
git clone http://github.com/ocaml/opam-repository c:\opam-repository
mkdir d:\cache
for /f "tokens=*" %i in ('opam env') do @%i
_build\install\default\bin\day10.exe health-check --cache-dir d:\cache --opam-repository c:\opam-repository 0install.2.18
The first run builds the base: it downloads opam and day10-install into the
container, runs opam init with opam's own Cygwin, and caches the result as
the base of a platform named cygwin-<version>-x86_64.
With --os-distribution msys2 the base unpacks an MSYS2 release into opam's
root and points opam init at it instead, so depexts come from pacman and
os-distribution is msys2 to the solver; that platform is named
msys2-<version>-x86_64.