Problem understanding how this runCommand may work

Hello all,

I’m trying to understand how the bash script of a runCommand invocation may work. Specifically, I don’t understand how the test at this line may work: https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/kubernetes/certs.nix#L45

I’ll copy here the relevant lines:

   writeCFSSL = content:
     pkgs.runCommand content.name {
      buildInputs = [ pkgs.cfssl pkgs.jq ];
     } ''
       mkdir -p $out
       cd $out

       json=${pkgs.lib.escapeShellArg (builtins.toJSON content)}

       # for a given $field in the $json, treat the associated value as a
       # file path and substitute the contents thereof into the $json
       # object.
       expandFileField() {
         local field=$1
         if jq -e --arg field "$field" 'has($field)'; then
           local path="$(echo "$json" | jq -r ".$field")"
           json="$(echo "$json" | jq --arg val "$(cat "$path")" ".$field = \$val")"
         fi
       }

       expandFileField key
       expandFileField ca
       expandFileField cert

       echo "$json" | cfssljson -bare ${content.name}
     '';

My understanding problem is with the test at the line if jq -e --arg field "$field" 'has($field)'.
From what i see from the documentation of the jq utility it expects the JSON formatted data either as a filename on the command line or as stdin (you can find jq docs here: Redirecting to jqlang.github.io ).

So, if jq needs a JSON doc on stdin, how can that test work?

I’ve also investigated how runCommand is defined, and it seems just a thin layer over mkDerivation and there seems not to be any stdin external handling.

anyone has a clue?

1 Like

You are right, that stdin isn’t available in runCommand builder

$ time nix-build -E 'with import <nixpkgs>{}; runCommand "test" {} "echo before stdin; while read -t 5 line; do echo reading stdin; done; echo after stdin"'
these derivations will be built:
   /nix/store/60wirv2nffqpyhy6ns4j5xr20ka611ws-test.drv
building '/nix/store/60wirv2nffqpyhy6ns4j5xr20ka611ws-test.drv'...
before stdin
after stdin
builder for '/nix/store/60wirv2nffqpyhy6ns4j5xr20ka611ws-test.drv' failed to produce output path '/nix/store/g98m755hz6s7cwrxvsc6snfm9lzcxliw-test'
error: build of '/nix/store/60wirv2nffqpyhy6ns4j5xr20ka611ws-test.drv' failed

real    0m0.297s
user    0m0.107s
sys     0m0.021s

I think this is Nix who closes stdin.

So, the

 if jq -e --arg field "$field" 'has($field)'; then

acts like it is piped with < /dev/null, essentially does nothing. This looks like logic flaw, and indeed, echo "$json" | may be missing