Merge pull request #2779 from LnL7/build-exit-codes

build: add exit code for hash and check mismatches
This commit is contained in:
Eelco Dolstra 2019-07-02 17:37:49 +02:00 committed by GitHub
commit 7e1c85c5fb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 111 additions and 12 deletions

View file

@ -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>

View file

@ -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;
} }

View file

@ -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());
} }

View file

@ -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";

View file

@ -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" ]

View file

@ -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