Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions src/libexpr/include/nix/expr/eval.hh
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ struct StaticEvalSymbols
line, column, functor, toString, right, wrong, structuredAttrs, json, allowedReferences, allowedRequisites,
disallowedReferences, disallowedRequisites, maxSize, maxClosureSize, builder, args, contentAddressed, impure,
outputHash, outputHashAlgo, outputHashMode, recurseForDerivations, description, self, epsilon, startSet,
operator_, key, path, prefix, outputSpecified, __meta, identifiers, license, licenses;
operator_, key, path, prefix, outputSpecified, __meta;

Expr::AstSymbols exprSymbols;

Expand Down Expand Up @@ -282,9 +282,6 @@ struct StaticEvalSymbols
.prefix = alloc.create("prefix"),
.outputSpecified = alloc.create("outputSpecified"),
.__meta = alloc.create("__meta"),
.identifiers = alloc.create("identifier"),
.license = alloc.create("license"),
.licenses = alloc.create("licenses"),
.exprSymbols = {
.sub = alloc.create("__sub"),
.lessThan = alloc.create("__lessThan"),
Expand Down
118 changes: 71 additions & 47 deletions src/libexpr/primops.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1380,16 +1380,15 @@ static void prim_second(EvalState & state, const PosIdx pos, Value ** args, Valu
* Derivations
*************************************************************/

static void derivationStrictInternal(EvalState & state, std::string_view name, const Bindings * attrs, Value & v);
static void derivationStrictInternal(
EvalState & state,
std::string_view name,
const Bindings * attrs,
Value & v,
std::shared_ptr<const Provenance> provenance,
bool acceptMeta);

/* Construct (as a unobservable side effect) a Nix derivation
expression that performs the derivation described by the argument
set. Returns the original set extended with the following
attributes: `outPath' containing the primary output path of the
derivation; `drvPath' containing the path of the Nix expression;
and `type' set to `derivation' to indicate that this is a
derivation. */
static void prim_derivationStrict(EvalState & state, const PosIdx pos, Value ** args, Value & v)
static void prim_derivationStrictGeneric(EvalState & state, const PosIdx pos, Value ** args, Value & v, bool acceptMeta)
{
state.forceAttrs(*args[0], pos, "while evaluating the argument passed to builtins.derivationStrict");

Expand All @@ -1409,7 +1408,7 @@ static void prim_derivationStrict(EvalState & state, const PosIdx pos, Value **
}

try {
derivationStrictInternal(state, drvName, attrs, v);
derivationStrictInternal(state, drvName, attrs, v, state.evalContext.provenance, acceptMeta);
} catch (Error & e) {
Pos pos = state.positions[nameAttr->pos];
/*
Expand Down Expand Up @@ -1440,6 +1439,18 @@ static void prim_derivationStrict(EvalState & state, const PosIdx pos, Value **
}
}

/* Construct a Nix derivation with metadata provenance */
static RegisterPrimOp primop_derivationStrictWithMeta(
PrimOp{
.name = "derivationStrictWithMeta",
.arity = 1,
.fun =
[](EvalState & state, const PosIdx pos, Value ** args, Value & v) {
prim_derivationStrictGeneric(state, pos, args, v, /*acceptMeta=*/true);
},
.internal = true,
});

/**
* Early validation for the derivation name, for better error message.
* It is checked again when constructing store paths.
Expand All @@ -1463,7 +1474,13 @@ static void checkDerivationName(EvalState & state, std::string_view drvName)
}
}

static void derivationStrictInternal(EvalState & state, std::string_view drvName, const Bindings * attrs, Value & v)
static void derivationStrictInternal(
EvalState & state,
std::string_view drvName,
const Bindings * attrs,
Value & v,
std::shared_ptr<const Provenance> provenance,
bool acceptMeta)
{
checkDerivationName(state, drvName);

Expand Down Expand Up @@ -1505,8 +1522,6 @@ static void derivationStrictInternal(EvalState & state, std::string_view drvName
StringSet outputs;
outputs.insert("out");

auto provenance = state.evalContext.provenance;

for (auto & i : attrs->lexicographicOrder(state.symbols)) {
if (i->name == state.s.ignoreNulls)
continue;
Expand Down Expand Up @@ -1574,29 +1589,6 @@ static void derivationStrictInternal(EvalState & state, std::string_view drvName
experimentalFeatureSettings.require(Xp::ImpureDerivations);
}
break;
case EvalState::s.__meta.getId():
if (experimentalFeatureSettings.isEnabled(Xp::Provenance)) {
state.forceAttrs(*i->value, pos, "");
auto meta = i->value->attrs();
auto obj = nlohmann::json();

for (auto & i : meta->lexicographicOrder(state.symbols)) {
auto key = state.symbols[i->name];
switch (i->name.getId()) {
case EvalState::s.identifiers.getId():
case EvalState::s.license.getId():
case EvalState::s.licenses.getId():
obj.emplace(key, printValueAsJSON(state, true, *i->value, pos, context));
break;
default:
continue;
}
}

provenance =
std::make_shared<const DerivationProvenance>(provenance, make_ref<nlohmann::json>(obj));
}
break;
/* The `args' attribute is special: it supplies the
command-line arguments to the builder. */
case EvalState::s.args.getId():
Expand All @@ -1613,7 +1605,19 @@ static void derivationStrictInternal(EvalState & state, std::string_view drvName
the environment. */
default:

if (jsonObject) {
if (acceptMeta && i->name == EvalState::s.__meta) {
if (experimentalFeatureSettings.isEnabled(Xp::Provenance)) {
state.forceAttrs(*i->value, pos, "while evaluating __meta");
NixStringContext ctx;
auto obj = printValueAsJSON(state, true, *i->value, pos, ctx);

if (!ctx.empty())
throw Error("Derivation __meta provenance can't contain string context like store paths.");

provenance =
std::make_shared<const DerivationProvenance>(provenance, make_ref<nlohmann::json>(obj));
}
} else if (jsonObject) {
Comment on lines 1608 to 1620
Copy link

@coderabbitai coderabbitai bot Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's look at the actual code at lines 1662-1684 in src/libexpr/primops.cc
sed -n '1662,1684p' src/libexpr/primops.cc

Repository: DeterminateSystems/nix-src

Length of output: 1305


🏁 Script executed:

# Also get some context around those lines to understand the structure better
sed -n '1650,1700p' src/libexpr/primops.cc | head -80

Repository: DeterminateSystems/nix-src

Length of output: 2624


🏁 Script executed:

# Find printValueAsJSON definition and signature
rg -n "printValueAsJSON" src/libexpr/ -A 3 -B 1 | head -50

Repository: DeterminateSystems/nix-src

Length of output: 3106


🏁 Script executed:

# Check if there are any safeguards or context handling around __meta in primops
rg -n "metaContext\|__meta" src/libexpr/primops.cc -B 2 -A 2

Repository: DeterminateSystems/nix-src

Length of output: 52


🏁 Script executed:

# Find the derivationStrictInternal function definition and context variable declaration
rg -n "derivationStrictInternal" src/libexpr/primops.cc -A 100 | head -120

Repository: DeterminateSystems/nix-src

Length of output: 5774


🏁 Script executed:

# Search for where context is declared in primops.cc around the derivation function
sed -n '1500,1700p' src/libexpr/primops.cc | grep -n "context\|NixStringContext"

Repository: DeterminateSystems/nix-src

Length of output: 1173


🏁 Script executed:

# Check how context is used after the __meta block in the derivation function
sed -n '1680,1750p' src/libexpr/primops.cc

Repository: DeterminateSystems/nix-src

Length of output: 4273


🏁 Script executed:

# Search for where the context collected during printValueAsJSON is actually used
rg -n "drv.env\|structuredAttrs" src/libexpr/primops.cc -B 2 -A 2 | head -80

Repository: DeterminateSystems/nix-src

Length of output: 52


🏁 Script executed:

# Let's see how context is used at the end of derivationStrictInternal
sed -n '1700,1800p' src/libexpr/primops.cc

Repository: DeterminateSystems/nix-src

Length of output: 5462


🏁 Script executed:

# Check how drv.env is populated and whether it uses context
rg -n "drv.env" src/libexpr/primops.cc -B 2 -A 2

Repository: DeterminateSystems/nix-src

Length of output: 1122


🏁 Script executed:

# Let's also check what happens to drv after it's built
sed -n '1800,1900p' src/libexpr/primops.cc

Repository: DeterminateSystems/nix-src

Length of output: 4423


🏁 Script executed:

# Search for where context is actually used to add dependencies
rg -n "printDerivation\|computeExportReferences" src/libexpr/primops.cc -B 1 -A 3

Repository: DeterminateSystems/nix-src

Length of output: 52


🏁 Script executed:

# Let's check if there's any filtering or validation of __meta elsewhere
rg -n "__meta\|metaContext" src/libexpr/primops.cc -B 2 -A 2

Repository: DeterminateSystems/nix-src

Length of output: 52


🏁 Script executed:

# Check if there are any validations for meta fields in other files
rg -n "license\|licenses\|identifiers" src/libexpr/ -l | head -20

Repository: DeterminateSystems/nix-src

Length of output: 52


🏁 Script executed:

# Let's also check if printValueAsJSON has any logic that clears context
sed -n '45,100p' src/libexpr/value-to-json.cc

Repository: DeterminateSystems/nix-src

Length of output: 1847


🏁 Script executed:

# Let's check if there's validation that license/identifiers should not have store paths
rg -n "license\|identifiers" src/libexpr/primops.cc -B 1 -A 1

Repository: DeterminateSystems/nix-src

Length of output: 52


🏁 Script executed:

# Check if there's documentation or comments about meta handling
sed -n '1650,1680p' src/libexpr/primops.cc | grep -i "meta\|provenance\|tracking"

Repository: DeterminateSystems/nix-src

Length of output: 441


🏁 Script executed:

# Look at the full context flow - see where context is passed and how it affects drv.inputDrvs/inputSrcs
sed -n '1945,1980p' src/libexpr/primops.cc

Repository: DeterminateSystems/nix-src

Length of output: 1222


🏁 Script executed:

# Let's verify the complete flow by checking exactly how context is accumulated
# and whether there's any filtering between __meta processing and the dependency loop
sed -n '1560,1600p' src/libexpr/primops.cc | grep -n "context"

Repository: DeterminateSystems/nix-src

Length of output: 101


🏁 Script executed:

# Check the function signature of derivationStrictInternal to understand context scope
sed -n '1560,1650p' src/libexpr/primops.cc

Repository: DeterminateSystems/nix-src

Length of output: 3963


🏁 Script executed:

# Verify that context is a single shared variable throughout the function
rg -n "NixStringContext context" src/libexpr/primops.cc

Repository: DeterminateSystems/nix-src

Length of output: 984


Isolate __meta context to prevent store-path references from leaking into derivation dependencies.

When processing __meta at line 1674, printValueAsJSON(..., context) accumulates string context into the shared context variable. If any __meta field (identifiers, license, licenses) contains a string with store-path context, that dependency is silently added to drv.inputDrvs and drv.inputSrcs (lines 1945+). This violates the design intent that meta is tracking-only metadata, not build input data. License information should be plain textual without store-path references.

Use a dedicated metaContext variable for __meta processing and reject non-empty context:

Proposed fix
                         auto obj = nlohmann::json();
+                        NixStringContext metaContext;
 
                         for (auto & i : meta->lexicographicOrder(state.symbols)) {
                             auto key = state.symbols[i->name];
                             switch (i->name.getId()) {
                             case EvalState::s.identifiers.getId():
                             case EvalState::s.license.getId():
                             case EvalState::s.licenses.getId():
-                                obj.emplace(key, printValueAsJSON(state, true, *i->value, pos, context));
+                                obj.emplace(key, printValueAsJSON(state, true, *i->value, pos, metaContext));
                                 break;
                             default:
                                 continue;
                             }
                         }
 
+                        if (!metaContext.empty())
+                            state.error<EvalError>("__meta must not reference store paths").atPos(pos).debugThrow();
+
                         provenance =
                             std::make_shared<const DerivationProvenance>(provenance, make_ref<nlohmann::json>(obj));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/libexpr/primops.cc` around lines 1662 - 1684, The __meta handling
currently calls printValueAsJSON(..., context) which lets any string context
(store-path references) leak into the shared context and thus into derivation
inputs; create a fresh metaContext (a Context instance with empty string
context) and pass metaContext to printValueAsJSON when processing
EvalState::s.__meta fields (identifiers, license, licenses), then after
processing check metaContext.stringContext (or equivalent) and if it is
non-empty reject/raise an error (or otherwise refuse to accept store-path
references in meta) before creating the DerivationProvenance and assigning
provenance via make_ref<nlohmann::json>(obj).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Metadata never contains store paths

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RossComputerGuy, thank you for the clarification! I understand that metadata is designed to contain only plain textual data without store-path references, consistent with the documented expectation that meta is for tracking purposes rather than build dependencies.

If you'd like to add defensive validation in the future to catch any accidental violations of this assumption (e.g., if meta fields unexpectedly contain store references), the separate metaContext approach could help surface such issues early. But I understand this may not be a practical concern given current usage patterns.

I've noted this design constraint for future reference.


🧠 Learnings used
Learnt from: RossComputerGuy
Repo: DeterminateSystems/nix-src PR: 342
File: src/libexpr/primops.cc:1577-1594
Timestamp: 2026-02-10T20:44:08.621Z
Learning: In `src/libexpr/primops.cc`, the `EvalState::s.meta` case in `derivationStrictInternal` intentionally records filtered meta information (identifiers, license, licenses fields) into the provenance system via `MetaProvenance` without adding it to the derivation environment (`drv.env` or `drv.structuredAttrs`). This is by design because meta is metadata for tracking purposes, not build inputs. License information is expected to be plain textual data without store path references.

Learnt from: edolstra
Repo: DeterminateSystems/nix-src PR: 321
File: src/libstore/build/derivation-building-goal.cc:443-447
Timestamp: 2026-01-23T12:00:50.308Z
Learning: In the Nix build system, for the `AlreadyValid` case in derivation building (when outputs already exist and are valid), provenance of the current derivation should NOT be returned because the existing outputs may have been built with different provenance than the current derivation, making it misleading to associate them.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RossComputerGuy What if meta does contain a store path, though? Just because it shouldn't or because we don't want it to doesn't mean it never will; there are lots of people doing crazy things out there ;P

Is it actually impossible for meta to include a store path because of some invariant somewhere?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm actually not sure entirely what would happen if meta were to contain a store path. Afaik, it would be in the provenance then and nothing special would happen. The store path could be used as normal.

The only invariant is nixpkgs that would affect things because of check-meta.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should keep nixpkgs in mind as we design and build, but we need to avoid assuming users are using nixpkgs.

fwiw I tried this:

{ system, seed ? 1 }:
let
  meta = derivation {
    name = "meta";
    inherit system seed;

    builder = "/bin/sh";
    args = [
      "-c"
      "echo $dep >$out"
    ];
  };
in
derivationWithMeta {
  name = "nested";
  inherit system seed;

  builder = "/bin/sh";
  args = [
    "-c"
    "echo ${meta} >$out"
  ];

  __meta.license = { meh = meta; };
}

and nix path-info showed:

      "provenance": {
        "buildHost": "Grahams-MacBook-Pro.local",
        "drv": "yid3h2v5bfg1g22zb5yjasy4gxgcl6ng-nested.drv",
        "next": {
          "meta": {
            "license": {
              "meh": "/nix/store/71dalf5p05pcfyb6lxqqmz27qjfzvqlx-meta"
            }
          },

which I think is an interesting thing to think about. I think we should probably prohibit this here to avoid a hard-to-undo mistake. We can always open it up later. Much harder to go backwards.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's certainly not great, the problem is meta does need some sort of structure and nixpkgs is really the only standard there is for this stuff. It is certainly nice to have pretty printing of the meta via nix provenance show and nix path-info --json showing the actual raw output. We always could check if there's a type attribute since that attribute is common. If it is set to the magic string then nix provenance show would print and assume the nixpkgs format. That way the caller can tell nix provenance show what that information is like without having to guess. The default could be to print the JSON when not recognizing the format or just not print the metadata at all.


if (i->name == state.s.structuredAttrs)
continue;
Expand Down Expand Up @@ -1900,11 +1904,21 @@ static void derivationStrictInternal(EvalState & state, std::string_view drvName
v.mkAttrs(result);
}

/* Construct (as a unobservable side effect) a Nix derivation
expression that performs the derivation described by the argument
set. Returns the original set extended with the following
attributes: `outPath' containing the primary output path of the
derivation; `drvPath' containing the path of the Nix expression;
and `type' set to `derivation' to indicate that this is a
derivation. */
static RegisterPrimOp primop_derivationStrict(
PrimOp{
.name = "derivationStrict",
.arity = 1,
.fun = prim_derivationStrict,
.fun =
[](EvalState & state, const PosIdx pos, Value ** args, Value & v) {
prim_derivationStrictGeneric(state, pos, args, v, /*acceptMeta=*/false);
},
});

/* Return a placeholder string for the specified output that will be
Expand Down Expand Up @@ -5445,14 +5459,7 @@ void EvalState::createBaseEnv(const EvalSettings & evalSettings)
language feature gets added. It's not necessary to increase it
when primops get added, because you can just use `builtins ?
primOp' to check. */
if (experimentalFeatureSettings.isEnabled(Xp::Provenance)) {
/* Provenance allows for meta to be inside of derivations.
We increment the version to 7 so Nixpkgs will know when
provenance is available. */
v.mkInt(7);
} else {
v.mkInt(6);
}
v.mkInt(6);
addConstant(
"__langVersion",
v,
Expand Down Expand Up @@ -5559,6 +5566,16 @@ void EvalState::createBaseEnv(const EvalSettings & evalSettings)
.type = nFunction,
});

auto vDerivationWithMeta = allocValue();
if (experimentalFeatureSettings.isEnabled(Xp::Provenance)) {
addConstant(
"derivationWithMeta",
vDerivationWithMeta,
{
.type = nFunction,
});
}

/* Now that we've added all primops, sort the `builtins' set,
because attribute lookups expect it to be sorted. */
const_cast<Bindings *>(getBuiltins().attrs())->sort();
Expand All @@ -5567,7 +5584,14 @@ void EvalState::createBaseEnv(const EvalSettings & evalSettings)

/* Note: we have to initialize the 'derivation' constant *after*
building baseEnv/staticBaseEnv because it uses 'builtins'. */
evalFile(derivationInternal, *vDerivation);
auto vDerivationValue = allocValue();
evalFile(derivationInternal, *vDerivationValue);

callFunction(*vDerivationValue, getBuiltin("derivationStrict"), *vDerivation, PosIdx());

if (experimentalFeatureSettings.isEnabled(Xp::Provenance))
callFunction(
*vDerivationValue, **get(internalPrimOps, "derivationStrictWithMeta"), *vDerivationWithMeta, PosIdx());
}

} // namespace nix
3 changes: 2 additions & 1 deletion src/libexpr/primops/derivation.nix
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,15 @@
Note that `derivation` is very bare-bones, and provides almost no commands during the build.
Most likely, you'll want to use functions like `stdenv.mkDerivation` in Nixpkgs to set up a basic environment.
*/
drvFunc:
drvAttrs@{
outputs ? [ "out" ],
...
}:

let

strict = derivationStrict drvAttrs;
strict = drvFunc drvAttrs;

commonAttrs =
drvAttrs
Expand Down
56 changes: 6 additions & 50 deletions src/nix/provenance.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@
#include "nix/util/json-utils.hh"

#include <memory>
#include <string>
#include <nlohmann/json.hpp>

#define TAB " "

using namespace nix;

struct CmdProvenance : NixMultiCommand
Expand Down Expand Up @@ -101,57 +104,10 @@ struct CmdProvenanceShow : StorePathsCommand
provenance = subpath->next;
} else if (auto drv = std::dynamic_pointer_cast<const DerivationProvenance>(provenance)) {
logger->cout("← with derivation metadata");
#define TAB " "
auto json = getObject(*(drv->meta));
if (auto identifiers = optionalValueAt(json, "identifiers")) {
auto ident = getObject(*identifiers);
if (auto cpeParts = optionalValueAt(ident, "cpeParts")) {
auto parts = getObject(*cpeParts);

auto vendor = parts["vendor"];
auto product = parts["product"];
auto version = parts["version"];
auto update = parts["update"];

logger->cout(
TAB "" ANSI_BOLD "CPE:" ANSI_NORMAL " cpe:2.3:a:%s:%s:%s:%s:*:*:*:*:*:*",
vendor.is_null() ? "*" : vendor.get<std::string>(),
product.is_null() ? "*" : product.get<std::string>(),
version.is_null() ? "*" : version.get<std::string>(),
update.is_null() ? "*" : update.get<std::string>());
}
}
if (auto license = optionalValueAt(json, "license")) {
if (license->is_array()) {
logger->cout(TAB "" ANSI_BOLD "Licenses:" ANSI_NORMAL);
auto licenses = getArray(*license);
for (auto it = licenses.begin(); it != licenses.end(); it++) {
auto license = getObject(*it);
auto shortName = license["shortName"];
logger->cout(TAB "" TAB "- %s", shortName.get<std::string>());
}
} else {
auto obj = getObject(*license);
auto shortName = obj["shortName"];
logger->cout(TAB "" ANSI_BOLD "License:" ANSI_NORMAL " %s", shortName.get<std::string>());
}
}
if (auto licenses = optionalValueAt(json, "licenses")) {
if (licenses->is_array()) {
logger->cout(TAB "" ANSI_BOLD "Licenses:" ANSI_NORMAL);
auto licensesArray = getArray(*licenses);
for (auto it = licensesArray.begin(); it != licensesArray.end(); it++) {
auto license = getObject(*it);
auto shortName = license["shortName"];
logger->cout(TAB "" TAB "- %s", shortName.get<std::string>());
}
} else {
auto license = getObject(*licenses);
auto shortName = license["shortName"];
logger->cout(TAB "" ANSI_BOLD "License:" ANSI_NORMAL " %s", shortName.get<std::string>());
}
std::istringstream stream((*drv->meta).dump(2));
for (std::string line; std::getline(stream, line);) {
logger->cout(" %s", line);
}
#undef TAB
provenance = drv->next;
} else {
// Unknown or unhandled provenance type
Expand Down
6 changes: 3 additions & 3 deletions tests/functional/config.nix.in
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,16 @@ rec {
shared = builtins.getEnv "_NIX_TEST_SHARED";

mkDerivation = args:
derivation ({
(if builtins ? derivationWithMeta then builtins.derivationWithMeta else builtins.derivation) ({
inherit system;
builder = shell;
args = ["-e" args.builder or (builtins.toFile "builder-${args.name}.sh" ''
if [ -e "$NIX_ATTRS_SH_FILE" ]; then source $NIX_ATTRS_SH_FILE; fi;
eval "$buildCommand"
'')];
PATH = path;
} // caArgs // optionalAttrs (builtins.langVersion >= 7) {
__meta = args.meta or {};
} // caArgs // optionalAttrs (builtins ? derivationWithMeta) {
__meta = removeAttrs (args.meta or {}) ["position"];
} // removeAttrs args ["builder" "meta"])
// { meta = args.meta or {}; };
}
38 changes: 30 additions & 8 deletions tests/functional/flakes/provenance.sh
Original file line number Diff line number Diff line change
Expand Up @@ -174,16 +174,27 @@ EOF
unset _NIX_FORCE_HTTP

# Test `nix provenance show`.
[[ "$(nix provenance show "$outPath")" = $(cat <<EOF
[[ "$(nix provenance show "$outPath")" = "$(cat <<EOF
$outPath
← copied from file://$binaryCache
← built from derivation $drvPath (output out) on test-host for $system
← with derivation metadata
Licenses:
- lgpl21
{
"license": [
{
"deprecated": true,
"free": true,
"fullName": "GNU Lesser General Public License v2.1",
"redistributable": true,
"shortName": "lgpl21",
"spdxId": "LGPL-2.1",
"url": "https://spdx.org/licenses/LGPL-2.1.html"
}
]
}
← instantiated from flake output git+file://$flake1Dir?ref=refs/heads/master&rev=$rev#packages.$system.default
EOF
) ]]
)" ]]

# Check that --impure does not add additional provenance.
clearStore
Expand Down Expand Up @@ -234,15 +245,26 @@ git -C "$flake1Dir" add somefile
nix build --impure --print-out-paths --no-link "$flake1Dir#packages.$system.default"
[[ $(nix path-info --json --json-format 1 "$builder" | jq ".\"$builder\".provenance") != null ]]

[[ "$(nix provenance show "$outPath")" = $(cat <<EOF
[[ "$(nix provenance show "$outPath")" = "$(cat <<EOF
$outPath
← built from derivation $drvPath (output out) on test-host for $system
← with derivation metadata
Licenses:
- lgpl21
{
"license": [
{
"deprecated": true,
"free": true,
"fullName": "GNU Lesser General Public License v2.1",
"redistributable": true,
"shortName": "lgpl21",
"spdxId": "LGPL-2.1",
"url": "https://spdx.org/licenses/LGPL-2.1.html"
}
]
}
← impurely instantiated from unlocked flake output git+file://$flake1Dir#packages.$system.default
EOF
) ]]
)" ]]

[[ "$(nix provenance show "$builder")" = $(cat <<EOF
$builder
Expand Down
Loading
Loading