From 0c2ced0224bd419b7b792b3935846177fe76b251 Mon Sep 17 00:00:00 2001 From: Martin Fischer Date: Mon, 24 Mar 2025 19:48:07 +0200 Subject: [PATCH] feat(nix-instantiate): add --raw flag The experimental `nix eval` command already supports a `--raw` flag. This commit implements the same flag for the stable nix-instantiate command. Until now instructions and scripts that didn't want to rely on experimental features had to use workarounds such as: nix-instantiate --eval | tr -d \" (which also undesirably also removes double quotation marks within the string), or nix-instantiate --eval | jq -j (which undesirably depends on another package). Co-authored-by: Raito Bezarius Co-authored-by: Silvan Mosberger Change-Id: Iced9a80ee7edd60af2385c5193485f1774175339 --- doc/manual/change-authors.yml | 7 +++++++ doc/manual/rl-next/nix-instantiate-raw.md | 12 ++++++++++++ doc/manual/src/command-ref/nix-instantiate.md | 7 ++++++- lix/legacy/nix-instantiate.cc | 10 ++++++++-- tests/functional/eval.nix | 2 +- tests/functional/eval.sh | 15 ++++++++++++--- 6 files changed, 46 insertions(+), 7 deletions(-) create mode 100644 doc/manual/rl-next/nix-instantiate-raw.md diff --git a/doc/manual/change-authors.yml b/doc/manual/change-authors.yml index 142686d82..193c087c7 100644 --- a/doc/manual/change-authors.yml +++ b/doc/manual/change-authors.yml @@ -101,6 +101,9 @@ ian-h-chamberlain: forgejo: ian-h-chamberlain github: ian-h-chamberlain +infinisil: + github: infinisil + isabelroses: forgejo: isabelroses github: isabelroses @@ -158,6 +161,10 @@ midnightveil: ncfavier: github: ncfavier +not-my-profile: + display_name: Martin Fischer + github: not-my-profile + p-e-meunier: display_name: Pierre-Etienne Meunier github: P-E-Meunier diff --git a/doc/manual/rl-next/nix-instantiate-raw.md b/doc/manual/rl-next/nix-instantiate-raw.md new file mode 100644 index 000000000..79b9281e6 --- /dev/null +++ b/doc/manual/rl-next/nix-instantiate-raw.md @@ -0,0 +1,12 @@ +--- +synopsis: "Add --raw flag to `nix-instantiate --eval` for unescaped output" +issues: [] +prs: [gh#12119] +cls: [2886] +category: Improvements +credits: [not-my-profile, infinisil, raito] +--- + +The `nix-instantiate --eval` command now supports a `--raw` flag. When used, +the result must be coercible to a string (as with `${...}`) and is printed +verbatim, without quotes or escaping. diff --git a/doc/manual/src/command-ref/nix-instantiate.md b/doc/manual/src/command-ref/nix-instantiate.md index c3f3da227..c0c025205 100644 --- a/doc/manual/src/command-ref/nix-instantiate.md +++ b/doc/manual/src/command-ref/nix-instantiate.md @@ -5,7 +5,7 @@ # Synopsis `nix-instantiate` - [`--parse` | `--eval` [`--strict`] [`--json`] [`--xml`] ] + [`--parse` | `--eval` [`--strict`] [`--raw`] [`--json`] [`--xml`] ] [`--read-write-mode`] [`--arg` *name* *value*] [{`--attr`| `-A`} *attrPath*] @@ -107,6 +107,11 @@ See that section for complete details (`nix-build --help`), but in summary, a pa > This option can cause non-termination, because lazy data > structures can be infinitely large. + - `--raw` + When used with `--eval`, the result must be coercible to a string, i.e., + something that can be converted using `${...}`. The output is + printed exactly as-is, with no quotes, escaping, or trailing newline. + - `--json`\ When used with `--eval`, print the resulting value as an JSON representation of the abstract syntax tree rather than as a Nix expression. diff --git a/lix/legacy/nix-instantiate.cc b/lix/legacy/nix-instantiate.cc index 9f19d5024..8f97c814d 100644 --- a/lix/legacy/nix-instantiate.cc +++ b/lix/legacy/nix-instantiate.cc @@ -22,7 +22,7 @@ static Path gcRoot; static int rootNr = 0; -enum OutputKind { okPlain, okXML, okJSON }; +enum OutputKind { okPlain, okRaw, okXML, okJSON }; void processExpr(EvalState & state, const Strings & attrPaths, bool parseOnly, bool strict, Bindings & autoArgs, @@ -48,7 +48,11 @@ void processExpr(EvalState & state, const Strings & attrPaths, vRes = v; else state.autoCallFunction(autoArgs, v, vRes, noPos); - if (output == okXML) + if (output == okRaw) + std::cout << *state.coerceToString(noPos, vRes, context, "while generating the nix-instantiate output", StringCoercionMode::Strict); + // We intentionally don't output a newline here. The default PS1 for Bash in NixOS starts with a newline + // and other interactive shells like Zsh are smart enough to print a missing newline before the prompt. + else if (output == okXML) printValueAsXML(state, strict, location, vRes, std::cout, context, noPos); else if (output == okJSON) { printValueAsJSON(state, strict, vRes, noPos, std::cout, context); @@ -130,6 +134,8 @@ static int main_nix_instantiate(AsyncIoRoot & aio, std::string programName, Stri gcRoot = getArg(*arg, arg, end); else if (*arg == "--indirect") ; + else if (*arg == "--raw") + outputKind = okRaw; else if (*arg == "--xml") outputKind = okXML; else if (*arg == "--json") diff --git a/tests/functional/eval.nix b/tests/functional/eval.nix index befbd17a9..cabf28c29 100644 --- a/tests/functional/eval.nix +++ b/tests/functional/eval.nix @@ -1,5 +1,5 @@ { int = 123; - str = "foo"; + str = "foo\nbar"; attr.foo = "bar"; } diff --git a/tests/functional/eval.sh b/tests/functional/eval.sh index ae6fcec63..0834efbf4 100644 --- a/tests/functional/eval.sh +++ b/tests/functional/eval.sh @@ -15,13 +15,17 @@ nix eval --expr 'assert 1 + 2 == 3; true' nix eval -E 'assert 1 + 2 == 3; true' [[ $(nix eval int -f "./eval.nix") == 123 ]] -[[ $(nix eval str -f "./eval.nix") == '"foo"' ]] -[[ $(nix eval str --raw -f "./eval.nix") == 'foo' ]] +[[ $(nix eval str -f "./eval.nix") == '"foo\nbar"' ]] +[[ $(nix eval str --raw -f "./eval.nix") == $'foo\nbar' ]] [[ "$(nix eval attr -f "./eval.nix")" == '{ foo = "bar"; }' ]] [[ $(nix eval attr --json -f "./eval.nix") == '{"foo":"bar"}' ]] [[ $(nix eval int -f - < "./eval.nix") == 123 ]] [[ "$(nix eval --expr '{"assert"=1;bar=2;}')" == '{ "assert" = 1; bar = 2; }' ]] +# Non-coercible values throws errors under `--raw` +topLevelInteger="$(expectStderr 1 nix eval int --raw -f "./eval.nix")" +[[ "$topLevelInteger" =~ "error: cannot coerce an integer to a string: 123" ]] + # Top-level eval errors should be printed to stderr with a traceback. topLevelThrow="$(expectStderr 1 nix eval --expr 'throw "a sample throw message"')" [[ "$topLevelThrow" =~ "a sample throw message" ]] @@ -36,12 +40,17 @@ outputOfNestedThrow="$(nix eval --expr '{ throws = throw "a sample throw message nix-instantiate --eval -E 'assert 1 + 2 == 3; true' [[ $(nix-instantiate -A int --eval "./eval.nix") == 123 ]] -[[ $(nix-instantiate -A str --eval "./eval.nix") == '"foo"' ]] +[[ $(nix-instantiate -A str --eval "./eval.nix") == '"foo\nbar"' ]] +[[ $(nix-instantiate -A str --raw --eval "./eval.nix") == $'foo\nbar' ]] [[ "$(nix-instantiate -A attr --eval "./eval.nix")" == '{ foo = "bar"; }' ]] [[ $(nix-instantiate -A attr --eval --json "./eval.nix") == '{"foo":"bar"}' ]] [[ $(nix-instantiate -A int --eval - < "./eval.nix") == 123 ]] [[ "$(nix-instantiate --eval -E '{"assert"=1;bar=2;}')" == '{ "assert" = 1; bar = 2; }' ]] +# Non-coercible values throws errors under `--raw` +topLevelInteger="$(expectStderr 1 nix-instantiate -A int --raw "./eval.nix")" +[[ "$topLevelInteger" =~ "error: expression was expected to be a derivation or collection of derivations, but instead was an integer" ]] + # Check that symlink cycles don't cause a hang. ln -sfn cycle.nix $TEST_ROOT/cycle.nix (! nix eval --file $TEST_ROOT/cycle.nix)