Skip to content

Commit

Permalink
feat: add --apply flag
Browse files Browse the repository at this point in the history
Allow applying every eval'ed drv to a provided nix expression to evaluate arbitrary fields.

EXAMPLE

```
% ./result/bin/nix-eval-jobs --flake github:NixOS/patchelf#hydraJobs \
  --apply \
  'drv: {
     version = if drv ? version then drv.version else null;
   }'
warning: `--gc-roots-dir' not specified
{"attr":"coverage","attrPath":["coverage"],"version":null}
{"attr":"patchelf-win32","attrPath":["patchelf-win32"],"version":"0.18.0"}
{"attr":"patchelf-win64","attrPath":["patchelf-win64"],"version":"0.18.0"}
{"attr":"release","attrPath":["release"],"version":null}
{"attr":"tarball","attrPath":["tarball"],"version":"0.18.0"}
```
  • Loading branch information
ysndr committed Oct 16, 2024
1 parent a3307ac commit fa997f1
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 8 deletions.
6 changes: 6 additions & 0 deletions src/eval-args.cc
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@ MyArgs::MyArgs() : MixCommonArgs("nix-eval-jobs") {
.description = "treat the argument as a Nix expression",
.handler = {&fromArgs, true}});

addFlag(
{.longName = "apply",
.description = "apply the derivation to the provided Nix expression",
.labels = {"expr"},
.handler = {&applyExpr}});

// usually in MixFlakeOptions
addFlag({
.longName = "override-input",
Expand Down
1 change: 1 addition & 0 deletions src/eval-args.hh
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class MyArgs : virtual public nix::MixEvalArgs,
virtual public nix::RootArgs {
public:
std::string releaseExpr;
std::string applyExpr;
nix::Path gcRootsDir;
bool flake = false;
bool fromArgs = false;
Expand Down
40 changes: 32 additions & 8 deletions src/worker.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <nix/attr-path.hh>
#include <nix/local-fs-store.hh>
#include <nix/installable-flake.hh>
#include <nix/value-to-json.hh>
#include <sys/resource.h>
#include <nlohmann/json.hpp>
#include <stdio.h>
Expand Down Expand Up @@ -130,22 +131,45 @@ void worker(nix::ref<nix::EvalState> state, nix::Bindings &autoArgs,
if (v->type() == nix::nAttrs) {
auto packageInfo = nix::getDerivation(*state, *v, false);
if (packageInfo) {
auto drv = Drv(attrPathS, *state, *packageInfo, args);
reply.update(drv);

if (args.applyExpr != "") {
auto applyExpr = state->parseExprFromString(
args.applyExpr, state->rootPath("."));

nix::Value vApply;
nix::Value vRes;

state->eval(applyExpr, vApply);

state->callFunction(vApply, *v, vRes, nix::noPos);
state->forceAttrs(
vRes, nix::noPos,
"apply needs to evaluate to an attrset");

nix::NixStringContext context;
std::stringstream ss;
nix::printValueAsJSON(*state, true, vRes, nix::noPos,
ss, context);

reply.update(nlohmann::json::parse(ss.str()));
} else {
auto drv = Drv(attrPathS, *state, *packageInfo, args);
reply.update(drv);
}

/* Register the derivation as a GC root. !!! This
registers roots for jobs that we may have already
done. */
if (args.gcRootsDir != "") {
auto localStore =
state->store
.dynamic_pointer_cast<nix::LocalFSStore>();
auto storePath = *packageInfo->queryDrvPath();

nix::Path root =
args.gcRootsDir + "/" +
std::string(nix::baseNameOf(drv.drvPath));
std::string(nix::baseNameOf(storePath.to_string()));
if (!nix::pathExists(root)) {
auto localStore =
state->store
.dynamic_pointer_cast<nix::LocalFSStore>();
auto storePath =
localStore->parseStorePath(drv.drvPath);
localStore->addPermRoot(storePath, root);
}
}
Expand Down
28 changes: 28 additions & 0 deletions tests/test_eval.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,34 @@ def test_eval_error() -> None:
assert "this is an evaluation error" in attrs["error"]


def test_apply() -> None:
with TemporaryDirectory() as tempdir:
applyExpr = """drv: {
the-name: drv.name;
version: builtins.tryEval drv.version or null;
}"""

cmd = [str(BIN), "--gc-roots-dir", tempdir, "--apply", applyExpr]
res = subprocess.run(
cmd,
cwd=TEST_ROOT.joinpath("assets"),
text=True,
check=True,
stdout=subprocess.PIPE,
)

print(res.stdout)
results = [json.loads(r) for r in res.stdout.split("\n") if r]
assert results[0]["the-name"] == "job1"
assert results[0]["version"] is None
assert results[1]["the-name"].startswith("nix-")
assert results[1]["version"] is not None
assert results[2]["the-name"] == "drvB"
assert results[2]["version"] is None
assert results[3]["the-name"].startswith("nix-")
assert results[3]["version"] is not None


@pytest.mark.infiniterecursion
def test_recursion_error() -> None:
with TemporaryDirectory() as tempdir:
Expand Down

0 comments on commit fa997f1

Please sign in to comment.