Scraping a build log for the word "error" is no way to run a pipeline. iets prints JSON, exits with codes you can branch on, and can write everything it says on stderr as records a program can read. This page shows each of those, and the plumbing operations you can pipe together.
The example files come from other pages. hello.nix and greeting.nix are
in Get started with iets, demo.nix is in
Replace your Nix tools with iets, and src.nix is in
Fix a wrong fixed-output hash. All but demo.nix read
<nixpkgs> or builtins.currentSystem, so their commands add --impure.
The record shapes are in Errors, exit codes and log records, and the contract of every plumbing operation is in Plumbing operations.
--json is on eval, build, diff, search, prefetch,
print-dev-env, parse and cache paths:
iets eval --impure --strict --json -I nixpkgs=$nixpkgs hello.nix{"drv":"/nix/store/2bki…-hello-2.12.3.drv","name":"hello-2.12.3"}iets diff --json -A a -A b demo.nix{"a":"/nix/store/623k…-demo.drv","b":"/nix/store/sx7z…-demo.drv",
"changes":[{"a":"\"-c\"\n\"echo one > $out\"\n","b":"\"-c\"\n\"echo two > $out\"\n","key":"","what":"args"},
{"a":"hello","b":"goodbye","key":"GREETING","what":"env"}],
"equal":false}iets eval-jobs writes JSON lines by default, one object per job.
Save this as dep.nix. It depends on a derivation that always fails:
let
bad = derivation {
name = "bad-dep"; system = "x86_64-linux"; builder = "/bin/sh";
args = [ "-c" "exit 1" ];
};
in derivation {
name = "needs-bad"; system = "x86_64-linux"; builder = "/bin/sh";
args = [ "-c" "echo ${bad} > $out" ];
}iets build --builder daemon dep.nix; echo $?100
The code is 0 for success, 1 for any error, 2 for a usage error of a native command, and 100, 101, 102 or 104 when a build failed, timed out, had a hash mismatch or wasn't deterministic. Several failures combine their bits, so a timeout next to a failed build gives 101. Real Nix gives these codes only when it builds in its own store. As a client of a daemon it exits 1, and iets reads the status of each build so you get the code either way. A build that fails on a builder that reports only a generic failure (status 9) exits 1.
Three commands differ. iets diff follows diff(1): 0 for equal, 1 for
different, 2 for anything else. The classic names exit 1 on an error, as Nix
does. iets eval-jobs exits 0 when only jobs fail, and their errors are in
the job lines. Errors, exit codes and log records
has the full table.
--log-format internal-json turns every stderr line into @nix followed by
one JSON object. It's the format of Nix's own JSON logger, so
nix-output-monitor reads it unchanged:
iets eval --log-format internal-json -E '{a=1;}.b'@nix {"action":"start","id":10831246095548416,"level":4,"parent":0,"sid":"01a1225c-a53a-7873-8635-0fc0dd34ee39","text":"evaluating the expression","type":0}
@nix {"action":"msg","code":"missing-attr","column":1,"file":"«string»:1:1","level":0,"line":1,"msg":"error: attribute 'b' missing\n at «string»:1:1","raw_msg":"attribute 'b' missing","sid":"01a1225c-a53a-7873-8635-0fc0dd34ee39"}
@nix {"action":"stop","id":10831246095548416,"sid":"01a1225c-a53a-7873-8635-0fc0dd34ee39"}
start and stop bracket an activity, as in Nix. The msg record is the
error.
These keys exist only in iets, and they're the ones to build on:
code: a stable error code. Branch on it, since the message text changes.hint: what to do about it, there only when iets is sure. A hash mismatch names the hash the build got. Aflake.lockentry without anarHashnames the input and thenix flake updatethat locks it.sid: one session id on every record of one run. Set it yourself withIETS_SESSION_IDto match a run with your own logs.
raw_msg is the message alone, and msg is the whole text iets would have
printed. level is 0 for an error and 1 for a warning. With --show-trace
you also get a trace array, innermost first.
Say you want the records in a file but still want to watch the run.
json-log-path writes every record to a file or a unix socket, without the
@nix prefix, whatever --log-format says. Stderr stays as it was:
iets eval --json-log-path ./log.json -E '{a=1;}.b'
cat log.json{"action":"start","id":10831301930123264,"level":4,"parent":0,"sid":"01a1225c-a547-7d12-82bf-d706defe431e","text":"evaluating the expression","type":0}
{"action":"msg","code":"missing-attr","column":1,"file":"«string»:1:1","level":0,"line":1,"msg":"error: attribute 'b' missing\n at «string»:1:1","raw_msg":"attribute 'b' missing","sid":"01a1225c-a547-7d12-82bf-d706defe431e"}
{"action":"stop","id":10831301930123264,"sid":"01a1225c-a547-7d12-82bf-d706defe431e"}--option json-log-path PATH and the nix.conf key of the same name do the
same. iets appends to a file and creates it with mode 0644. If the path can't
be opened, you get one error (ignored) line on stderr and the run goes on.
As in Nix, two processes that write one file can mix their records.
Without a terminal, two behaviors change. Set them yourself instead of
trusting detection. The first is fix-hashes:
iets --iets-option fix-hashes always build --impure --builder daemon src.nixIt defaults to ask, which asks only on a terminal. Anywhere else, a failing
hash is reported and nothing is fixed. always fixes without asking
(Fix a wrong fixed-output hash).
The second is color in iets search. Nix writes its escape sequences to
stdout even when stdout isn't a terminal, and iets does the same, byte for
byte. Use --json when a program reads the output.
iets plumbing is the layer under the everyday commands. Each operation
takes JSON and gives JSON, with nothing else on stdout. The operations are
resolve, plan, missing, build and fetch.
An operation that takes roots accepts --roots - and reads the stdout of the
one before it, so you can pipe them:
iets plumbing resolve --impure greeting.nix{"attrPaths":[],"roots":[{"attr":null,"drv":"/nix/store/6jq7…-greeting.drv","output":null}]}iets plumbing resolve --impure greeting.nix | iets plumbing plan --roots -{"drvs":["/nix/store/6jq7…-greeting.drv"],"sources":[],"storeDrvs":1}iets plumbing resolve --impure greeting.nix | iets plumbing build --builder daemon --roots -{"outcomes":[{"attr":"greeting.drv","drv":"/nix/store/6jq7…-greeting.drv",
"logTail":[],
"outputs":{"out":"/nix/store/hl2d…-greeting"},
"reason":null,"success":true}]}missing takes the same pipe and prints what iets build --dry-run --json
prints. Add --check-cache-status for a per-root verdict.
When you pipe, the next operation reads the closure from /nix/store by
default. That works only for a derivation that's already there. To hand over
one that isn't, dump it:
iets plumbing resolve --dump-drvs dd demo.nix -A a{"attrPaths":[],"drvDir":"dd","roots":[{"attr":"a","drv":"/nix/store/623k…-demo.drv","output":null}]}The drvDir key travels with the roots, so the next operation reads the
derivations from that directory. --drvs DIR sets it by hand.
iets plumbing --help lists every operation, and
iets help reference/plumbing prints the reference page.