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> | </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> | </refsection> | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -266,6 +266,12 @@ public: | ||||||
|     /* Set if at least one derivation had a timeout. */ |     /* Set if at least one derivation had a timeout. */ | ||||||
|     bool timedOut; |     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; |     LocalStore & store; | ||||||
| 
 | 
 | ||||||
|     std::unique_ptr<HookInstance> hook; |     std::unique_ptr<HookInstance> hook; | ||||||
|  | @ -3213,6 +3219,7 @@ void DerivationGoal::registerOutputs() | ||||||
| 
 | 
 | ||||||
|                 /* Throw an error after registering the path as
 |                 /* Throw an error after registering the path as
 | ||||||
|                    valid. */ |                    valid. */ | ||||||
|  |                 worker.hashMismatch = true; | ||||||
|                 delayedException = std::make_exception_ptr( |                 delayedException = std::make_exception_ptr( | ||||||
|                     BuildError("hash mismatch in fixed-output derivation '%s':\n  wanted: %s\n  got:    %s", |                     BuildError("hash mismatch in fixed-output derivation '%s':\n  wanted: %s\n  got:    %s", | ||||||
|                         dest, h.to_string(), h2.to_string())); |                         dest, h.to_string(), h2.to_string())); | ||||||
|  | @ -3255,6 +3262,7 @@ void DerivationGoal::registerOutputs() | ||||||
|             if (!worker.store.isValidPath(path)) continue; |             if (!worker.store.isValidPath(path)) continue; | ||||||
|             auto info = *worker.store.queryPathInfo(path); |             auto info = *worker.store.queryPathInfo(path); | ||||||
|             if (hash.first != info.narHash) { |             if (hash.first != info.narHash) { | ||||||
|  |                 worker.checkMismatch = true; | ||||||
|                 if (settings.runDiffHook || settings.keepFailed) { |                 if (settings.runDiffHook || settings.keepFailed) { | ||||||
|                     Path dst = worker.store.toRealPath(path + checkSuffix); |                     Path dst = worker.store.toRealPath(path + checkSuffix); | ||||||
|                     deletePath(dst); |                     deletePath(dst); | ||||||
|  | @ -3266,10 +3274,10 @@ void DerivationGoal::registerOutputs() | ||||||
|                         buildUser ? buildUser->getGID() : getgid(), |                         buildUser ? buildUser->getGID() : getgid(), | ||||||
|                         path, dst, drvPath, tmpDir); |                         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); |                         % drvPath % path % dst); | ||||||
|                 } else |                 } 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); |                         % drvPath % path); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  | @ -4101,6 +4109,8 @@ Worker::Worker(LocalStore & store) | ||||||
|     lastWokenUp = steady_time_point::min(); |     lastWokenUp = steady_time_point::min(); | ||||||
|     permanentFailure = false; |     permanentFailure = false; | ||||||
|     timedOut = false; |     timedOut = false; | ||||||
|  |     hashMismatch = false; | ||||||
|  |     checkMismatch = false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -4461,7 +4471,29 @@ void Worker::waitForInput() | ||||||
| 
 | 
 | ||||||
| unsigned int Worker::exitStatus() | 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) { |     if (expectedStorePath != "" && storePath != expectedStorePath) { | ||||||
|  |         unsigned int statusCode = 102; | ||||||
|         Hash gotHash = request.unpack |         Hash gotHash = request.unpack | ||||||
|             ? hashPath(request.expectedHash.type, store->toRealPath(storePath)).first |             ? hashPath(request.expectedHash.type, store->toRealPath(storePath)).first | ||||||
|             : hashFile(request.expectedHash.type, store->toRealPath(storePath)); |             : 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()); |             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> { |   fetchurl = import <nix/fetchurl.nix> { | ||||||
|     url = "file://" + toString ./lang/eval-okay-xml.exp.xml; |     url = "file://" + toString ./lang/eval-okay-xml.exp.xml; | ||||||
|     sha256 = "0kg4sla7ihm8ijr8cb3117fhl99zrc2bwy1jrngsfmkh8bav4m0v"; |     sha256 = "0kg4sla7ihm8ijr8cb3117fhl99zrc2bwy1jrngsfmkh8bav4m0v"; | ||||||
|  |  | ||||||
|  | @ -6,14 +6,16 @@ nix-build dependencies.nix --no-out-link | ||||||
| nix-build dependencies.nix --no-out-link --check | 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 | ||||||
| (! 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 | grep 'may not be deterministic' $TEST_ROOT/log | ||||||
|  | [ "$status" = "104" ] | ||||||
| 
 | 
 | ||||||
| clearStore | clearStore | ||||||
| 
 | 
 | ||||||
| nix-build dependencies.nix --no-out-link --repeat 3 | 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 | grep 'differs from previous round' $TEST_ROOT/log | ||||||
| 
 | 
 | ||||||
| path=$(nix-build check.nix -A fetchurl --no-out-link --hashed-mirrors '') | path=$(nix-build check.nix -A fetchurl --no-out-link --hashed-mirrors '') | ||||||
|  | @ -23,10 +25,23 @@ echo foo > $path | ||||||
| chmod -w $path | chmod -w $path | ||||||
| 
 | 
 | ||||||
| nix-build check.nix -A fetchurl --no-out-link --check --hashed-mirrors '' | 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. | # Note: "check" doesn't repair anything, it just compares to the hash stored in the database. | ||||||
| [[ $(cat $path) = foo ]] | [[ $(cat $path) = foo ]] | ||||||
| 
 | 
 | ||||||
| nix-build check.nix -A fetchurl --no-out-link --repair --hashed-mirrors '' | nix-build check.nix -A fetchurl --no-out-link --repair --hashed-mirrors '' | ||||||
| 
 |  | ||||||
| [[ $(cat $path) != foo ]] | [[ $(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 | source common.sh | ||||||
| 
 | 
 | ||||||
| failed=0 | 
 | ||||||
| messages="`nix-build -Q timeout.nix -A infiniteLoop --timeout 2 2>&1 || failed=1`" | set +e | ||||||
| if [ $failed -ne 0 ]; then | messages=$(nix-build -Q timeout.nix -A infiniteLoop --timeout 2 2>&1) | ||||||
|     echo "error: 'nix-store' succeeded; should have timed out" | status=$? | ||||||
|  | set -e | ||||||
|  | 
 | ||||||
|  | if [ $status -ne 101 ]; then | ||||||
|  |     echo "error: 'nix-store' exited with '$status'; should have exited 101" | ||||||
|     exit 1 |     exit 1 | ||||||
| fi | fi | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue