mdbook-nix-eval

This is a mdbook preprocessor designed to evaluate code blocks containing nix expressions.

```test-file.nix builtins.langVersion ```

code blocks with nix expressions are evaluated using nix.

```nix builtins.langVersion ```

Simple Evaluation

builtins.langVersion
5

Importing Files

Code blocks with filenames ending in ".nix" will be put into a temporary directory for each chapter.

langVersion.nix

builtins.langVersion
5

As the expression is written to a known file name, it's possible to import as usual.

let version = import ./langVersion.nix; in "Nix Langauge Version: ${toString version}"
"Nix Langauge Version: 5"

With evaluation errors passed through

let version = import ./langVersion.nix; in "Nix Langauge Version: ${version}"
error: cannot coerce an integer to a string, at /tmp/.tmpudFalA/eval.nix:4:2

Expressions can be functions

{ pkgs ? import <nixpkgs> {} }: pkgs.path
"/nix/store/26gcnrhl5v14s6s2syxi1ynpcmik1xvd-nixpkgs"

However, they should have defaults, otherwise they won't return an evaluation that makes sense to display.

without-defaults.nix

{ pkgs }: pkgs.path
error: anonymous function at /tmp/.tmpudFalA/without-defaults.nix:1:1 called without required argument 'pkgs', at (string):1:1

Default-less function arg files can still be referenced later without issue

import ./without-defaults.nix { pkgs = import <nixpkgs> {}; }
"/nix/store/26gcnrhl5v14s6s2syxi1ynpcmik1xvd-nixpkgs"

More Examples

If the nix-builder has sandboxing enabled, there should be limited access to sensitive info, but... it's probably best to only run trusted expressions.

let run = (with import <nixpkgs> {}; runCommand "foo" {} "ls -l ~/.ssh > $out"); in builtins.readFile run
building '/nix/store/hsvgfm0h37yqirmgjnzqsw9jrrh1shrc-foo.drv'... ls: cannot access '/homeless-shelter/.ssh': No such file or directory builder for '/nix/store/hsvgfm0h37yqirmgjnzqsw9jrrh1shrc-foo.drv' failed with exit code 2 error: build of '/nix/store/hsvgfm0h37yqirmgjnzqsw9jrrh1shrc-foo.drv' failed

Network access is allowed in some (most?) cases, so again trusted expressions only are advised.

{ pkgs ? import <nixpkgs> {} }: let gitignore = builtins.fetchurl { url = "https://www.toptal.com/developers/gitignore/api/jetbrains,linux,macos,git"; name = "gitignore"; sha256 = "0fn3632fdz5rbvbkwnn82q6qsdsq2haxc7mlbm536g69zlr41c1z"; }; in { out = builtins.elemAt (pkgs.lib.splitString "\n" (builtins.readFile gitignore)) 1; }
{ "out": "# Created by https://www.toptal.com/developers/gitignore/api/jetbrains,linux,macos,git" }
builtins.readFile (with import <nixpkgs> {}; runCommand "foo" {} "date > $out; ${fping}/bin/fping -c5 1.1.1.1 >> $out")
" Fri Mar 5 07:46:36 UTC 2021 1.1.1.1 : [0], 64 bytes, 13.8 ms (13.8 avg, 0% loss) 1.1.1.1 : [1], 64 bytes, 12.5 ms (13.1 avg, 0% loss) 1.1.1.1 : [2], 64 bytes, 12.6 ms (13.0 avg, 0% loss) 1.1.1.1 : [3], 64 bytes, 12.5 ms (12.9 avg, 0% loss) 1.1.1.1 : [4], 64 bytes, 12.6 ms (12.8 avg, 0% loss) "

Remote Build Machines

If you have remote building enabled,

systems.nix

{ linux = (with import <nixpkgs> { system = "x86_64-linux"; }; runCommand "foo" {} "uname > $out"); darwin = (with import <nixpkgs> { system = "x86_64-darwin"; }; runCommand "foo" {} "uname > $out"); }
{ "darwin": "/nix/store/jnwb5xrkyncn11y3a4b8abad6qmnysn9-foo", "linux": "/nix/store/psny6mvwyc4l258mqhkfahl7asf6bdhh-foo" }
builtins.readFile (import ./systems.nix).linux
"Linux"
builtins.readFile (import ./systems.nix).darwin
"Darwin"

Supported output types

null

null
null

string

"this is a string"
"this is a string"

(note: the formatting is slightly modified to better show multiline)

"this is\na multiline string\nnote that trailing whitespace is trimmed\n"
" this is a multiline string note that trailing whitespace is trimmed "

numeric

12345
12345

lists (arrays)

[1234 6789]
[ 1234, 6789 ]

sets (objects)

{first = 1234; second = 6789;}
{ "first": 1234, "second": 6789 }

functions? nope

builtins.readFile
error: cannot convert the built-in function 'readFile' to JSON