git-subtree-dir: third_party/nix
git-subtree-mainline: cf8cd640c1
git-subtree-split: be66c7a6b24e3c3c6157fd37b86c7203d14acf10
		
	
			
		
			
				
	
	
		
			205 lines
		
	
	
	
		
			6.7 KiB
		
	
	
	
		
			XML
		
	
	
	
	
	
			
		
		
	
	
			205 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
 | |
| these derivations 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
 | |
| these derivations 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
 | |
| these derivations 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>
 |