libexpr/eval: Deprecate shadowing <nix/fetchurl.nix>

If the NIX_PATH shadows internal files, this will often break things,
hence we forbid it.

Fixes #998

Change-Id: I70e5d389532ada1c9f910c60281abe565e3ce6bb
This commit is contained in:
Tom Hubrecht
2025-11-30 12:28:54 +00:00
parent 3981458d8c
commit 0c6d299e16
12 changed files with 125 additions and 1 deletions
@@ -0,0 +1,13 @@
---
synopsis: "Deprecate shadowing internal files through the Nix search path"
issues: [998]
cls: [4632]
category: "Breaking Changes"
credits: [thubrecht]
---
As Lix uses the path `<nix/fetchurl.nix>` for bootstrapping purposes, the ability to shadow it by adding `nix=/some/path` (or `/other/path` that contains a `nix` directory) to the search path is not desirable.
To alleviate potential issues, Lix now emits a warning when the Nix search path contains potential shadows for internal files, which will be changed to an error in a future release.
The warning can be disabled by enabling the deprecated feature `nix-path-shadow`.
+57
View File
@@ -4,12 +4,14 @@
#include "lix/libutil/archive.hh"
#include "lix/libutil/ansicolor.hh"
#include "lix/libutil/async.hh"
#include "lix/libutil/deprecated-features.hh"
#include "lix/libutil/error.hh"
#include "lix/libutil/english.hh"
#include "lix/libutil/fmt.hh"
#include "lix/libexpr/primops.hh"
#include "lix/libexpr/print-options.hh"
#include "lix/libmain/shared.hh"
#include "lix/libutil/logging.hh"
#include "lix/libutil/suggestions.hh"
#include "lix/libutil/types.hh"
#include "lix/libstore/store-api.hh"
@@ -347,6 +349,61 @@ EvalPaths::EvalPaths(
allowPath(path);
}
}
#if LIX_MAJOR >= 3 || (LIX_MAJOR == 2 && LIX_MINOR >= 96)
#warning \
"The feature nix-path-shadow was deprecated in 2.95 with a warning, which needs to be turned into an error in 2.96"
#endif
if (!featureSettings.isEnabled(DeprecatedFeature::NixPathShadow)) {
for (auto & [prefix, path] : searchPath_.elements) {
// Match on the 'nix' prefix
if (prefix.s == "nix") {
logWarning(
{.msg = HintFmt(
"The prefix '%s' is reserved for internal use by Lix in the Nix search "
"path, its usage is deprecated and will be forbidden in the future.\n"
"Use %s to silence this warning.\n"
"This is due to adding '%s=%s' in the Nix search path, either through the "
"environment variable '%s' or by passing the flag %s to the nix "
"invocation.",
"nix",
"--extra-deprecated-features nix-path-shadow",
prefix.s,
path.s,
"NIX_PATH",
"-I"
)}
);
} else
// Match prefixless paths that contain a `nix` directory
if (auto res =
prefix.suffixIfPotentialMatch("nix").and_then([&](std::string_view s) {
return aio.blockOn(resolveSearchPathPath(path))
.and_then([&](std::string r) {
Path res = s.length() ? concatStrings(r, "/", s): r;
return pathExists(res) ? std::optional(res) : std::nullopt;
});
}))
{
logWarning(
{.msg = HintFmt(
"Shadowing '%s' by configuring the nix-path is deprecated and "
"will be forbidden in the future.\n"
"Use %s to silence this warning.\n"
"This is due to adding '%s' to the nix-path without a prefix, "
"either by passing the flag '-I %s' to the nix invocation or by "
"adding this path to the environment variable '%s'.",
"<nix/...>",
"--extra-deprecated-features nix-path-shadow",
path.s,
path.s,
"NIX_PATH"
)}
);
}
}
}
}
Evaluator::Evaluator(
@@ -0,0 +1,6 @@
---
name: nix-path-shadow
internalName: NixPathShadow
---
Allows shadowing `<nix/fetchurl.nix>` by configuration of the [*nix path*](@docroot@/language/builtin-constants.html#builtins-nixPath) to a value containing `nix=/some/path`.
+1
View File
@@ -172,6 +172,7 @@ deprecated_feature_definitions = files(
# keep-sorted start
'deprecated-features/ancient-let.md',
'deprecated-features/cr-line-endings.md',
'deprecated-features/nix-path-shadow.md',
'deprecated-features/nul-bytes.md',
'deprecated-features/rec-set-overrides.md',
'deprecated-features/shadow-internal-symbols.md',
@@ -0,0 +1 @@
/__corepkgs__/fetchurl.nix
@@ -0,0 +1,3 @@
warning: The prefix 'nix' is reserved for internal use by Lix in the Nix search path, its usage is deprecated and will be forbidden in the future.
Use --extra-deprecated-features nix-path-shadow to silence this warning.
This is due to adding 'nix=nix-shadow' in the Nix search path, either through the environment variable 'NIX_PATH' or by passing the flag -I to the nix invocation.
@@ -0,0 +1 @@
/__corepkgs__/fetchurl.nix
@@ -0,0 +1,3 @@
warning: Shadowing '<nix/...>' by configuring the nix-path is deprecated and will be forbidden in the future.
Use --extra-deprecated-features nix-path-shadow to silence this warning.
This is due to adding 'nix-shadow' to the nix-path without a prefix, either by passing the flag '-I nix-shadow' to the nix invocation or by adding this path to the environment variable 'NIX_PATH'.
@@ -0,0 +1 @@
/pwd/nix-shadow/nix/fetchurl.nix
@@ -0,0 +1 @@
<nix/fetchurl.nix>
@@ -0,0 +1 @@
assert false; "womp womp"
@@ -2,7 +2,7 @@ from collections.abc import Callable
from pathlib import Path
from functional2.lang.test_lang import test_eval as nix_eval
from functional2.testlib.fixtures.file_helper import with_files, CopyTree, CopyFile, AssetSymlink
from functional2.testlib.fixtures.file_helper import AssetSymlink, CopyFile, CopyTree, with_files
from functional2.testlib.fixtures.nix import Nix
from functional2.testlib.fixtures.snapshot import Snapshot
@@ -36,3 +36,39 @@ def test_search_path(files: Path, nix: Nix, snapshot: Callable[[str], Snapshot])
],
snapshot,
)
@with_files(
{
"nix-shadow": CopyTree("nix-shadow"),
"in.nix": CopyFile("in-fetchurl.nix"),
"out.exp": AssetSymlink("eval-okay-prefixed.out.exp"),
"err.exp": AssetSymlink("eval-okay-prefixed.err.exp"),
}
)
def test_prefixed_search_path(files: Path, nix: Nix, snapshot: Callable[[str], Snapshot]):
nix.env.set_env("NIX_PATH", "nix=nix-shadow")
nix_eval(files, nix, [], snapshot)
@with_files(
{
"nix-shadow": CopyTree("nix-shadow"),
"in.nix": CopyFile("in-fetchurl.nix"),
"out.exp": AssetSymlink("eval-okay-prefixless.out.exp"),
"err.exp": AssetSymlink("eval-okay-prefixless.err.exp"),
}
)
def test_prefixless_search_path(files: Path, nix: Nix, snapshot: Callable[[str], Snapshot]):
nix_eval(files, nix, ["-I", "nix-shadow"], snapshot)
@with_files(
{
"in.nix": CopyFile("in-fetchurl.nix"),
"out.exp": AssetSymlink("eval-okay-fetchurl.out.exp"),
"err.exp": AssetSymlink("eval-okay-fetchurl.err.exp"),
}
)
def test_empty_search_path(files: Path, nix: Nix, snapshot: Callable[[str], Snapshot]):
nix_eval(files, nix, [], snapshot)