Get flake input store path


As in the title; how can I get a flake input’s store path?

Thank you kindly for the help!

1 Like

Any attributeset in Nix containing an outPath attribute can be coerced to a string with toString or via string interpolation, or even automatically if used in a context where a string is expected. For example:

  f = { outPath = "/foo"; };
"${f}" + toString f + f

would result in the string "/foo/foo/foo"

Flake inputs contain this outPath attribute, which is a path to their source directory in the nix store, so you can get the path any of the above mentioned ways.

1 Like

Sorry; I meant via command-line. Or do I have to use nix instantiate --eval (or was it --expr…)?

1 Like
$ nix flake metadata nixpkgs --json | jq .path
$ nix flake metadata github:nobbz/nixos-config --json | jq .path

Are there any performance differences or other downsides if I use this, with flake-compat?

nix eval --impure --expr "(import ./.).inputs.input.outPath"

I’d prefer to do it this way if possible as this way I don’t need to have flakes enabled.

I expose the inputs of my flake in its outputs, just for easy access.


Actually, I think that happens automatically… I don’t have to manually add the inputs to the outputs. Can anyone else confirm this?

1 Like

This is probably true for flake-compat, but not for flakes proper. You can access flake inputs in a nix repl if you import the flake, but you cannot build outputs of your inputs unless you specifically re-export them.

1 Like

They’re automatically exposed and you can refer to an input’s inputs in exactly the same way. No need to engineer anything here.

1 Like

Just to answer this question yes, Nix has an eval cache when using flake based commands. The current implementation of that cache is heavily tied to flake semantics, so flake-compat does not benefit from the evaluation cache at all and will eval from scratch every time.

You mean via flake-compat? This is not true for flake based commands, e.g. one cannot do nix build

1 Like

Ah, got it; I was referring to the use of builtins.getFlake, where the result has an inputs attribute. Also, it doesn’t seem to be too much of a performance differences; plus, the backwards-compatibility when not using flakes is nice.

Oh if you want them with flakes compat you have to tweak the implementation a bit. Depending on your use case ( a direct input vs recursive ) you can literally do getInput = url: id: let with builtins; let lock = fromJSON ( readFile ( fetchurl url ).outPath + "/flake.lock") ); follow = i: if ( lock.inputs.${i}.type == "indirect" ) then ( follow lock.inputs.${i}.id ) else lock.inputs.${i}; in fetchTree ( removeAttrs ( follow id ) ["flake"] )

I forgot the exact structure of the lock, but you get the idea. You can add extra handling for edge cases, for example if you can’t find an indirect id in a lock fall back to getFlake to resolve with the registry, and yank the lock from the outPath. You can also merge multiple locks to mimic follows or do other complex resolutions.

This is more or less what flakes compat does, except that it implements fetchTree from scratch to provide backwards compatibility to old versions of Nix - which you might not need or you can just copy.

Flakes compat is hard coded to only return packages and shells but you can easily tweak it to return arbitrary fields.

If your goal is just to forward your inputs as outputs, you can access self.inputs and extend it with fields merged from reading your own lock using fromJSON ( readFile ( ( toString ./. ) + "/flake.lock" ) ) or self.sourceInfo.outPath.

Try to remember that under all the magic, flakes are literally just parsing JSON and fetching using that info. That’s why flakes compat works in legacy versions of Nix in the first place.

1 Like

You can’t see them from the CLI but you can pretty easily write a routine that forwards your inputs’ outputs. I did this early on when flakes were new; I would basically do lib.recursiveUpdate with a special case for overlays.

If you just care about a specific field you can do outputs = inputs: { packages = with inputs.nixpkgs.lib; with builtins; foldl' ( acc: i: recursiveUpdate acc ( i.packages or {} ) { foo = derivation {...}; } ( attrValues ( removeAttrs inputs ["nixpkgs" "self" "whatever other registry stuff you want or just explicitly name the inputs to forward"] ); };

I don’t actually recommend this in most cases, because it’s basically what overlays and registries do a better job of; but you definitely can forward inputs.

If you want the nix CLI to do this, make a wrapper script using nix eval --expr or nix build --expr.

1 Like

If you use builtins.getFlake, beware that something like builtins.getFlake (toString ./.) will not do the proper thing if you do it in a Git checkout — instead, it will recursively copy the whole contents of the current directory, including the .git subdirectory, into the Nix store. This may result in leaking whatever authentication tokens you may have inside .git (e.g., if you use GitHub Actions, by default $GITHUB_TOKEN is there, which would at least be invalidated after the workflow has completed, but if another token was specified during checkout, the leak of that token may have much worse consequences).

In my case I needed to use something like this instead:

flake_url="$( nix flake metadata --json | jq -r '.url' )"
current_system="$( nix eval --raw --impure --expr 'builtins.currentSystem' )"
nix shell --expr "(builtins.getFlake \"$flake_url\").inputs.nixpkgs.legacyPackages.\"$current_system\".nix-build-uncached" \
  -c nix-build-uncached $drv_list || build_result=$?
1 Like

Oh, dear; I will try to keep this in mind. Thanks for the heads up!