diff --git a/doc/manual/rl-next/nix-develop-output-checks.md b/doc/manual/rl-next/nix-develop-output-checks.md new file mode 100644 index 000000000..dbb358958 --- /dev/null +++ b/doc/manual/rl-next/nix-develop-output-checks.md @@ -0,0 +1,29 @@ +--- +synopsis: "Fix nix develop for derivations that rejects dependencies with structured attrs" +issues: [fj#997] +cls: [4182, 4214] +category: Fixes +credits: [raito] +--- + +For the sake of concision, we refer to `disallowedReferences` in what follows, +but all output checks were equally fixed: +`{dis,}allowed{References,Requisites}`. + +Derivations can define *output checks* to reject unwanted dependencies, such as +interpreters like `bash` or compilers like `gcc`. This can be done in two ways: + +* **Legacy style**: `disallowedReferences = [ ... ]` in the environment. +* **Structured attrs**: `outputChecks..disallowedReferences = [ ... ]`, + typically used in `__json`. + +Only the structured form supports derivations with multiple outputs. + +`nix develop` internally rewrites derivations to create development shells. It +relied on the legacy `disallowedReferences`, and failed to honor the structured +variant. This led to broken shells in cases where `bashInteractive` was +explicitly disallowed using structured output checks, e.g. `nix develop +nixpkgs#systemd` after the "bash-less NixOS" changes. + +This fix teaches `nix develop` to respect structured output checks, restoring +support for such derivations. diff --git a/lix/nix/develop.cc b/lix/nix/develop.cc index 52a6f5d4f..f6d4f79c6 100644 --- a/lix/nix/develop.cc +++ b/lix/nix/develop.cc @@ -6,6 +6,7 @@ #include "lix/libstore/store-api.hh" #include "lix/libstore/outputs-spec.hh" #include "lix/libstore/derivations.hh" +#include "lix/libstore/parsed-derivations.hh" #include "lix/libutil/async.hh" #include "lix/libutil/json.hh" #include "run.hh" @@ -214,6 +215,8 @@ const static std::string getEnvSh = static kj::Promise> getDerivationEnvironment(ref store, ref evalStore, const StorePath & drvPath) try { auto drv = TRY_AWAIT(evalStore->derivationFromPath(drvPath)); + ParsedDerivation parsedDrv(drvPath, drv); + JSON updatedStructuredAttrs; auto builder = baseNameOf(drv.builder); if (builder != "bash") @@ -230,6 +233,20 @@ try { drv.env.erase("disallowedRequisites"); drv.env.erase("name"); + /* Remove output checks in structured attrs. */ + if (auto structuredAttrs = parsedDrv.getStructuredAttrs()) { + drv.env.erase("__json"); + updatedStructuredAttrs = *structuredAttrs; + updatedStructuredAttrs.erase("allowedReferences"); + updatedStructuredAttrs.erase("allowedRequisites"); + updatedStructuredAttrs.erase("disallowedReferences"); + updatedStructuredAttrs.erase("disallowedRequisites"); + updatedStructuredAttrs.erase("maxSize"); + updatedStructuredAttrs.erase("maxClosureSize"); + updatedStructuredAttrs.erase("outputChecks"); + drv.env.emplace("__json", updatedStructuredAttrs.dump()); + } + /* Rehash and write the derivation. FIXME: would be nice to use 'buildDerivation', but that's privileged. */ drv.name += "-env";