Merge remote-tracking branch 'upstream/master' into ca-drv-exotic

This commit is contained in:
John Ericson 2023-04-19 11:26:34 -04:00
commit 76baaeb341
10 changed files with 87 additions and 29 deletions

View file

@ -127,7 +127,7 @@
builder can rely on external inputs such as the network or the builder can rely on external inputs such as the network or the
system time) but the Nix model assumes it. system time) but the Nix model assumes it.
- Nix database{#gloss-nix-database}\ - [Nix database]{#gloss-nix-database}\
An SQlite database to track [reference]s between [store object]s. An SQlite database to track [reference]s between [store object]s.
This is an implementation detail of the [local store]. This is an implementation detail of the [local store].

View file

@ -136,7 +136,7 @@ which you may remove.
### macOS ### macOS
1. Edit `/etc/zshrc` and `/etc/bashrc` to remove the lines sourcing 1. Edit `/etc/zshrc`, `/etc/bashrc`, and `/etc/bash.bashrc` to remove the lines sourcing
`nix-daemon.sh`, which should look like this: `nix-daemon.sh`, which should look like this:
```bash ```bash
@ -153,6 +153,7 @@ which you may remove.
```console ```console
sudo mv /etc/zshrc.backup-before-nix /etc/zshrc sudo mv /etc/zshrc.backup-before-nix /etc/zshrc
sudo mv /etc/bashrc.backup-before-nix /etc/bashrc sudo mv /etc/bashrc.backup-before-nix /etc/bashrc
sudo mv /etc/bash.bashrc.backup-before-nix /etc/bash.bashrc
``` ```
This will stop shells from sourcing the file and bringing everything you This will stop shells from sourcing the file and bringing everything you

View file

@ -31,7 +31,7 @@ static void writeTrustedList(const TrustedList & trustedList)
void ConfigFile::apply() void ConfigFile::apply()
{ {
std::set<std::string> whitelist{"bash-prompt", "bash-prompt-prefix", "bash-prompt-suffix", "flake-registry"}; std::set<std::string> whitelist{"bash-prompt", "bash-prompt-prefix", "bash-prompt-suffix", "flake-registry", "commit-lockfile-summary"};
for (auto & [name, value] : settings) { for (auto & [name, value] : settings) {

View file

@ -991,7 +991,8 @@ nlohmann::json DerivationOutput::toJSON(
DerivationOutput DerivationOutput::fromJSON( DerivationOutput DerivationOutput::fromJSON(
const Store & store, std::string_view drvName, std::string_view outputName, const Store & store, std::string_view drvName, std::string_view outputName,
const nlohmann::json & _json) const nlohmann::json & _json,
const ExperimentalFeatureSettings & xpSettings)
{ {
std::set<std::string_view> keys; std::set<std::string_view> keys;
auto json = (std::map<std::string, nlohmann::json>) _json; auto json = (std::map<std::string, nlohmann::json>) _json;
@ -1028,6 +1029,7 @@ DerivationOutput DerivationOutput::fromJSON(
} }
else if (keys == (std::set<std::string_view> { "hashAlgo" })) { else if (keys == (std::set<std::string_view> { "hashAlgo" })) {
xpSettings.require(Xp::CaDerivations);
auto [method, hashType] = methodAlgo(); auto [method, hashType] = methodAlgo();
return DerivationOutput::CAFloating { return DerivationOutput::CAFloating {
.method = std::move(method), .method = std::move(method),
@ -1040,6 +1042,7 @@ DerivationOutput DerivationOutput::fromJSON(
} }
else if (keys == (std::set<std::string_view> { "hashAlgo", "impure" })) { else if (keys == (std::set<std::string_view> { "hashAlgo", "impure" })) {
xpSettings.require(Xp::ImpureDerivations);
auto [method, hashType] = methodAlgo(); auto [method, hashType] = methodAlgo();
return DerivationOutput::Impure { return DerivationOutput::Impure {
.method = std::move(method), .method = std::move(method),

View file

@ -136,11 +136,15 @@ struct DerivationOutput : _DerivationOutputRaw
const Store & store, const Store & store,
std::string_view drvName, std::string_view drvName,
std::string_view outputName) const; std::string_view outputName) const;
/**
* @param xpSettings Stop-gap to avoid globals during unit tests.
*/
static DerivationOutput fromJSON( static DerivationOutput fromJSON(
const Store & store, const Store & store,
std::string_view drvName, std::string_view drvName,
std::string_view outputName, std::string_view outputName,
const nlohmann::json & json); const nlohmann::json & json,
const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
}; };
typedef std::map<std::string, DerivationOutput> DerivationOutputs; typedef std::map<std::string, DerivationOutput> DerivationOutputs;

View file

@ -7,15 +7,18 @@ namespace nix {
NarInfo::NarInfo(const Store & store, const std::string & s, const std::string & whence) NarInfo::NarInfo(const Store & store, const std::string & s, const std::string & whence)
: ValidPathInfo(StorePath(StorePath::dummy), Hash(Hash::dummy)) // FIXME: hack : ValidPathInfo(StorePath(StorePath::dummy), Hash(Hash::dummy)) // FIXME: hack
{ {
auto corrupt = [&]() { unsigned line = 1;
return Error("NAR info file '%1%' is corrupt", whence);
auto corrupt = [&](const char * reason) {
return Error("NAR info file '%1%' is corrupt: %2%", whence,
std::string(reason) + (line > 0 ? " at line " + std::to_string(line) : ""));
}; };
auto parseHashField = [&](const std::string & s) { auto parseHashField = [&](const std::string & s) {
try { try {
return Hash::parseAnyPrefixed(s); return Hash::parseAnyPrefixed(s);
} catch (BadHash &) { } catch (BadHash &) {
throw corrupt(); throw corrupt("bad hash");
} }
}; };
@ -26,12 +29,12 @@ NarInfo::NarInfo(const Store & store, const std::string & s, const std::string &
while (pos < s.size()) { while (pos < s.size()) {
size_t colon = s.find(':', pos); size_t colon = s.find(':', pos);
if (colon == std::string::npos) throw corrupt(); if (colon == std::string::npos) throw corrupt("expecting ':'");
std::string name(s, pos, colon - pos); std::string name(s, pos, colon - pos);
size_t eol = s.find('\n', colon + 2); size_t eol = s.find('\n', colon + 2);
if (eol == std::string::npos) throw corrupt(); if (eol == std::string::npos) throw corrupt("expecting '\\n'");
std::string value(s, colon + 2, eol - colon - 2); std::string value(s, colon + 2, eol - colon - 2);
@ -47,7 +50,7 @@ NarInfo::NarInfo(const Store & store, const std::string & s, const std::string &
fileHash = parseHashField(value); fileHash = parseHashField(value);
else if (name == "FileSize") { else if (name == "FileSize") {
auto n = string2Int<decltype(fileSize)>(value); auto n = string2Int<decltype(fileSize)>(value);
if (!n) throw corrupt(); if (!n) throw corrupt("invalid FileSize");
fileSize = *n; fileSize = *n;
} }
else if (name == "NarHash") { else if (name == "NarHash") {
@ -56,12 +59,12 @@ NarInfo::NarInfo(const Store & store, const std::string & s, const std::string &
} }
else if (name == "NarSize") { else if (name == "NarSize") {
auto n = string2Int<decltype(narSize)>(value); auto n = string2Int<decltype(narSize)>(value);
if (!n) throw corrupt(); if (!n) throw corrupt("invalid NarSize");
narSize = *n; narSize = *n;
} }
else if (name == "References") { else if (name == "References") {
auto refs = tokenizeString<Strings>(value, " "); auto refs = tokenizeString<Strings>(value, " ");
if (!references.empty()) throw corrupt(); if (!references.empty()) throw corrupt("extra References");
for (auto & r : refs) for (auto & r : refs)
references.insert(StorePath(r)); references.insert(StorePath(r));
} }
@ -72,17 +75,26 @@ NarInfo::NarInfo(const Store & store, const std::string & s, const std::string &
else if (name == "Sig") else if (name == "Sig")
sigs.insert(value); sigs.insert(value);
else if (name == "CA") { else if (name == "CA") {
if (ca) throw corrupt(); if (ca) throw corrupt("extra CA");
// FIXME: allow blank ca or require skipping field? // FIXME: allow blank ca or require skipping field?
ca = ContentAddress::parseOpt(value); ca = ContentAddress::parseOpt(value);
} }
pos = eol + 1; pos = eol + 1;
line += 1;
} }
if (compression == "") compression = "bzip2"; if (compression == "") compression = "bzip2";
if (!havePath || !haveNarHash || url.empty() || narSize == 0) throw corrupt(); if (!havePath || !haveNarHash || url.empty() || narSize == 0) {
line = 0; // don't include line information in the error
throw corrupt(
!havePath ? "StorePath missing" :
!haveNarHash ? "NarHash missing" :
url.empty() ? "URL missing" :
narSize == 0 ? "NarSize missing or zero"
: "?");
}
} }
std::string NarInfo::to_string(const Store & store) const std::string NarInfo::to_string(const Store & store) const

View file

@ -1,6 +1,7 @@
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include "experimental-features.hh"
#include "derivations.hh" #include "derivations.hh"
#include "tests/libstore.hh" #include "tests/libstore.hh"
@ -9,10 +10,32 @@ namespace nix {
class DerivationTest : public LibStoreTest class DerivationTest : public LibStoreTest
{ {
public:
/**
* We set these in tests rather than the regular globals so we don't have
* to worry about race conditions if the tests run concurrently.
*/
ExperimentalFeatureSettings mockXpSettings;
}; };
#define TEST_JSON(NAME, STR, VAL, DRV_NAME, OUTPUT_NAME) \ class CaDerivationTest : public DerivationTest
TEST_F(DerivationTest, DerivationOutput_ ## NAME ## _to_json) { \ {
void SetUp() override
{
mockXpSettings.set("experimental-features", "ca-derivations");
}
};
class ImpureDerivationTest : public DerivationTest
{
void SetUp() override
{
mockXpSettings.set("experimental-features", "impure-derivations");
}
};
#define TEST_JSON(FIXTURE, NAME, STR, VAL, DRV_NAME, OUTPUT_NAME) \
TEST_F(FIXTURE, DerivationOutput_ ## NAME ## _to_json) { \
using nlohmann::literals::operator "" _json; \ using nlohmann::literals::operator "" _json; \
ASSERT_EQ( \ ASSERT_EQ( \
STR ## _json, \ STR ## _json, \
@ -22,7 +45,7 @@ class DerivationTest : public LibStoreTest
OUTPUT_NAME)); \ OUTPUT_NAME)); \
} \ } \
\ \
TEST_F(DerivationTest, DerivationOutput_ ## NAME ## _from_json) { \ TEST_F(FIXTURE, DerivationOutput_ ## NAME ## _from_json) { \
using nlohmann::literals::operator "" _json; \ using nlohmann::literals::operator "" _json; \
ASSERT_EQ( \ ASSERT_EQ( \
DerivationOutput { VAL }, \ DerivationOutput { VAL }, \
@ -30,10 +53,11 @@ class DerivationTest : public LibStoreTest
*store, \ *store, \
DRV_NAME, \ DRV_NAME, \
OUTPUT_NAME, \ OUTPUT_NAME, \
STR ## _json)); \ STR ## _json, \
mockXpSettings)); \
} }
TEST_JSON(inputAddressed, TEST_JSON(DerivationTest, inputAddressed,
R"({ R"({
"path": "/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-drv-name-output-name" "path": "/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-drv-name-output-name"
})", })",
@ -42,7 +66,7 @@ TEST_JSON(inputAddressed,
}), }),
"drv-name", "output-name") "drv-name", "output-name")
TEST_JSON(caFixed, TEST_JSON(DerivationTest, caFixed,
R"({ R"({
"hashAlgo": "r:sha256", "hashAlgo": "r:sha256",
"hash": "894517c9163c896ec31a2adbd33c0681fd5f45b2c0ef08a64c92a03fb97f390f", "hash": "894517c9163c896ec31a2adbd33c0681fd5f45b2c0ef08a64c92a03fb97f390f",
@ -59,7 +83,7 @@ TEST_JSON(caFixed,
}), }),
"drv-name", "output-name") "drv-name", "output-name")
TEST_JSON(caFixedText, TEST_JSON(CaDerivationTest, caFixedText,
R"({ R"({
"hashAlgo": "text:sha256", "hashAlgo": "text:sha256",
"hash": "894517c9163c896ec31a2adbd33c0681fd5f45b2c0ef08a64c92a03fb97f390f", "hash": "894517c9163c896ec31a2adbd33c0681fd5f45b2c0ef08a64c92a03fb97f390f",
@ -75,7 +99,7 @@ TEST_JSON(caFixedText,
}), }),
"drv-name", "output-name") "drv-name", "output-name")
TEST_JSON(caFloating, TEST_JSON(CaDerivationTest, caFloating,
R"({ R"({
"hashAlgo": "r:sha256" "hashAlgo": "r:sha256"
})", })",
@ -85,12 +109,12 @@ TEST_JSON(caFloating,
}), }),
"drv-name", "output-name") "drv-name", "output-name")
TEST_JSON(deferred, TEST_JSON(DerivationTest, deferred,
R"({ })", R"({ })",
DerivationOutput::Deferred { }, DerivationOutput::Deferred { },
"drv-name", "output-name") "drv-name", "output-name")
TEST_JSON(impure, TEST_JSON(ImpureDerivationTest, impure,
R"({ R"({
"hashAlgo": "r:sha256", "hashAlgo": "r:sha256",
"impure": true "impure": true

View file

@ -381,10 +381,12 @@ The following attributes are supported in `flake.nix`:
* `nixConfig`: a set of `nix.conf` options to be set when evaluating any * `nixConfig`: a set of `nix.conf` options to be set when evaluating any
part of a flake. In the interests of security, only a small set of part of a flake. In the interests of security, only a small set of
whitelisted options (currently `bash-prompt`, `bash-prompt-prefix`, set of options is allowed to be set without confirmation so long as [`accept-flake-config`](@docroot@/command-ref/conf-file.md#conf-accept-flake-config) is not enabled in the global configuration:
`bash-prompt-suffix`, and `flake-registry`) are allowed to be set without - [`bash-prompt`](@docroot@/command-ref/conf-file.md#conf-bash-prompt)
confirmation so long as `accept-flake-config` is not set in the global - [`bash-prompt-prefix`](@docroot@/command-ref/conf-file.md#conf-bash-prompt-prefix)
configuration. - [`bash-prompt-suffix`](@docroot@/command-ref/conf-file.md#conf-bash-prompt-suffix)
- [`flake-registry`](@docroot@/command-ref/conf-file.md#conf-flake-registry)
- [`commit-lockfile-summary`](@docroot@/command-ref/conf-file.md#conf-commit-lockfile-summary)
## Flake inputs ## Flake inputs

View file

@ -16,6 +16,9 @@ drvPath3=$(nix derivation add --dry-run < $TEST_HOME/foo.json)
# With --dry-run nothing is actually written # With --dry-run nothing is actually written
[[ ! -e "$drvPath3" ]] [[ ! -e "$drvPath3" ]]
# But the JSON is rejected without the experimental feature
expectStderr 1 nix derivation add < $TEST_HOME/foo.json --experimental-features nix-command | grepQuiet "experimental Nix feature 'ca-derivations' is disabled"
# Without --dry-run it is actually written # Without --dry-run it is actually written
drvPath4=$(nix derivation add < $TEST_HOME/foo.json) drvPath4=$(nix derivation add < $TEST_HOME/foo.json)
[[ "$drvPath4" = "$drvPath3" ]] [[ "$drvPath4" = "$drvPath3" ]]

View file

@ -10,6 +10,15 @@ clearStore
# Basic test of impure derivations: building one a second time should not use the previous result. # Basic test of impure derivations: building one a second time should not use the previous result.
printf 0 > $TEST_ROOT/counter printf 0 > $TEST_ROOT/counter
# `nix derivation add` with impure derivations work
drvPath=$(nix-instantiate ./impure-derivations.nix -A impure)
nix derivation show $drvPath | jq .[] > $TEST_HOME/impure-drv.json
drvPath2=$(nix derivation add < $TEST_HOME/impure-drv.json)
[[ "$drvPath" = "$drvPath2" ]]
# But only with the experimental feature!
expectStderr 1 nix derivation add < $TEST_HOME/impure-drv.json --experimental-features nix-command | grepQuiet "experimental Nix feature 'impure-derivations' is disabled"
nix build --dry-run --json --file ./impure-derivations.nix impure.all nix build --dry-run --json --file ./impure-derivations.nix impure.all
json=$(nix build -L --no-link --json --file ./impure-derivations.nix impure.all) json=$(nix build -L --no-link --json --file ./impure-derivations.nix impure.all)
path1=$(echo $json | jq -r .[].outputs.out) path1=$(echo $json | jq -r .[].outputs.out)