nix-eval-jobs: --apply

Based off of https://github.com/NixOS/nix-eval-jobs/pull/332 and
9f292a402a6edf620b872bedaaad5f0a45c23826

closes https://git.lix.systems/lix-project/lix/issues/1214

Co-authored-by: Mic92 <joerg@thalheim.io>
Co-authored-by: Yannik Sander <yannik@floxdev.com>
Change-Id: Ifba7081b384d18fbdc47437de261d30b65a7930a
(cherry picked from commit 405ff2eb03)
This commit is contained in:
isabel
2026-07-13 17:12:40 +00:00
committed by eldritch horrors
parent c5f394c729
commit 6bc4807e64
7 changed files with 84 additions and 2 deletions
+3
View File
@@ -315,5 +315,8 @@ yorickvp:
yshui:
github: yshui
ysndr:
github: ysndr
zimbatm:
github: zimbatm
+12
View File
@@ -0,0 +1,12 @@
---
synopsis: "nix-eval-jobs support `--apply` flag"
cls: [5748]
category: "Features"
credits: [isabelroses,mic92,ysndr]
issues: [fj#1214]
---
`nix-eval-jobs` now supports the `--apply` flag. With this you can apply the
provided function to the each derivation, the result of this function will then
be serialized as a JSON value and stored inside `"extraValue"` key of the json
line output.
+1
View File
@@ -46,6 +46,7 @@ executable.
$ nix-eval-jobs --help
USAGE: nix-eval-jobs [options] expr
--apply Apply provided Nix function to each derivation. The result of this function will be serialized as a JSON value and stored inside `"extraValue"` key of the json line output.
--arg Pass the value *expr* as the argument *name* to Nix functions.
--argstr Pass the string *string* as the argument *name* to Nix functions.
--check-cache-status Check if the derivations are present locally or in any configured substituters (i.e. binary cache). The information will be exposed in the `isCached` field of the JSON output.
+12 -2
View File
@@ -12,7 +12,8 @@
#include "eval-args.hh"
MyArgs::MyArgs(nix::AsyncIoRoot & aio) : MixCommonArgs("nix-eval-jobs"), aio_(aio) {
MyArgs::MyArgs(nix::AsyncIoRoot &aio)
: MixCommonArgs("nix-eval-jobs"), aio_(aio) {
addFlag({
.longName = "help",
.description = "show usage information",
@@ -22,7 +23,8 @@ MyArgs::MyArgs(nix::AsyncIoRoot & aio) : MixCommonArgs("nix-eval-jobs"), aio_(ai
if (flag->hidden || hiddenCategories.count(flag->category)) {
continue;
}
std::cout << nix::fmt(" --%-20s %s\n", name, flag->description);
std::cout << nix::fmt(" --%-20s %s\n", name,
flag->description);
}
::exit(0);
}},
@@ -105,6 +107,14 @@ MyArgs::MyArgs(nix::AsyncIoRoot & aio) : MixCommonArgs("nix-eval-jobs"), aio_(ai
.description = "treat the argument as a Nix expression",
.handler = {&fromArgs, true}});
addFlag({.longName = "apply",
.description =
"Apply provided Nix function to each derivation. The result "
"of this function will be serialized as a JSON value and "
"stored inside `\"extraValue\"` key of the json line output.",
.labels = {"expr"},
.handler = {&applyExpr}});
// usually in MixFlakeOptions
addFlag({
.longName = "override-input",
@@ -22,6 +22,7 @@ struct MyArgs : virtual public nix::MixEvalArgs,
bool worker = false;
std::string releaseExpr;
std::string applyExpr;
nix::Path gcRootsDir;
bool flake = false;
bool fromArgs = false;
+16
View File
@@ -28,6 +28,7 @@
#include <lix/libexpr/symbol-table.hh>
#include <lix/libutil/types.hh>
#include <lix/libexpr/value.hh>
#include <lix/libexpr/value-to-json.hh>
#include <lix/libutil/terminal.hh>
#include <exception>
#include <numeric>
@@ -201,6 +202,21 @@ try {
maybeConstituents =
readConstituents(&v, state, evaluator);
}
if (args.applyExpr != "") {
nix::Expr &applyExpr = state->ctx.parseExprFromString(
args.applyExpr, nix::CanonPath::fromCwd());
nix::Value vApply = state->eval(applyExpr);
nix::Value vRes =
state->callFunction(vApply, v, nix::noPos);
state->forceAttrs(
vRes, nix::noPos,
"apply needs to evaluate to an attrset");
nix::NixStringContext context;
reply["extraValue"] = nix::printValueAsJSON(
*state, true, vRes, nix::noPos, context);
}
auto drv = Drv(attrPathS, *state, *drvInfo, args,
maybeConstituents);
reply.update(drv);
@@ -124,6 +124,45 @@ def test_eval_error() -> None:
assert "this is an evaluation error" in attr["error"]
def test_apply() -> None:
with TemporaryDirectory() as tempdir:
applyExpr = """drv: {
the-name = drv.name;
version = drv.version or null;
}"""
results, _ = evaluate(
tempdir,
0,
["--workers", "1", "--apply", applyExpr, "--flake", ".#hydraJobs"],
)
assert len(results) == 4 # sanity check that we assert against all jobs
# Check that nix-eval-jobs applied the expression correctly,
# exposing the result under the `extraValue` key, and extracted
# 'version' as 'version' and 'name' as 'the-name'
dotted_job = results[0]
assert dotted_job["attr"] == '"dotted.attr"'
assert dotted_job["extraValue"]["the-name"].startswith("hello-")
assert dotted_job["extraValue"]["version"] is not None
built_job = results[1]
assert built_job["attr"] == "builtJob"
assert built_job["extraValue"]["the-name"] == "job1"
assert built_job["extraValue"]["version"] is None
recurse_drv = results[2]
assert recurse_drv["attr"] == "recurse.drvB"
assert recurse_drv["extraValue"]["the-name"] == "drvB"
assert recurse_drv["extraValue"]["version"] is None
substituted_job = results[3]
assert substituted_job["attr"] == "substitutedJob"
assert substituted_job["extraValue"]["the-name"].startswith("hello-")
assert substituted_job["extraValue"]["version"] is not None
@pytest.mark.infiniterecursion
def test_recursion_error() -> None:
with TemporaryDirectory() as tempdir: