-
Notifications
You must be signed in to change notification settings - Fork 7
Record subset of meta in provenance #342
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
30e33f1
bb832c0
bccd6a4
b158718
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| #pragma once | ||
|
|
||
| #include "nix/util/provenance.hh" | ||
|
|
||
| namespace nix { | ||
|
|
||
| /** | ||
| * Provenance indicating that this store path was instantiated by the `derivation` builtin function. Its main purpose is | ||
| * to record `meta` fields. | ||
| */ | ||
| struct DerivationProvenance : Provenance | ||
| { | ||
| std::shared_ptr<const Provenance> next; | ||
| ref<nlohmann::json> meta; | ||
|
|
||
| DerivationProvenance(std::shared_ptr<const Provenance> next, ref<nlohmann::json> meta) | ||
| : next(std::move(next)) | ||
| , meta(std::move(meta)) {}; | ||
|
|
||
| nlohmann::json to_json() const override; | ||
| }; | ||
|
|
||
| } // namespace nix |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,6 +18,7 @@ | |
| #include "nix/fetchers/fetch-to-store.hh" | ||
| #include "nix/util/sort.hh" | ||
| #include "nix/util/mounted-source-accessor.hh" | ||
| #include "nix/expr/provenance.hh" | ||
|
|
||
| #include <boost/container/small_vector.hpp> | ||
| #include <boost/unordered/concurrent_flat_map.hpp> | ||
|
|
@@ -1504,6 +1505,8 @@ 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; | ||
|
|
@@ -1571,6 +1574,29 @@ 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(): | ||
|
|
@@ -1847,8 +1873,7 @@ static void derivationStrictInternal(EvalState & state, std::string_view drvName | |
| } | ||
|
|
||
| /* Write the resulting term into the Nix store directory. */ | ||
| auto drvPath = | ||
| writeDerivation(*state.store, *state.asyncPathWriter, drv, state.repair, false, state.evalContext.provenance); | ||
| auto drvPath = writeDerivation(*state.store, *state.asyncPathWriter, drv, state.repair, false, provenance); | ||
| auto drvPathS = state.store->printStorePath(drvPath); | ||
|
|
||
| printMsg(lvlChatty, "instantiated '%1%' -> '%2%'", drvName, drvPathS); | ||
|
|
@@ -5420,7 +5445,14 @@ 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. */ | ||
| v.mkInt(6); | ||
| 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); | ||
| } | ||
|
Comment on lines
+5448
to
+5455
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bumping the language version in an experimental feature seems pretty wrong to me... Especially because it means that if upstream bumps its version for an unrelated reason, we now have two diverging language versions. Why do we need to do this? What are the alternatives?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We need some sort of way to communicate that
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm more interested in what happens if upstream happens to bump the language version to v7 as well.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Then people not using DetNix and pass
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Alternatively, we could add a field
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One of the problems with having it as
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| addConstant( | ||
| "__langVersion", | ||
| v, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| #include "nix/expr/provenance.hh" | ||
| #include "nix/util/json-utils.hh" | ||
|
|
||
| #include <nlohmann/json.hpp> | ||
|
|
||
| namespace nix { | ||
|
|
||
| nlohmann::json DerivationProvenance::to_json() const | ||
| { | ||
| return nlohmann::json{ | ||
| {"type", "derivation"}, | ||
| {"meta", *meta}, | ||
| {"next", next ? next->to_json() : nlohmann::json(nullptr)}, | ||
| }; | ||
| } | ||
|
|
||
| Provenance::Register registerDerivationProvenance("derivation", [](nlohmann::json json) { | ||
| auto & obj = getObject(json); | ||
| std::shared_ptr<const Provenance> next; | ||
| if (auto p = optionalValueAt(obj, "next"); p && !p->is_null()) | ||
| next = Provenance::from_json(*p); | ||
| return make_ref<DerivationProvenance>(next, make_ref<nlohmann::json>(valueAt(obj, "meta"))); | ||
| }); | ||
|
|
||
| } // namespace nix |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,11 @@ | ||
| #include "nix/cmd/command.hh" | ||
| #include "nix/store/store-api.hh" | ||
| #include "nix/expr/provenance.hh" | ||
| #include "nix/store/provenance.hh" | ||
| #include "nix/flake/provenance.hh" | ||
| #include "nix/fetchers/provenance.hh" | ||
| #include "nix/util/provenance.hh" | ||
| #include "nix/util/json-utils.hh" | ||
|
|
||
| #include <memory> | ||
| #include <nlohmann/json.hpp> | ||
|
|
@@ -92,6 +94,60 @@ struct CmdProvenanceShow : StorePathsCommand | |
| } else if (auto subpath = std::dynamic_pointer_cast<const SubpathProvenance>(provenance)) { | ||
| logger->cout("← from file " ANSI_BOLD "%s" ANSI_NORMAL, subpath->subpath.abs()); | ||
| provenance = subpath->next; | ||
| } else if (auto drv = std::dynamic_pointer_cast<const DerivationProvenance>(provenance)) { | ||
| logger->cout("← with derivation metadata"); | ||
| #define TAB " " | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a reason not to define this closer to the top, and not undefine it?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, this was just how I did it heh. We could move it to the top, that's fine by me.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| 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:*:*:*:*:*:*", | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For my own knowledge: https://en.wikipedia.org/wiki/Common_Platform_Enumeration The
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes because there's no type part. |
||
| 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>()); | ||
| } | ||
| } | ||
| #undef TAB | ||
| provenance = drv->next; | ||
| } else { | ||
| // Unknown or unhandled provenance type | ||
| auto json = provenance->to_json(); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing error message in the force