Merge pull request #2779 from LnL7/build-exit-codes
build: add exit code for hash and check mismatches
This commit is contained in:
		
						commit
						7e1c85c5fb
					
				
					 6 changed files with 111 additions and 12 deletions
				
			
		|  | @ -215,6 +215,48 @@ printed.)</para> | |||
| 
 | ||||
| </variablelist> | ||||
| 
 | ||||
| <para>Special exit codes:</para> | ||||
| 
 | ||||
| <variablelist> | ||||
| 
 | ||||
|   <varlistentry><term><literal>100</literal></term> | ||||
|     <listitem><para>Generic build failure, the builder process | ||||
|     returned with a non-zero exit code.</para></listitem> | ||||
|   </varlistentry> | ||||
| 
 | ||||
|   <varlistentry><term><literal>101</literal></term> | ||||
|     <listitem><para>Build timeout, the build was aborted because it | ||||
|     did not complete within the specified <link | ||||
|     linkend='conf-timeout'><literal>timeout</literal></link>. | ||||
|     </para></listitem> | ||||
|   </varlistentry> | ||||
| 
 | ||||
|   <varlistentry><term><literal>102</literal></term> | ||||
|     <listitem><para>Hash mismatch, the build output was rejected | ||||
|     because it does not match the specified <link | ||||
|     linkend="fixed-output-drvs"><varname>outputHash</varname></link>. | ||||
|     </para></listitem> | ||||
|   </varlistentry> | ||||
| 
 | ||||
|   <varlistentry><term><literal>104</literal></term> | ||||
|     <listitem><para>Not deterministic, the build succeeded in check | ||||
|     mode but the resulting output is not binary reproducable.</para> | ||||
|     </listitem> | ||||
|   </varlistentry> | ||||
| 
 | ||||
| </variablelist> | ||||
| 
 | ||||
| <para>With the <option>--keep-going</option> flag it's possible for | ||||
| multiple failures to occur, in this case the 1xx status codes are or combined | ||||
| using binary or. <screen> | ||||
| 1100100 | ||||
|    ^^^^ | ||||
|    |||`- timeout | ||||
|    ||`-- output hash mismatch | ||||
|    |`--- build failure | ||||
|    `---- not deterministic | ||||
| </screen></para> | ||||
| 
 | ||||
| </refsection> | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -266,6 +266,12 @@ public: | |||
|     /* Set if at least one derivation had a timeout. */ | ||||
|     bool timedOut; | ||||
| 
 | ||||
|     /* Set if at least one derivation fails with a hash mismatch. */ | ||||
|     bool hashMismatch; | ||||
| 
 | ||||
|     /* Set if at least one derivation is not deterministic in check mode. */ | ||||
|     bool checkMismatch; | ||||
| 
 | ||||
|     LocalStore & store; | ||||
| 
 | ||||
|     std::unique_ptr<HookInstance> hook; | ||||
|  | @ -3213,6 +3219,7 @@ void DerivationGoal::registerOutputs() | |||
| 
 | ||||
