206 lines
6.7 KiB
XML
206 lines
6.7 KiB
XML
<chapter xmlns="http://docbook.org/ns/docbook"
|
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
xmlns:xi="http://www.w3.org/2001/XInclude"
|
|
xml:id="chap-diff-hook"
|
|
version="5.0"
|
|
>
|
|
|
|
<title>Verifying Build Reproducibility with <option linkend="conf-diff-hook">diff-hook</option></title>
|
|
|
|
<subtitle>Check build reproducibility by running builds multiple times
|
|
and comparing their results.</subtitle>
|
|
|
|
<para>Specify a program with Nix's <xref linkend="conf-diff-hook" /> to
|
|
compare build results when two builds produce different results. Note:
|
|
this hook is only executed if the results are not the same, this hook
|
|
is not used for determining if the results are the same.</para>
|
|
|
|
<para>For purposes of demonstration, we'll use the following Nix file,
|
|
<filename>deterministic.nix</filename> for testing:</para>
|
|
|
|
<programlisting>
|
|
let
|
|
inherit (import <nixpkgs> {}) runCommand;
|
|
in {
|
|
stable = runCommand "stable" {} ''
|
|
touch $out
|
|
'';
|
|
|
|
unstable = runCommand "unstable" {} ''
|
|
echo $RANDOM > $out
|
|
'';
|
|
}
|
|
</programlisting>
|
|
|
|
<para>Additionally, <filename>nix.conf</filename> contains:
|
|
|
|
<programlisting>
|
|
diff-hook = /etc/nix/my-diff-hook
|
|
run-diff-hook = true
|
|
</programlisting>
|
|
|
|
where <filename>/etc/nix/my-diff-hook</filename> is an executable
|
|
file containing:
|
|
|
|
<programlisting>
|
|
#!/bin/sh
|
|
exec >&2
|
|
echo "For derivation $3:"
|
|
/run/current-system/sw/bin/diff -r "$1" "$2"
|
|
</programlisting>
|
|
|
|
</para>
|
|
|
|
<para>The diff hook is executed by the same user and group who ran the
|
|
build. However, the diff hook does not have write access to the store
|
|
path just built.</para>
|
|
|
|
<section>
|
|
<title>
|
|
Spot-Checking Build Determinism
|
|
</title>
|
|
|
|
<para>
|
|
Verify a path which already exists in the Nix store by passing
|
|
<option>--check</option> to the build command.
|
|
</para>
|
|
|
|
<para>If the build passes and is deterministic, Nix will exit with a
|
|
status code of 0:</para>
|
|
|
|
<screen>
|
|
$ nix-build ./deterministic.nix -A stable
|
|
this derivation will be built:
|
|
/nix/store/z98fasz2jqy9gs0xbvdj939p27jwda38-stable.drv
|
|
building '/nix/store/z98fasz2jqy9gs0xbvdj939p27jwda38-stable.drv'...
|
|
/nix/store/yyxlzw3vqaas7wfp04g0b1xg51f2czgq-stable
|
|
|
|
$ nix-build ./deterministic.nix -A stable --check
|
|
checking outputs of '/nix/store/z98fasz2jqy9gs0xbvdj939p27jwda38-stable.drv'...
|
|
/nix/store/yyxlzw3vqaas7wfp04g0b1xg51f2czgq-stable
|
|
</screen>
|
|
|
|
<para>If the build is not deterministic, Nix will exit with a status
|
|
code of 1:</para>
|
|
|
|
<screen>
|
|
$ nix-build ./deterministic.nix -A unstable
|
|
this derivation will be built:
|
|
/nix/store/cgl13lbj1w368r5z8gywipl1ifli7dhk-unstable.drv
|
|
building '/nix/store/cgl13lbj1w368r5z8gywipl1ifli7dhk-unstable.drv'...
|
|
/nix/store/krpqk0l9ib0ibi1d2w52z293zw455cap-unstable
|
|
|
|
$ nix-build ./deterministic.nix -A unstable --check
|
|
checking outputs of '/nix/store/cgl13lbj1w368r5z8gywipl1ifli7dhk-unstable.drv'...
|
|
error: derivation '/nix/store/cgl13lbj1w368r5z8gywipl1ifli7dhk-unstable.drv' may not be deterministic: output '/nix/store/krpqk0l9ib0ibi1d2w52z293zw455cap-unstable' differs
|
|
</screen>
|
|
|
|
<para>In the Nix daemon's log, we will now see:
|
|
<screen>
|
|
For derivation /nix/store/cgl13lbj1w368r5z8gywipl1ifli7dhk-unstable.drv:
|
|
1c1
|
|
< 8108
|
|
---
|
|
> 30204
|
|
</screen>
|
|
</para>
|
|
|
|
<para>Using <option>--check</option> with <option>--keep-failed</option>
|
|
will cause Nix to keep the second build's output in a special,
|
|
<literal>.check</literal> path:</para>
|
|
|
|
<screen>
|
|
$ nix-build ./deterministic.nix -A unstable --check --keep-failed
|
|
checking outputs of '/nix/store/cgl13lbj1w368r5z8gywipl1ifli7dhk-unstable.drv'...
|
|
note: keeping build directory '/tmp/nix-build-unstable.drv-0'
|
|
error: derivation '/nix/store/cgl13lbj1w368r5z8gywipl1ifli7dhk-unstable.drv' may not be deterministic: output '/nix/store/krpqk0l9ib0ibi1d2w52z293zw455cap-unstable' differs from '/nix/store/krpqk0l9ib0ibi1d2w52z293zw455cap-unstable.check'
|
|
</screen>
|
|
|
|
<para>In particular, notice the
|
|
<literal>/nix/store/krpqk0l9ib0ibi1d2w52z293zw455cap-unstable.check</literal>
|
|
output. Nix has copied the build results to that directory where you
|
|
can examine it.</para>
|
|
|
|
<note xml:id="check-dirs-are-unregistered">
|
|
<title><literal>.check</literal> paths are not registered store paths</title>
|
|
|
|
<para>Check paths are not protected against garbage collection,
|
|
and this path will be deleted on the next garbage collection.</para>
|
|
|
|
<para>The path is guaranteed to be alive for the duration of
|
|
<xref linkend="conf-diff-hook" />'s execution, but may be deleted
|
|
any time after.</para>
|
|
|
|
<para>If the comparison is performed as part of automated tooling,
|
|
please use the diff-hook or author your tooling to handle the case
|
|
where the build was not deterministic and also a check path does
|
|
not exist.</para>
|
|
</note>
|
|
|
|
<para>
|
|
<option>--check</option> is only usable if the derivation has
|
|
been built on the system already. If the derivation has not been
|
|
built Nix will fail with the error:
|
|
<screen>
|
|
error: some outputs of '/nix/store/hzi1h60z2qf0nb85iwnpvrai3j2w7rr6-unstable.drv' are not valid, so checking is not possible
|
|
</screen>
|
|
|
|
Run the build without <option>--check</option>, and then try with
|
|
<option>--check</option> again.
|
|
</para>
|
|
</section>
|
|
|
|
<section>
|
|
<title>
|
|
Automatic and Optionally Enforced Determinism Verification
|
|
</title>
|
|
|
|
<para>
|
|
Automatically verify every build at build time by executing the
|
|
build multiple times.
|
|
</para>
|
|
|
|
<para>
|
|
Setting <xref linkend="conf-repeat" /> and
|
|
<xref linkend="conf-enforce-determinism" /> in your
|
|
<filename>nix.conf</filename> permits the automated verification
|
|
of every build Nix performs.
|
|
</para>
|
|
|
|
<para>
|
|
The following configuration will run each build three times, and
|
|
will require the build to be deterministic:
|
|
|
|
<programlisting>
|
|
enforce-determinism = true
|
|
repeat = 2
|
|
</programlisting>
|
|
</para>
|
|
|
|
<para>
|
|
Setting <xref linkend="conf-enforce-determinism" /> to false as in
|
|
the following configuration will run the build multiple times,
|
|
execute the build hook, but will allow the build to succeed even
|
|
if it does not build reproducibly:
|
|
|
|
<programlisting>
|
|
enforce-determinism = false
|
|
repeat = 1
|
|
</programlisting>
|
|
</para>
|
|
|
|
<para>
|
|
An example output of this configuration:
|
|
<screen>
|
|
$ nix-build ./test.nix -A unstable
|
|
this derivation will be built:
|
|
/nix/store/ch6llwpr2h8c3jmnf3f2ghkhx59aa97f-unstable.drv
|
|
building '/nix/store/ch6llwpr2h8c3jmnf3f2ghkhx59aa97f-unstable.drv' (round 1/2)...
|
|
building '/nix/store/ch6llwpr2h8c3jmnf3f2ghkhx59aa97f-unstable.drv' (round 2/2)...
|
|
output '/nix/store/6xg356v9gl03hpbbg8gws77n19qanh02-unstable' of '/nix/store/ch6llwpr2h8c3jmnf3f2ghkhx59aa97f-unstable.drv' differs from '/nix/store/6xg356v9gl03hpbbg8gws77n19qanh02-unstable.check' from previous round
|
|
/nix/store/6xg356v9gl03hpbbg8gws77n19qanh02-unstable
|
|
</screen>
|
|
</para>
|
|
</section>
|
|
</chapter>
|