om ci
om ci
runs continuous integration (CI)-friendly builds for your project. It builds all outputs in the flake, or optionally its sub-flakes. You can run om ci
locally or in an actual CI envirnoment, like GitHub Actions. Using devour-flake it will automatically build the following outputs:
Type | Output Key |
---|---|
Standard flake outputs | packages , apps , checks , devShells |
NixOS | nixosConfigurations.* |
nix-darwin | darwinConfigurations.* |
home-manager | legacyPackages.${system}.homeConfigurations.* |
A result
symlink is also produced, containing a JSON of all built paths. See here.
Basic Usage
om ci run
accepts any valid flake URL or a Github PR URL.
# Run CI on current directory flake
$ om ci # Or `om ci run` or `om ci run .`
# Run CI on a local flake (default is $PWD)
$ om ci run ~/code/myproject
# Pass custom arguments to `nix` after '--'
$ om ci run ~/code/myproject -- --accept-flake-config
# Run CI on a github repo
$ om ci run github:hercules-ci/hercules-ci-agent
# Run CI on a github PR
$ om ci run https://github.com/srid/emanote/pull/451
# Run CI only the selected sub-flake
$ git clone https://github.com/srid/haskell-flake && cd haskell-flake
$ om ci run .#default.dev
# Run CI remotely over SSH
$ om ci run --on ssh://myname@myserver ~/code/myproject
Results JSON and closure
Just like nix build
, om ci
will produce a result
symlink that contains a JSON of all store paths built. Use options --out-link <PATH>
and --no-link
to control this behaviour.
As long as this symlink exists, your built paths will survive garbage collection, because the closure of this symlink contains the entire build closure.
Note that in order to include all build dependencies, you should pass --include-all-dependencies
, viz.:
om ci run --include-all-dependencies | xargs cachix push mycache
The above command will push the entire build closure (runtime and build dependencies) to the given cache.
Using in Github Actions
In addition to serving the purpose of being a “local CI”, om ci
can be used in Github Actions to enable CI for your GitHub repositories.
Standard Runners
Add this to your workflow file (.github/workflows/ci.yml
) to build all flake outputs using GitHub provided runners:
- uses: actions/checkout@v4
- uses: DeterminateSystems/nix-installer-action@main
- name: Install omnix
run: nix --accept-flake-config profile install "github:juspay/omnix"
- run: om ci
Self-hosted Runners with Job Matrix
Here’s a more advanced example that configures a job matrix. This is useful when you want to run the CI on multiple systems (e.g. aarch64-linux
, aarch64-darwin
), each captured as a separate job by GitHub, as shown in the screenshot below. It also, incidentally, demonstrates how to use self-hosted runners.
The om ci gh-matrix
command outputs the matrix JSON for creating a matrix of job variations. An example configuration, using self-hosted runners, is shown below.
note
This currently requires an explicit CI configuration in your flake, setting om.ci.default.root.dir
to .
.
# Run on aarch64-linux and aarch64-darwin
jobs:
configure:
runs-on: x86_64-linux
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- uses: actions/checkout@v4
- id: set-matrix
run: |
set -euxo pipefail
MATRIX="$(om ci gh-matrix --systems=x86_64-linux,aarch64-darwin | jq -c .)"
echo "matrix=$MATRIX" >> $GITHUB_OUTPUT
nix:
runs-on: ${{ matrix.system }}
needs: configure
strategy:
matrix: ${{ fromJson(needs.configure.outputs.matrix) }}
fail-fast: false
steps:
- uses: actions/checkout@v4
- run: om ci run --systems "${{ matrix.system }}" ".#default.${{ matrix.subflake }}"
tip
If your builds fail due to GitHub’s rate limiting, consider passing --extra-access-tokens
(see an example PR).
Configuring
By default, om ci
will build the top-level flake, but you can tell it to build sub-flakes (here, ./dir1
and ./dir2
) by adding the following to your Om configuration:
# myproject/flake.nix
{
om.ci.default = {
dir1 = {
dir = "dir1";
};
dir2 = {
dir = "dir2";
overrideInputs.myproject = ./.;
};
}
}
You can have more than one CI configuration. For eg., om ci run .#foo
will run the configuration from om.ci.foo
flake output.
Custom CI actions
You can define custom CI actions in your flake, which will be run as part of om ci run
. For example, to run tests in the nix develop shell:
{
om.ci.default = {
root = {
dir = ".";
steps = {
# The build step is enabled by default. It builds all flake outputs.
build.enable = true;
# Other steps include: lockfile & flake-check
# Users can define custom steps to run any arbitrary flake app or devShell command.
custom = {
# Here, we run cargo tests in the nix shell
# This equivalent to `nix develop .#default -c cargo test`
cargo-test = {
type = "devshell";
# name = "default"
command = [ "cargo" "test" ];
};
# We can also flake apps
# This is equivalent to `nix run .#check-closure-size`
closure-size = {
type = "app";
name = "check-closure-size";
};
};
};
};
};
}
For a real-world example of custom steps, checkout Omnix’s configuration.
Remote CI
Omnix can run CI over SSH.
om ci run --on ssh://myname@myserver ~/code/myproject
What this does:
- Copy the flake source to the remote server, and run
om ci
there - Copy the built paths back to local store
Options
- Pass
copy-inputs=true
if you wish to copy all flake inputs recursively. This is useful if you have private Git inputs. For example,om ci run --on "ssh://myname@myserver?copy-inputs=true" ~/code/myproject
- Omnix copies the results back to local store, unless
--no-link
was passed.
Examples
Some real-world examples of how om ci
is used with specific configurations:
- omnix
- services-flake
- nixos-flake
- haskell-flake
- Here’s a blog post that talks about how it is used in haskell-flake
- superposition
- haskell-rust-ffi-template
What it does
- Check that the Nix version is not tool old (using
om health
) - Determine the list of flakes in the repo to build
- By default, this is the root flake.
- The user can also explicitly specify multiple sub-flakes in
om.ci.default
output of their root flake.
- For each (sub)flake identified,
om ci run
will run the following steps:- Check that
flake.lock
is up to date, if applicable. - Build all flake outputs, using devour-flake1
- Then, print the built store paths to stdout
- If the
flake-check
step is enabled (example), runnix flake check
- Run user defined custom steps
- Check that
Support for flake-schemas is planned
See also
- github-nix-ci - A simple NixOS & nix-darwin module for self-hosting GitHub runners
- jenkins-nix-ci - Jenkins NixOS module that supports
nixci
(predecessor ofom ci
) as a Groovy function - cachix-push - A flake-parts module that provides an app to enable whitelisted pushing and pinning of store paths to cachix.