|                 /* Throw an error after registering the path as
 | ||||
|                    valid. */ | ||||
|                 worker.hashMismatch = true; | ||||
|                 delayedException = std::make_exception_ptr( | ||||
|                     BuildError("hash mismatch in fixed-output derivation '%s':\n  wanted: %s\n  got:    %s", | ||||
|                         dest, h.to_string(), h2.to_string())); | ||||
|  | @ -3255,6 +3262,7 @@ void DerivationGoal::registerOutputs() | |||
|             if (!worker.store.isValidPath(path)) continue; | ||||
|             auto info = *worker.store.queryPathInfo(path); | ||||
|             if (hash.first != info.narHash) { | ||||
|                 worker.checkMismatch = true; | ||||
|                 if (settings.runDiffHook || settings.keepFailed) { | ||||
|                     Path dst = worker.store.toRealPath(path + checkSuffix); | ||||
|                     deletePath(dst); | ||||
|  | @ -3266,10 +3274,10 @@ void DerivationGoal::registerOutputs() | |||
|                         buildUser ? buildUser->getGID() : getgid(), | ||||
|                         path, dst, drvPath, tmpDir); | ||||
| 
 | ||||
|                     throw Error(format("derivation '%1%' may not be deterministic: output '%2%' differs from '%3%'") | ||||
|                     throw NotDeterministic(format("derivation '%1%' may not be deterministic: output '%2%' differs from '%3%'") | ||||
|                         % drvPath % path % dst); | ||||
|                 } else | ||||
|                     throw Error(format("derivation '%1%' may not be deterministic: output '%2%' differs") | ||||
|                     throw NotDeterministic(format("derivation '%1%' may not be deterministic: output '%2%' differs") | ||||
|                         % drvPath % path); | ||||
|             } | ||||
| 
 | ||||
|  | @ -4101,6 +4109,8 @@ Worker::Worker(LocalStore & store) | |||
|     lastWokenUp = steady_time_point::min(); | ||||
|     permanentFailure = false; | ||||
|     timedOut = false; | ||||
|     hashMismatch = false; | ||||
|     checkMismatch = false; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  | @ -4461,7 +4471,29 @@ void Worker::waitForInput() | |||
| 
 | ||||
| unsigned int Worker::exitStatus() | ||||
| { | ||||
|     return timedOut ? 101 : (permanentFailure ? 100 : 1); | ||||
|     /*
 | ||||
|      * 1100100 | ||||
|      *    ^^^^ | ||||
|      *    |||`- timeout | ||||
|      *    ||`-- output hash mismatch | ||||
|      *    |`--- build failure | ||||
|      *    `---- not deterministic | ||||
|      */ | ||||
|     unsigned int mask = 0; | ||||
|     bool buildFailure = permanentFailure || timedOut || hashMismatch; | ||||
|     if (buildFailure) | ||||
|         mask |= 0x04;  // 100
 | ||||
|     if (timedOut) | ||||
|         mask |= 0x01;  // 101
 | ||||
|     if (hashMismatch) | ||||
|         mask |= 0x02;  // 102
 | ||||
|     if (checkMismatch) { | ||||
|         mask |= 0x08;  // 104
 | ||||
|     } | ||||
| 
 | ||||
|     if (mask) | ||||
|         mask |= 0x60; | ||||
|     return mask ? mask : 1; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -855,10 +855,11 @@ CachedDownloadResult Downloader::downloadCached( | |||
|     } | ||||
| 
 | ||||
|     if (expectedStorePath != "" && storePath != expectedStorePath) { | ||||
|         unsigned int statusCode = 102; | ||||
|         Hash gotHash = request.unpack | ||||
|             ? hashPath(request.expectedHash.type, store->toRealPath(storePath)).first | ||||
|             : hashFile(request.expectedHash.type, store->toRealPath(storePath)); | ||||
|         throw nix::Error("hash mismatch in file downloaded from '%s':\n  wanted: %s\n  got:    %s", | ||||
|         throw nix::Error(statusCode, "hash mismatch in file downloaded from '%s':\n  wanted: %s\n  got:    %s", | ||||
|             url, request.expectedHash.to_string(), gotHash.to_string()); | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -10,6 +10,11 @@ with import ./config.nix; | |||
|       ''; | ||||
|   }; | ||||
| 
 | ||||
|   hashmismatch = import <nix/fetchurl.nix> { | ||||
|     url = "file://" + toString ./dummy; | ||||
|     sha256 = "0mdqa9w1p6cmli6976v4wi0sw9r4p5prkj7lzfd1877wk11c9c73"; | ||||
|   }; | ||||
| 
 | ||||
|   fetchurl = import <nix/fetchurl.nix> { | ||||
|     url = "file://" + toString ./lang/eval-okay-xml.exp.xml; | ||||
|     sha256 = "0kg4sla7ihm8ijr8cb3117fhl99zrc2bwy1jrngsfmkh8bav4m0v"; | ||||
|  |  | |||
|  | @ -6,14 +6,16 @@ nix-build dependencies.nix --no-out-link | |||
| nix-build dependencies.nix --no-out-link --check | ||||
| 
 | ||||
| nix-build check.nix -A nondeterministic --no-out-link | ||||
| (! nix-build check.nix -A nondeterministic --no-out-link --check 2> $TEST_ROOT/log) | ||||
| nix-build check.nix -A nondeterministic --no-out-link --check 2> $TEST_ROOT/log || status=$? | ||||
| grep 'may not be deterministic' $TEST_ROOT/log | ||||
| [ "$status" = "104" ] | ||||
| 
 | ||||
| clearStore | ||||
| 
 | ||||
| nix-build dependencies.nix --no-out-link --repeat 3 | ||||
| 
 | ||||
| (! nix-build check.nix -A nondeterministic --no-out-link --repeat 1 2> $TEST_ROOT/log) | ||||
| nix-build check.nix -A nondeterministic --no-out-link --repeat 1 2> $TEST_ROOT/log || status=$? | ||||
| [ "$status" = "1" ] | ||||
| grep 'differs from previous round' $TEST_ROOT/log | ||||
| 
 | ||||
| path=$(nix-build check.nix -A fetchurl --no-out-link --hashed-mirrors '') | ||||
|  | @ -23,10 +25,23 @@ echo foo > $path | |||
| chmod -w $path | ||||
| 
 | ||||
| nix-build check.nix -A fetchurl --no-out-link --check --hashed-mirrors '' | ||||
| 
 | ||||
| # Note: "check" doesn't repair anything, it just compares to the hash stored in the database. | ||||
| [[ $(cat $path) = foo ]] | ||||
| 
 | ||||
| nix-build check.nix -A fetchurl --no-out-link --repair --hashed-mirrors '' | ||||
| 
 | ||||
| [[ $(cat $path) != foo ]] | ||||
| 
 | ||||
| nix-build check.nix -A hashmismatch --no-out-link --hashed-mirrors '' || status=$? | ||||
| [ "$status" = "102" ] | ||||
| 
 | ||||
| echo -n > ./dummy | ||||
| nix-build check.nix -A hashmismatch --no-out-link --hashed-mirrors '' | ||||
| echo 'Hello World' > ./dummy | ||||
| 
 | ||||
| nix-build check.nix -A hashmismatch --no-out-link --check --hashed-mirrors '' || status=$? | ||||
| [ "$status" = "102" ] | ||||
| 
 | ||||
| # Multiple failures with --keep-going | ||||
| nix-build check.nix -A nondeterministic --no-out-link | ||||
| nix-build check.nix -A nondeterministic -A hashmismatch --no-out-link --check --keep-going --hashed-mirrors '' || status=$? | ||||
| [ "$status" = "110" ] | ||||
|  |  | |||
|  | @ -2,10 +2,14 @@ | |||
| 
 | ||||
| source common.sh | ||||
| 
 | ||||
| failed=0 | ||||
| messages="`nix-build -Q timeout.nix -A infiniteLoop --timeout 2 2>&1 || failed=1`" | ||||
| if [ $failed -ne 0 ]; then | ||||
|     echo "error: 'nix-store' succeeded; should have timed out" | ||||
| 
 | ||||
| set +e | ||||
| messages=$(nix-build -Q timeout.nix -A infiniteLoop --timeout 2 2>&1) | ||||
| status=$? | ||||
| set -e | ||||
| 
 | ||||
| if [ $status -ne 101 ]; then | ||||
|     echo "error: 'nix-store' exited with '$status'; should have exited 101" | ||||
|     exit 1 | ||||
| fi | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue