aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/manual/src/language/advanced-attributes.md10
-rw-r--r--doc/manual/src/release-notes/rl-next.md8
-rw-r--r--src/libexpr/eval.cc5
-rw-r--r--src/libexpr/eval.hh5
-rw-r--r--src/libexpr/flake/flakeref.cc6
-rw-r--r--src/libexpr/primops.cc25
-rw-r--r--src/libexpr/tests/derived-path.cc9
-rw-r--r--src/libfetchers/fetchers.cc8
-rw-r--r--src/libfetchers/fetchers.hh6
-rw-r--r--src/libfetchers/git.cc2
-rw-r--r--src/libfetchers/github.cc2
-rw-r--r--src/libfetchers/indirect.cc2
-rw-r--r--src/libfetchers/mercurial.cc2
-rw-r--r--src/libfetchers/path.cc2
-rw-r--r--src/libfetchers/tarball.cc18
-rw-r--r--src/libstore/build/local-derivation-goal.cc1
-rw-r--r--src/libstore/derivations.cc48
-rw-r--r--src/libstore/downstream-placeholder.cc4
-rw-r--r--src/libstore/downstream-placeholder.hh5
-rw-r--r--src/libstore/tests/downstream-placeholder.cc16
-rw-r--r--src/libutil/experimental-features.cc11
-rw-r--r--src/libutil/experimental-features.hh1
-rw-r--r--src/libutil/json-utils.cc24
-rw-r--r--src/libutil/json-utils.hh22
-rw-r--r--src/nix/app.cc11
-rw-r--r--tests/check-refs.sh6
-rw-r--r--tests/fetchTree-file.sh14
27 files changed, 181 insertions, 92 deletions
diff --git a/doc/manual/src/language/advanced-attributes.md b/doc/manual/src/language/advanced-attributes.md
index 307971434..5e8aaeba0 100644
--- a/doc/manual/src/language/advanced-attributes.md
+++ b/doc/manual/src/language/advanced-attributes.md
@@ -320,16 +320,6 @@ Derivations can declare some infrequently used optional attributes.
```
- [`unsafeDiscardReferences`]{#adv-attr-unsafeDiscardReferences}\
- > **Warning**
- > This attribute is part of an [experimental feature](@docroot@/contributing/experimental-features.md).
- >
- > To use this attribute, you must enable the
- > [`discard-references`](@docroot@/contributing/experimental-features.md#xp-feature-discard-references) experimental feature.
- > For example, in [nix.conf](../command-ref/conf-file.md) you could add:
- >
- > ```
- > extra-experimental-features = discard-references
- > ```
When using [structured attributes](#adv-attr-structuredAttrs), the
attribute `unsafeDiscardReferences` is an attribute set with a boolean value for each output name.
diff --git a/doc/manual/src/release-notes/rl-next.md b/doc/manual/src/release-notes/rl-next.md
index df30ce83d..6516a2663 100644
--- a/doc/manual/src/release-notes/rl-next.md
+++ b/doc/manual/src/release-notes/rl-next.md
@@ -8,3 +8,11 @@
These functions are useful for converting between flake references encoded as attribute sets and URLs.
- [`builtins.toJSON`](@docroot@/language/builtins.md#builtins-parseFlakeRef) now prints [--show-trace](@docroot@/command-ref/conf-file.html#conf-show-trace) items for the path in which it finds an evaluation error.
+
+- Error messages regarding malformed input to [`derivation add`](@docroot@/command-ref/new-cli/nix3-derivation-add.md) are now clearer and more detailed.
+
+- The `discard-references` feature has been stabilized.
+ This means that the
+ [unsafeDiscardReferences](@docroot@/contributing/experimental-features.md#xp-feature-discard-references)
+ attribute is no longer guarded by an experimental flag and can be used
+ freely.
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index e57de6c1d..a8e6baea6 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -1031,14 +1031,15 @@ void EvalState::mkOutputString(
Value & value,
const StorePath & drvPath,
const std::string outputName,
- std::optional<StorePath> optOutputPath)
+ std::optional<StorePath> optOutputPath,
+ const ExperimentalFeatureSettings & xpSettings)
{
value.mkString(
optOutputPath
? store->printStorePath(*std::move(optOutputPath))
/* Downstream we would substitute this for an actual path once
we build the floating CA derivation */
- : DownstreamPlaceholder::unknownCaOutput(drvPath, outputName).render(),
+ : DownstreamPlaceholder::unknownCaOutput(drvPath, outputName, xpSettings).render(),
NixStringContext {
NixStringContextElem::Built {
.drvPath = drvPath,
diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh
index 887b9cb97..29d0f05a1 100644
--- a/src/libexpr/eval.hh
+++ b/src/libexpr/eval.hh
@@ -689,12 +689,15 @@ public:
* be passed if and only if output store object is input-addressed.
* Will be printed to form string if passed, otherwise a placeholder
* will be used (see `DownstreamPlaceholder`).
+ *
+ * @param xpSettings Stop-gap to avoid globals during unit tests.
*/
void mkOutputString(
Value & value,
const StorePath & drvPath,
const std::string outputName,
- std::optional<StorePath> optOutputPath);
+ std::optional<StorePath> optOutputPath,
+ const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
void concatLists(Value & v, size_t nrLists, Value * * lists, const PosIdx pos, std::string_view errorCtx);
diff --git a/src/libexpr/flake/flakeref.cc b/src/libexpr/flake/flakeref.cc
index 08adbe0c9..d3fa1d557 100644
--- a/src/libexpr/flake/flakeref.cc
+++ b/src/libexpr/flake/flakeref.cc
@@ -105,7 +105,7 @@ std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
};
return std::make_pair(
- FlakeRef(Input::fromURL(parsedURL), ""),
+ FlakeRef(Input::fromURL(parsedURL, isFlake), ""),
percentDecode(match.str(6)));
}
@@ -176,7 +176,7 @@ std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
parsedURL.query.insert_or_assign("shallow", "1");
return std::make_pair(
- FlakeRef(Input::fromURL(parsedURL), getOr(parsedURL.query, "dir", "")),
+ FlakeRef(Input::fromURL(parsedURL, isFlake), getOr(parsedURL.query, "dir", "")),
fragment);
}
@@ -204,7 +204,7 @@ std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
std::string fragment;
std::swap(fragment, parsedURL.fragment);
- auto input = Input::fromURL(parsedURL);
+ auto input = Input::fromURL(parsedURL, isFlake);
input.parent = baseDir;
return std::make_pair(
diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc
index ddf529b9e..430607214 100644
--- a/src/libexpr/primops.cc
+++ b/src/libexpr/primops.cc
@@ -84,22 +84,21 @@ StringMap EvalState::realiseContext(const NixStringContext & context)
for (auto & d : drvs) buildReqs.emplace_back(DerivedPath { d });
store->buildPaths(buildReqs);
- /* Get all the output paths corresponding to the placeholders we had */
for (auto & drv : drvs) {
auto outputs = resolveDerivedPath(*store, drv);
for (auto & [outputName, outputPath] : outputs) {
- res.insert_or_assign(
- DownstreamPlaceholder::unknownCaOutput(drv.drvPath, outputName).render(),
- store->printStorePath(outputPath)
- );
- }
- }
-
- /* Add the output of this derivations to the allowed
- paths. */
- if (allowedPaths) {
- for (auto & [_placeholder, outputPath] : res) {
- allowPath(store->toRealPath(outputPath));
+ /* Add the output of this derivations to the allowed
+ paths. */
+ if (allowedPaths) {
+ allowPath(outputPath);
+ }
+ /* Get all the output paths corresponding to the placeholders we had */
+ if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) {
+ res.insert_or_assign(
+ DownstreamPlaceholder::unknownCaOutput(drv.drvPath, outputName).render(),
+ store->printStorePath(outputPath)
+ );
+ }
}
}
diff --git a/src/libexpr/tests/derived-path.cc b/src/libexpr/tests/derived-path.cc
index 8210efef2..c713fe28a 100644
--- a/src/libexpr/tests/derived-path.cc
+++ b/src/libexpr/tests/derived-path.cc
@@ -37,8 +37,15 @@ RC_GTEST_FIXTURE_PROP(
prop_built_path_placeholder_round_trip,
(const StorePath & drvPath, const StorePathName & outputName))
{
+ /**
+ * 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;
+ mockXpSettings.set("experimental-features", "ca-derivations");
+
auto * v = state.allocValue();
- state.mkOutputString(*v, drvPath, outputName.name, std::nullopt);
+ state.mkOutputString(*v, drvPath, outputName.name, std::nullopt, mockXpSettings);
auto [d, _] = state.coerceToDerivedPathUnchecked(noPos, *v, "");
DerivedPath::Built b {
.drvPath = drvPath,
diff --git a/src/libfetchers/fetchers.cc b/src/libfetchers/fetchers.cc
index f86c0604e..e683b9f80 100644
--- a/src/libfetchers/fetchers.cc
+++ b/src/libfetchers/fetchers.cc
@@ -13,9 +13,9 @@ void registerInputScheme(std::shared_ptr<InputScheme> && inputScheme)
inputSchemes->push_back(std::move(inputScheme));
}
-Input Input::fromURL(const std::string & url)
+Input Input::fromURL(const std::string & url, bool requireTree)
{
- return fromURL(parseURL(url));
+ return fromURL(parseURL(url), requireTree);
}
static void fixupInput(Input & input)
@@ -31,10 +31,10 @@ static void fixupInput(Input & input)
input.locked = true;
}
-Input Input::fromURL(const ParsedURL & url)
+Input Input::fromURL(const ParsedURL & url, bool requireTree)
{
for (auto & inputScheme : *inputSchemes) {
- auto res = inputScheme->inputFromURL(url);
+ auto res = inputScheme->inputFromURL(url, requireTree);
if (res) {
res->scheme = inputScheme;
fixupInput(*res);
diff --git a/src/libfetchers/fetchers.hh b/src/libfetchers/fetchers.hh
index d0738f619..6e10e9513 100644
--- a/src/libfetchers/fetchers.hh
+++ b/src/libfetchers/fetchers.hh
@@ -44,9 +44,9 @@ struct Input
std::optional<Path> parent;
public:
- static Input fromURL(const std::string & url);
+ static Input fromURL(const std::string & url, bool requireTree = true);
- static Input fromURL(const ParsedURL & url);
+ static Input fromURL(const ParsedURL & url, bool requireTree = true);
static Input fromAttrs(Attrs && attrs);
@@ -129,7 +129,7 @@ struct InputScheme
virtual ~InputScheme()
{ }
- virtual std::optional<Input> inputFromURL(const ParsedURL & url) const = 0;
+ virtual std::optional<Input> inputFromURL(const ParsedURL & url, bool requireTree) const = 0;
virtual std::optional<Input> inputFromAttrs(const Attrs & attrs) const = 0;
diff --git a/src/libfetchers/git.cc b/src/libfetchers/git.cc
index be5842d53..f8d89ab2f 100644
--- a/src/libfetchers/git.cc
+++ b/src/libfetchers/git.cc
@@ -256,7 +256,7 @@ std::pair<StorePath, Input> fetchFromWorkdir(ref<Store> store, Input & input, co
struct GitInputScheme : InputScheme
{
- std::optional<Input> inputFromURL(const ParsedURL & url) const override
+ std::optional<Input> inputFromURL(const ParsedURL & url, bool requireTree) const override
{
if (url.scheme != "git" &&
url.scheme != "git+http" &&
diff --git a/src/libfetchers/github.cc b/src/libfetchers/github.cc
index 80598e7f8..291f457f0 100644
--- a/src/libfetchers/github.cc
+++ b/src/libfetchers/github.cc
@@ -30,7 +30,7 @@ struct GitArchiveInputScheme : InputScheme
virtual std::optional<std::pair<std::string, std::string>> accessHeaderFromToken(const std::string & token) const = 0;
- std::optional<Input> inputFromURL(const ParsedURL & url) const override
+ std::optional<Input> inputFromURL(const ParsedURL & url, bool requireTree) const override
{
if (url.scheme != type()) return {};
diff --git a/src/libfetchers/indirect.cc b/src/libfetchers/indirect.cc
index b99504a16..4874a43ff 100644
--- a/src/libfetchers/indirect.cc
+++ b/src/libfetchers/indirect.cc
@@ -7,7 +7,7 @@ std::regex flakeRegex("[a-zA-Z][a-zA-Z0-9_-]*", std::regex::ECMAScript);
struct IndirectInputScheme : InputScheme
{
- std::optional<Input> inputFromURL(const ParsedURL & url) const override
+ std::optional<Input> inputFromURL(const ParsedURL & url, bool requireTree) const override
{
if (url.scheme != "flake") return {};
diff --git a/src/libfetchers/mercurial.cc b/src/libfetchers/mercurial.cc
index 86e8f81f4..51fd1ed42 100644
--- a/src/libfetchers/mercurial.cc
+++ b/src/libfetchers/mercurial.cc
@@ -43,7 +43,7 @@ static std::string runHg(const Strings & args, const std::optional<std::string>
struct MercurialInputScheme : InputScheme
{
- std::optional<Input> inputFromURL(const ParsedURL & url) const override
+ std::optional<Input> inputFromURL(const ParsedURL & url, bool requireTree) const override
{
if (url.scheme != "hg+http" &&
url.scheme != "hg+https" &&
diff --git a/src/libfetchers/path.cc b/src/libfetchers/path.cc
index 61541e69d..01f1be978 100644
--- a/src/libfetchers/path.cc
+++ b/src/libfetchers/path.cc
@@ -6,7 +6,7 @@ namespace nix::fetchers {
struct PathInputScheme : InputScheme
{
- std::optional<Input> inputFromURL(const ParsedURL & url) const override
+ std::optional<Input> inputFromURL(const ParsedURL & url, bool requireTree) const override
{
if (url.scheme != "path") return {};
diff --git a/src/libfetchers/tarball.cc b/src/libfetchers/tarball.cc
index a012234e0..107d38e92 100644
--- a/src/libfetchers/tarball.cc
+++ b/src/libfetchers/tarball.cc
@@ -194,11 +194,11 @@ struct CurlInputScheme : InputScheme
|| hasSuffix(path, ".tar.zst");
}
- virtual bool isValidURL(const ParsedURL & url) const = 0;
+ virtual bool isValidURL(const ParsedURL & url, bool requireTree) const = 0;
- std::optional<Input> inputFromURL(const ParsedURL & _url) const override
+ std::optional<Input> inputFromURL(const ParsedURL & _url, bool requireTree) const override
{
- if (!isValidURL(_url))
+ if (!isValidURL(_url, requireTree))
return std::nullopt;
Input input;
@@ -265,13 +265,13 @@ struct FileInputScheme : CurlInputScheme
{
const std::string inputType() const override { return "file"; }
- bool isValidURL(const ParsedURL & url) const override
+ bool isValidURL(const ParsedURL & url, bool requireTree) const override
{
auto parsedUrlScheme = parseUrlScheme(url.scheme);
return transportUrlSchemes.count(std::string(parsedUrlScheme.transport))
&& (parsedUrlScheme.application
- ? parsedUrlScheme.application.value() == inputType()
- : !hasTarballExtension(url.path));
+ ? parsedUrlScheme.application.value() == inputType()
+ : (!requireTree && !hasTarballExtension(url.path)));
}
std::pair<StorePath, Input> fetch(ref<Store> store, const Input & input) override
@@ -285,14 +285,14 @@ struct TarballInputScheme : CurlInputScheme
{
const std::string inputType() const override { return "tarball"; }
- bool isValidURL(const ParsedURL & url) const override
+ bool isValidURL(const ParsedURL & url, bool requireTree) const override
{
auto parsedUrlScheme = parseUrlScheme(url.scheme);
return transportUrlSchemes.count(std::string(parsedUrlScheme.transport))
&& (parsedUrlScheme.application
- ? parsedUrlScheme.application.value() == inputType()
- : hasTarballExtension(url.path));
+ ? parsedUrlScheme.application.value() == inputType()
+ : (requireTree || hasTarballExtension(url.path)));
}
std::pair<StorePath, Input> fetch(ref<Store> store, const Input & _input) override
diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc
index b7a27490c..8b7fbdcda 100644
--- a/src/libstore/build/local-derivation-goal.cc
+++ b/src/libstore/build/local-derivation-goal.cc
@@ -2307,7 +2307,6 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs()
bool discardReferences = false;
if (auto structuredAttrs = parsedDrv->getStructuredAttrs()) {
if (auto udr = get(*structuredAttrs, "unsafeDiscardReferences")) {
- experimentalFeatureSettings.require(Xp::DiscardReferences);
if (auto output = get(*udr, outputName)) {
if (!output->is_boolean())
throw Error("attribute 'unsafeDiscardReferences.\"%s\"' of derivation '%s' must be a Boolean", outputName, drvPath.to_string());
diff --git a/src/libstore/derivations.cc b/src/libstore/derivations.cc
index b831b2fe5..f4e4980c2 100644
--- a/src/libstore/derivations.cc
+++ b/src/libstore/derivations.cc
@@ -879,9 +879,11 @@ std::optional<BasicDerivation> Derivation::tryResolve(
for (auto & [inputDrv, inputOutputs] : inputDrvs) {
for (auto & outputName : inputOutputs) {
if (auto actualPath = get(inputDrvOutputs, { inputDrv, outputName })) {
- inputRewrites.emplace(
- DownstreamPlaceholder::unknownCaOutput(inputDrv, outputName).render(),
- store.printStorePath(*actualPath));
+ if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) {
+ inputRewrites.emplace(
+ DownstreamPlaceholder::unknownCaOutput(inputDrv, outputName).render(),
+ store.printStorePath(*actualPath));
+ }
resolved.inputSrcs.insert(*actualPath);
} else {
warn("output '%s' of input '%s' missing, aborting the resolving",
@@ -993,6 +995,7 @@ DerivationOutput DerivationOutput::fromJSON(
const ExperimentalFeatureSettings & xpSettings)
{
std::set<std::string_view> keys;
+ ensureType(_json, nlohmann::detail::value_t::object);
auto json = (std::map<std::string, nlohmann::json>) _json;
for (const auto & [key, _] : json)
@@ -1097,36 +1100,51 @@ Derivation Derivation::fromJSON(
const Store & store,
const nlohmann::json & json)
{
+ using nlohmann::detail::value_t;
+
Derivation res;
- res.name = json["name"];
+ ensureType(json, value_t::object);
- {
- auto & outputsObj = json["outputs"];
+ res.name = ensureType(valueAt(json, "name"), value_t::string);
+
+ try {
+ auto & outputsObj = ensureType(valueAt(json, "outputs"), value_t::object);
for (auto & [outputName, output] : outputsObj.items()) {
res.outputs.insert_or_assign(
outputName,
DerivationOutput::fromJSON(store, res.name, outputName, output));
}
+ } catch (Error & e) {
+ e.addTrace({}, "while reading key 'outputs'");
+ throw;
}
- {
- auto & inputsList = json["inputSrcs"];
+ try {
+ auto & inputsList = ensureType(valueAt(json, "inputSrcs"), value_t::array);
for (auto & input : inputsList)
res.inputSrcs.insert(store.parseStorePath(static_cast<const std::string &>(input)));
+ } catch (Error & e) {
+ e.addTrace({}, "while reading key 'inputSrcs'");
+ throw;
}
- {
- auto & inputDrvsObj = json["inputDrvs"];
- for (auto & [inputDrvPath, inputOutputs] : inputDrvsObj.items())
+ try {
+ auto & inputDrvsObj = ensureType(valueAt(json, "inputDrvs"), value_t::object);
+ for (auto & [inputDrvPath, inputOutputs] : inputDrvsObj.items()) {
+ ensureType(inputOutputs, value_t::array);
res.inputDrvs[store.parseStorePath(inputDrvPath)] =
static_cast<const StringSet &>(inputOutputs);
+ }
+ } catch (Error & e) {
+ e.addTrace({}, "while reading key 'inputDrvs'");
+ throw;
}
- res.platform = json["system"];
- res.builder = json["builder"];
- res.args = json["args"];
- res.env = json["env"];
+ res.platform = ensureType(valueAt(json, "system"), value_t::string);
+ res.builder = ensureType(valueAt(json, "builder"), value_t::string);
+ res.args = ensureType(valueAt(json, "args"), value_t::array);
+ res.env = ensureType(valueAt(json, "env"), value_t::object);
return res;
}
diff --git a/src/libstore/downstream-placeholder.cc b/src/libstore/downstream-placeholder.cc
index 1752738f2..d623c05e2 100644
--- a/src/libstore/downstream-placeholder.cc
+++ b/src/libstore/downstream-placeholder.cc
@@ -11,8 +11,10 @@ std::string DownstreamPlaceholder::render() const
DownstreamPlaceholder DownstreamPlaceholder::unknownCaOutput(
const StorePath & drvPath,
- std::string_view outputName)
+ std::string_view outputName,
+ const ExperimentalFeatureSettings & xpSettings)
{
+ xpSettings.require(Xp::CaDerivations);
auto drvNameWithExtension = drvPath.name();
auto drvName = drvNameWithExtension.substr(0, drvNameWithExtension.size() - 4);
auto clearText = "nix-upstream-output:" + std::string { drvPath.hashPart() } + ":" + outputPathName(drvName, outputName);
diff --git a/src/libstore/downstream-placeholder.hh b/src/libstore/downstream-placeholder.hh
index f0c0dee77..97f77e6b8 100644
--- a/src/libstore/downstream-placeholder.hh
+++ b/src/libstore/downstream-placeholder.hh
@@ -52,10 +52,13 @@ public:
*
* The derivation itself is known (we have a store path for it), but
* the output doesn't yet have a known store path.
+ *
+ * @param xpSettings Stop-gap to avoid globals during unit tests.
*/
static DownstreamPlaceholder unknownCaOutput(
const StorePath & drvPath,
- std::string_view outputName);
+ std::string_view outputName,
+ const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
/**
* Create a placehold for the output of an unknown derivation.
diff --git a/src/libstore/tests/downstream-placeholder.cc b/src/libstore/tests/downstream-placeholder.cc
index ec3e1000f..fd29530ac 100644
--- a/src/libstore/tests/downstream-placeholder.cc
+++ b/src/libstore/tests/downstream-placeholder.cc
@@ -5,17 +5,24 @@
namespace nix {
TEST(DownstreamPlaceholder, unknownCaOutput) {
+ /**
+ * 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;
+ mockXpSettings.set("experimental-features", "ca-derivations");
+
ASSERT_EQ(
DownstreamPlaceholder::unknownCaOutput(
StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv" },
- "out").render(),
+ "out",
+ mockXpSettings).render(),
"/0c6rn30q4frawknapgwq386zq358m8r6msvywcvc89n6m5p2dgbz");
}
TEST(DownstreamPlaceholder, unknownDerivation) {
/**
- * 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.
+ * Same reason as above
*/
ExperimentalFeatureSettings mockXpSettings;
mockXpSettings.set("experimental-features", "dynamic-derivations ca-derivations");
@@ -24,7 +31,8 @@ TEST(DownstreamPlaceholder, unknownDerivation) {
DownstreamPlaceholder::unknownDerivation(
DownstreamPlaceholder::unknownCaOutput(
StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv.drv" },
- "out"),
+ "out",
+ mockXpSettings),
"out",
mockXpSettings).render(),
"/0gn6agqxjyyalf0dpihgyf49xq5hqxgw100f0wydnj6yqrhqsb3w");
diff --git a/src/libutil/experimental-features.cc b/src/libutil/experimental-features.cc
index 7c4112d32..782331283 100644
--- a/src/libutil/experimental-features.cc
+++ b/src/libutil/experimental-features.cc
@@ -12,7 +12,7 @@ struct ExperimentalFeatureDetails
std::string_view description;
};
-constexpr std::array<ExperimentalFeatureDetails, 15> xpFeatureDetails = {{
+constexpr std::array<ExperimentalFeatureDetails, 14> xpFeatureDetails = {{
{
.tag = Xp::CaDerivations,
.name = "ca-derivations",
@@ -183,15 +183,6 @@ constexpr std::array<ExperimentalFeatureDetails, 15> xpFeatureDetails = {{
)",
},
{
- .tag = Xp::DiscardReferences,
- .name = "discard-references",
- .description = R"(
- Allow the use of the [`unsafeDiscardReferences`](@docroot@/language/advanced-attributes.html#adv-attr-unsafeDiscardReferences) attribute in derivations
- that use [structured attributes](@docroot@/language/advanced-attributes.html#adv-attr-structuredAttrs). This disables scanning of outputs for
- runtime dependencies.
- )",
- },
- {
.tag = Xp::DaemonTrustOverride,
.name = "daemon-trust-override",
.description = R"(
diff --git a/src/libutil/experimental-features.hh b/src/libutil/experimental-features.hh
index faf2e9398..add592ae6 100644
--- a/src/libutil/experimental-features.hh
+++ b/src/libutil/experimental-features.hh
@@ -27,7 +27,6 @@ enum struct ExperimentalFeature
ReplFlake,
AutoAllocateUids,
Cgroups,
- DiscardReferences,
DaemonTrustOverride,
DynamicDerivations,
ParseTomlTimestamps,
diff --git a/src/libutil/json-utils.cc b/src/libutil/json-utils.cc
index d7220e71d..61cef743d 100644
--- a/src/libutil/json-utils.cc
+++ b/src/libutil/json-utils.cc
@@ -1,4 +1,5 @@
#include "json-utils.hh"
+#include "error.hh"
namespace nix {
@@ -16,4 +17,27 @@ nlohmann::json * get(nlohmann::json & map, const std::string & key)
return &*i;
}
+const nlohmann::json & valueAt(
+ const nlohmann::json & map,
+ const std::string & key)
+{
+ if (!map.contains(key))
+ throw Error("Expected JSON object to contain key '%s' but it doesn't", key);
+
+ return map[key];
+}
+
+const nlohmann::json & ensureType(
+ const nlohmann::json & value,
+ nlohmann::json::value_type expectedType
+ )
+{
+ if (value.type() != expectedType)
+ throw Error(
+ "Expected JSON value to be of type '%s' but it is of type '%s'",
+ nlohmann::json(expectedType).type_name(),
+ value.type_name());
+
+ return value;
+}
}
diff --git a/src/libutil/json-utils.hh b/src/libutil/json-utils.hh
index 5e63c1af4..77c63595c 100644
--- a/src/libutil/json-utils.hh
+++ b/src/libutil/json-utils.hh
@@ -11,6 +11,28 @@ const nlohmann::json * get(const nlohmann::json & map, const std::string & key);
nlohmann::json * get(nlohmann::json & map, const std::string & key);
/**
+ * Get the value of a json object at a key safely, failing
+ * with a Nix Error if the key does not exist.
+ *
+ * Use instead of nlohmann::json::at() to avoid ugly exceptions.
+ *
+ * _Does not check whether `map` is an object_, use `ensureType` for that.
+ */
+const nlohmann::json & valueAt(
+ const nlohmann::json & map,
+ const std::string & key);
+
+/**
+ * Ensure the type of a json object is what you expect, failing
+ * with a Nix Error if it isn't.
+ *
+ * Use before type conversions and element access to avoid ugly exceptions.
+ */
+const nlohmann::json & ensureType(
+ const nlohmann::json & value,
+ nlohmann::json::value_type expectedType);
+
+/**
* For `adl_serializer<std::optional<T>>` below, we need to track what
* types are not already using `null`. Only for them can we use `null`
* to represent `std::nullopt`.
diff --git a/src/nix/app.cc b/src/nix/app.cc
index e678b54f0..e0f68b4fc 100644
--- a/src/nix/app.cc
+++ b/src/nix/app.cc
@@ -22,11 +22,12 @@ StringPairs resolveRewrites(
StringPairs res;
for (auto & dep : dependencies)
if (auto drvDep = std::get_if<BuiltPathBuilt>(&dep.path))
- for (auto & [ outputName, outputPath ] : drvDep->outputs)
- res.emplace(
- DownstreamPlaceholder::unknownCaOutput(drvDep->drvPath, outputName).render(),
- store.printStorePath(outputPath)
- );
+ if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations))
+ for (auto & [ outputName, outputPath ] : drvDep->outputs)
+ res.emplace(
+ DownstreamPlaceholder::unknownCaOutput(drvDep->drvPath, outputName).render(),
+ store.printStorePath(outputPath)
+ );
return res;
}
diff --git a/tests/check-refs.sh b/tests/check-refs.sh
index 2778e491d..3b587d1e5 100644
--- a/tests/check-refs.sh
+++ b/tests/check-refs.sh
@@ -42,8 +42,10 @@ nix-build -o $RESULT check-refs.nix -A test7
nix-build -o $RESULT check-refs.nix -A test10
if isDaemonNewer 2.12pre20230103; then
- enableFeatures discard-references
- restartDaemon
+ if ! isDaemonNewer 2.16.0; then
+ enableFeatures discard-references
+ restartDaemon
+ fi
# test11 should succeed.
test11=$(nix-build -o $RESULT check-refs.nix -A test11)
diff --git a/tests/fetchTree-file.sh b/tests/fetchTree-file.sh
index fe569cfb8..6395c133d 100644
--- a/tests/fetchTree-file.sh
+++ b/tests/fetchTree-file.sh
@@ -27,6 +27,7 @@ test_file_flake_input () {
mkdir inputs
echo foo > inputs/test_input_file
+ echo '{ outputs = { self }: { }; }' > inputs/flake.nix
tar cfa test_input.tar.gz inputs
cp test_input.tar.gz test_input_no_ext
input_tarball_hash="$(nix hash path test_input.tar.gz)"
@@ -50,6 +51,9 @@ test_file_flake_input () {
url = "file+file://$PWD/test_input.tar.gz";
flake = false;
};
+ inputs.flake_no_ext = {
+ url = "file://$PWD/test_input_no_ext";
+ };
outputs = { ... }: {};
}
EOF
@@ -58,7 +62,7 @@ EOF
nix eval --file - <<EOF
with (builtins.fromJSON (builtins.readFile ./flake.lock));
- # Url inputs whose extension doesn’t match a known archive format should
+ # Non-flake inputs whose extension doesn’t match a known archive format should
# not be unpacked by default
assert (nodes.no_ext_default_no_unpack.locked.type == "file");
assert (nodes.no_ext_default_no_unpack.locked.unpack or false == false);
@@ -75,8 +79,16 @@ EOF
# Explicitely passing the unpack parameter should enforce the desired behavior
assert (nodes.no_ext_explicit_unpack.locked.narHash == nodes.tarball_default_unpack.locked.narHash);
assert (nodes.tarball_explicit_no_unpack.locked.narHash == nodes.no_ext_default_no_unpack.locked.narHash);
+
+ # Flake inputs should always be tarballs
+ assert (nodes.flake_no_ext.locked.type == "tarball");
+
true
EOF
+
+ # Test tarball URLs on the command line.
+ [[ $(nix flake metadata --json file://$PWD/test_input_no_ext | jq -r .resolved.type) = tarball ]]
+
popd
[[ -z "${NIX_DAEMON_PACKAGE-}" ]] && return 0