Skip to content

Commit ee14c7c

Browse files
Record subset of meta in provenance
1 parent df396d1 commit ee14c7c

File tree

10 files changed

+189
-37
lines changed

10 files changed

+189
-37
lines changed

src/libexpr/include/nix/expr/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ headers = [ config_pub_h ] + files(
3030
'print-ambiguous.hh',
3131
'print-options.hh',
3232
'print.hh',
33+
'provenance.hh',
3334
'repl-exit-status.hh',
3435
'search-path.hh',
3536
'static-string-data.hh',
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#pragma once
2+
3+
#include "nix/util/provenance.hh"
4+
5+
namespace nix {
6+
7+
/**
8+
* Provenance indicating that this store path was instantiated by the `derivation` builtin function. Its main purpose is
9+
* to record `meta` fields.
10+
*/
11+
struct MetaProvenance : Provenance
12+
{
13+
std::shared_ptr<const Provenance> next;
14+
ref<nlohmann::json> meta;
15+
16+
MetaProvenance(std::shared_ptr<const Provenance> next, ref<nlohmann::json> meta)
17+
: next(std::move(next))
18+
, meta(std::move(meta)) {};
19+
20+
nlohmann::json to_json() const override;
21+
};
22+
23+
} // namespace nix

src/libexpr/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ sources = files(
181181
'primops.cc',
182182
'print-ambiguous.cc',
183183
'print.cc',
184+
'provenance.cc',
184185
'search-path.cc',
185186
'symbol-table.cc',
186187
'value-to-json.cc',

src/libexpr/primops.cc

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "nix/fetchers/fetch-to-store.hh"
1919
#include "nix/util/sort.hh"
2020
#include "nix/util/mounted-source-accessor.hh"
21+
#include "nix/expr/provenance.hh"
2122

2223
#include <boost/container/small_vector.hpp>
2324
#include <boost/unordered/concurrent_flat_map.hpp>
@@ -1504,6 +1505,8 @@ static void derivationStrictInternal(EvalState & state, std::string_view drvName
15041505
StringSet outputs;
15051506
outputs.insert("out");
15061507

1508+
auto provenance = state.evalContext.provenance;
1509+
15071510
for (auto & i : attrs->lexicographicOrder(state.symbols)) {
15081511
if (i->name == state.s.ignoreNulls)
15091512
continue;
@@ -1571,6 +1574,24 @@ static void derivationStrictInternal(EvalState & state, std::string_view drvName
15711574
experimentalFeatureSettings.require(Xp::ImpureDerivations);
15721575
}
15731576
break;
1577+
case EvalState::s.meta.getId():
1578+
experimentalFeatureSettings.require(Xp::Provenance);
1579+
1580+
{
1581+
auto meta = printValueAsJSON(state, true, *i->value, pos, context);
1582+
1583+
for (auto it = meta.begin(); it != meta.end();) {
1584+
if (it.key() == "identifiers" || it.key() == "license" || it.key() == "licenses") {
1585+
it++;
1586+
continue;
1587+
}
1588+
1589+
it = meta.erase(it);
1590+
}
1591+
1592+
provenance = std::make_shared<const MetaProvenance>(provenance, make_ref<nlohmann::json>(meta));
1593+
}
1594+
break;
15741595
/* The `args' attribute is special: it supplies the
15751596
command-line arguments to the builder. */
15761597
case EvalState::s.args.getId():
@@ -1847,8 +1868,7 @@ static void derivationStrictInternal(EvalState & state, std::string_view drvName
18471868
}
18481869

18491870
/* Write the resulting term into the Nix store directory. */
1850-
auto drvPath =
1851-
writeDerivation(*state.store, *state.asyncPathWriter, drv, state.repair, false, state.evalContext.provenance);
1871+
auto drvPath = writeDerivation(*state.store, *state.asyncPathWriter, drv, state.repair, false, provenance);
18521872
auto drvPathS = state.store->printStorePath(drvPath);
18531873

18541874
printMsg(lvlChatty, "instantiated '%1%' -> '%2%'", drvName, drvPathS);
@@ -5420,7 +5440,14 @@ void EvalState::createBaseEnv(const EvalSettings & evalSettings)
54205440
language feature gets added. It's not necessary to increase it
54215441
when primops get added, because you can just use `builtins ?
54225442
primOp' to check. */
5423-
v.mkInt(6);
5443+
if (experimentalFeatureSettings.isEnabled(Xp::Provenance)) {
5444+
/* Provenance allows for meta to be inside of derivations.
5445+
We increment the version to 7 so Nixpkgs will know when
5446+
provenance is available. */
5447+
v.mkInt(7);
5448+
} else {
5449+
v.mkInt(6);
5450+
}
54245451
addConstant(
54255452
"__langVersion",
54265453
v,

src/libexpr/provenance.cc

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#include "nix/expr/provenance.hh"
2+
#include "nix/util/json-utils.hh"
3+
4+
#include <nlohmann/json.hpp>
5+
6+
namespace nix {
7+
8+
nlohmann::json MetaProvenance::to_json() const
9+
{
10+
return nlohmann::json{
11+
{"type", "meta"},
12+
{"meta", *meta},
13+
{"next", next ? next->to_json() : nlohmann::json(nullptr)},
14+
};
15+
}
16+
17+
Provenance::Register registerMetaProvenance("meta", [](nlohmann::json json) {
18+
auto & obj = getObject(json);
19+
std::shared_ptr<const Provenance> next;
20+
if (auto p = optionalValueAt(obj, "next"); p && !p->is_null())
21+
next = Provenance::from_json(*p);
22+
return make_ref<MetaProvenance>(next, make_ref<nlohmann::json>(valueAt(obj, "meta")));
23+
});
24+
25+
} // namespace nix

src/nix/provenance-show.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ The provenance chain shows the history of how the store path came to exist, incl
2222
- **Built**: The path was built from a derivation.
2323
- **Flake evaluation**: The derivation was instantiated during the evaluation of a flake output.
2424
- **Fetched**: The path was obtained by fetching a source tree.
25+
- **Meta**: Metadata associated with the derivation.
2526

2627
Note: if you want provenance in JSON format, use the `provenance` field returned by `nix path-info --json`.
2728

src/nix/provenance.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "nix/cmd/command.hh"
22
#include "nix/store/store-api.hh"
3+
#include "nix/expr/provenance.hh"
34
#include "nix/store/provenance.hh"
45
#include "nix/flake/provenance.hh"
56
#include "nix/fetchers/provenance.hh"
@@ -91,6 +92,9 @@ struct CmdProvenanceShow : StorePathsCommand
9192
} else if (auto subpath = std::dynamic_pointer_cast<const SubpathProvenance>(provenance)) {
9293
logger->cout("← from file " ANSI_BOLD "%s" ANSI_NORMAL, subpath->subpath.abs());
9394
provenance = subpath->next;
95+
} else if (auto meta = std::dynamic_pointer_cast<const MetaProvenance>(provenance)) {
96+
logger->cout("← with metadata");
97+
provenance = meta->next;
9498
} else {
9599
// Unknown or unhandled provenance type
96100
auto json = provenance->to_json();

tests/functional/config.nix.in

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ let
55
outputHashMode = "recursive";
66
outputHashAlgo = "sha256";
77
} else {};
8+
9+
optional = cond: elem: if cond then [ elem ] else [];
10+
optionalAttrs = cond: attrs: if cond then attrs else {};
811
in
912

1013
rec {
@@ -25,6 +28,6 @@ rec {
2528
eval "$buildCommand"
2629
'')];
2730
PATH = path;
28-
} // caArgs // removeAttrs args ["builder" "meta"])
29-
// { meta = args.meta or {}; };
31+
} // caArgs // removeAttrs args (["builder"] ++ optional (builtins.langVersion < 7) "meta"))
32+
// optionalAttrs (builtins.langVersion < 7) { meta = args.meta or {}; };
3033
}

tests/functional/flakes/provenance.sh

Lines changed: 83 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -15,35 +15,51 @@ lastModified=$(nix flake metadata --json "$flake1Dir" | jq -r .locked.lastModifi
1515
treePath=$(nix flake prefetch --json "$flake1Dir" | jq -r .storePath)
1616
builder=$(nix eval --raw "$flake1Dir#packages.$system.default._builder")
1717

18-
# Building a derivation should have tree+subpath+flake+build provenance.
19-
[[ $(nix path-info --json --json-format 1 "$outPath" | jq ".\"$outPath\".provenance") = $(cat <<EOF
18+
# Building a derivation should have tree+subpath+flake+meta+build provenance.
19+
[[ "$(nix path-info --json --json-format 1 "$outPath" | jq ".\"$outPath\".provenance")" == "$(cat <<EOF
2020
{
2121
"buildHost": "test-host",
2222
"drv": "$(basename "$drvPath")",
2323
"next": {
24-
"flakeOutput": "packages.$system.default",
24+
"meta": {
25+
"license": [
26+
{
27+
"deprecated": true,
28+
"free": true,
29+
"fullName": "GNU Lesser General Public License v2.1",
30+
"redistributable": true,
31+
"shortName": "lgpl21",
32+
"spdxId": "LGPL-2.1",
33+
"url": "https://spdx.org/licenses/LGPL-2.1.html"
34+
}
35+
]
36+
},
2537
"next": {
38+
"flakeOutput": "packages.$system.default",
2639
"next": {
27-
"attrs": {
28-
"lastModified": $lastModified,
29-
"ref": "refs/heads/master",
30-
"rev": "$rev",
31-
"revCount": 1,
32-
"type": "git",
33-
"url": "file://$flake1Dir"
40+
"next": {
41+
"attrs": {
42+
"lastModified": $lastModified,
43+
"ref": "refs/heads/master",
44+
"rev": "$rev",
45+
"revCount": 1,
46+
"type": "git",
47+
"url": "file://$flake1Dir"
48+
},
49+
"type": "tree"
3450
},
35-
"type": "tree"
51+
"subpath": "/flake.nix",
52+
"type": "subpath"
3653
},
37-
"subpath": "/flake.nix",
38-
"type": "subpath"
54+
"type": "flake"
3955
},
40-
"type": "flake"
56+
"type": "meta"
4157
},
4258
"output": "out",
4359
"type": "build"
4460
}
4561
EOF
46-
) ]]
62+
)" ]]
4763

4864
# Flakes should have "tree" provenance.
4965
[[ $(nix path-info --json --json-format 1 "$treePath" | jq ".\"$treePath\".provenance") = $(cat <<EOF
@@ -89,52 +105,88 @@ clearStore
89105

90106
nix copy --from "file://$binaryCache" "$outPath" --no-check-sigs
91107

92-
[[ $(nix path-info --json --json-format 1 "$outPath" | jq ".\"$outPath\".provenance") = $(cat <<EOF
108+
[[ "$(nix path-info --json --json-format 1 "$outPath" | jq ".\"$outPath\".provenance")" = "$(cat <<EOF
93109
{
94110
"from": "file://$binaryCache",
95111
"next": {
96112
"buildHost": "test-host",
97113
"drv": "$(basename "$drvPath")",
98114
"next": {
99-
"flakeOutput": "packages.$system.default",
115+
"meta": {
116+
"license": [
117+
{
118+
"deprecated": true,
119+
"free": true,
120+
"fullName": "GNU Lesser General Public License v2.1",
121+
"redistributable": true,
122+
"shortName": "lgpl21",
123+
"spdxId": "LGPL-2.1",
124+
"url": "https://spdx.org/licenses/LGPL-2.1.html"
125+
}
126+
]
127+
},
100128
"next": {
129+
"flakeOutput": "packages.$system.default",
101130
"next": {
102-
"attrs": {
103-
"lastModified": $lastModified,
104-
"ref": "refs/heads/master",
105-
"rev": "$rev",
106-
"revCount": 1,
107-
"type": "git",
108-
"url": "file://$flake1Dir"
131+
"next": {
132+
"attrs": {
133+
"lastModified": $lastModified,
134+
"ref": "refs/heads/master",
135+
"rev": "$rev",
136+
"revCount": 1,
137+
"type": "git",
138+
"url": "file://$flake1Dir"
139+
},
140+
"type": "tree"
109141
},
110-
"type": "tree"
142+
"subpath": "/flake.nix",
143+
"type": "subpath"
111144
},
112-
"subpath": "/flake.nix",
113-
"type": "subpath"
145+
"type": "flake"
114146
},
115-
"type": "flake"
147+
"type": "meta"
116148
},
117149
"output": "out",
118150
"type": "build"
119151
},
120152
"type": "copied"
121153
}
122154
EOF
123-
) ]]
155+
)" ]]
124156

125157
# Test `nix provenance show`.
126-
[[ $(nix provenance show "$outPath") = $(cat <<EOF
158+
[[ "$(nix provenance show "$outPath")" = $(cat <<EOF
127159
$outPath
128160
← copied from file://$binaryCache
129161
← built from derivation $drvPath (output out) on test-host
162+
← with metadata
130163
← instantiated from flake output git+file://$flake1Dir?ref=refs/heads/master&rev=$rev#packages.$system.default
131164
EOF
132165
) ]]
133166

134167
# Check that --impure does not add provenance.
135168
clearStore
136169
nix build --impure --print-out-paths --no-link "$flake1Dir#packages.$system.default"
137-
[[ $(nix path-info --json --json-format 1 "$drvPath" | jq ".\"$drvPath\".provenance") = null ]]
170+
[[ "$(nix path-info --json --json-format 1 "$drvPath" | jq ".\"$drvPath\".provenance")" = "$(cat << EOF
171+
{
172+
"meta": {
173+
"license": [
174+
{
175+
"deprecated": true,
176+
"free": true,
177+
"fullName": "GNU Lesser General Public License v2.1",
178+
"redistributable": true,
179+
"shortName": "lgpl21",
180+
"spdxId": "LGPL-2.1",
181+
"url": "https://spdx.org/licenses/LGPL-2.1.html"
182+
}
183+
]
184+
},
185+
"next": null,
186+
"type": "meta"
187+
}
188+
EOF
189+
)" ]]
138190

139191
clearStore
140192
echo foo > "$flake1Dir/somefile"

tests/functional/simple.nix

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,20 @@ mkDerivation {
66
_builder = ./simple.builder.sh;
77
PATH = "";
88
goodPath = path;
9-
meta.position = "${__curPos.file}:${toString __curPos.line}";
9+
meta = {
10+
position = "${__curPos.file}:${toString __curPos.line}";
11+
license = [
12+
# Since this file is from Nix, use Nix's license.
13+
# Keep in sync with `lib.licenses.lgpl21` from Nixpkgs.
14+
{
15+
deprecated = true;
16+
free = true;
17+
fullName = "GNU Lesser General Public License v2.1";
18+
redistributable = true;
19+
shortName = "lgpl21";
20+
spdxId = "LGPL-2.1";
21+
url = "https://spdx.org/licenses/LGPL-2.1.html";
22+
}
23+
];
24+
};
1025
}

0 commit comments

Comments
 (0)