Flake-Env: yet another direnv plugin for nix

Hi! Recently, nix-direnv (which I help maintain) has had a lot of issues opened because of the bash version or the core-tools dependencies we have. For instance, we recently started requiring GNU sort, but there are users out there with other sort implementations. There’s really very little that we can do to ensure the user’s experience is good in this scenario and that got me thinking about whether or not we could do better.

And then I started writing a new tool. I’m not sure it’s better yet, but I think I’m ready to announce flake_env.
Flake_env is a compiled version of nix-direnv. The logic is almost exactly the same, but there’s a lot less bash involved, so you can be reasonably sure that the binary will work with your setup. Overall, flake_env is much simpler than nix-direnv. It is flake-only by design and will not support legacy setups.

I know that this space is crowded already, but this has been a lot of fun to write and I think the path forward is a lot simpler than competing tools. I hope it helps some of you all. Find it at flake_env: *experimental* direnv plugin for nix flakes


Very nice! If I may ask, what led you to use Reason as opposed to OCaml?

Long story short: I started using ReasonML not long before the ReScript rebranding for unifying data type definitions across the server/client stack and got to like the syntax. I don’t come from a functional programming background and so found Reason to be closer to what I know and thus a bit easier to work in. I’ve since gotten a lot more comfortable with the OCaml syntax, but not quite comfortable enough to churn it out quickly.

You’ll notice that this application is “just” an OCaml application with Reason slapped on top (I’m not using any of the stuff targeting the JS backend or anything), so extensions could easily be written in OCaml without knowledge of the Reason syntax at all. All you’d have to fight with is the poorly documented bits of JaneStreet’s libraries and the bad rendering on the OCaml docs pages (not that I am upset about those at all or anything…)

1 Like

Nice, I’ll have to give that a try!

Bonus points also for using an actual free and open source git forge instead of GitHub!


I didn’t try this (yet), but I had to thank you right away for teaching me about the watch_file command (on the README example!)

1 Like

Heya, just wanted to check it out but I’m getting this error:

error: 'print-dev-env--profile/home/user/my/project/.direnvflake-tmp-profile.501071.' is not a recognised command

It seems like Lib.Util.nix isn’t putting in the spaces:

let nix = (args) => {
  let stdout_chan = Unix.open_process_in(
    "nix --extra-experimental-features \"nix-command flakes\" " ++ (args |> String.concat));
  let stdout_content = stdout_chan |> In_channel.input_all;
  let exit_code = Unix.close_process_in(stdout_chan);
  (exit_code, stdout_content)

I don’t really know ocaml, and I am confused by the docs reading val concat : string -> string list -> string. Isn’t the separator parameter missing? I’ll try to replace (args |> String.concat) with (String.concat " " args) and see if that fixes it.

Edit: Writing ocaml is pretty tough if you don’t realize the code is actually ReasonML :joy:

Anyways, this patch seems to work for me:

diff --git a/lib/flake_env__util.re b/lib/flake_env__util.re
index 2d9c062..d3024f0 100644
--- a/lib/flake_env__util.re
+++ b/lib/flake_env__util.re
@@ -3,7 +3,7 @@ module Unix = Core_unix;
 let nix = (args) => {
   let stdout_chan = Unix.open_process_in(
-    "nix --extra-experimental-features \"nix-command flakes\" " ++ (args |> String.concat));
+    "nix --extra-experimental-features \"nix-command flakes\" " ++ (String.concat(args, ~sep=" ")));
   let stdout_content = stdout_chan |> In_channel.input_all;
   let exit_code = Unix.close_process_in(stdout_chan);
   (exit_code, stdout_content)

(Sorry, never used sourcehut so idk how this would usually work and I’m in a bit of a rush. Hope this is helpful anyways.)

This is absolutely a bug and was caused by me misreading core’s documentation. I expected the separator default to be a space, but it is actually the empty string. I’m writing tests now and will definitely add one for this to avoid regressions. I can’t promise when those tests will be incoming, but they’ll probably show up slowly. Thanks for reporting and for being patient about a solution! I’m not quite yet dogfooding this, but that should be coming shortly too. My time for this is smaller now that I am back at work full time (this was a diversion during the winter holiday break I took this year), so progress will be slow (but steady, hopefully)

1 Like

A fix for this immediate issue has been pushed.
I do expect some additional problems to crop up in the near term as other people start using this more.

Please send additional bug reports to the mailing list found in the sr.ht project.

Because of the on-going issues with sr.ht, I have created GitHub - bbenne10/flake_env: *experimental* direnv plugin for flakes (READ ONLY mirror of https://sr.ht/~bryan_bennett/flake_env) as a read-only mirror. I am going to try and keep this synced with the sr.ht repo after everything resolves.

I wonder why all these tools use .envrc, seems kinda pointless if you already have shell.nix or devShells in flake.nix, is there alternative or option to not use .envrc?

Made alternative that does not use .envrc:

1 Like

The reason I am doing this is because that’s how direnv launches plugins and I wanted to make use of direnv (I use it for a LOT of other things). There’s obviously a way to do this less generally as you have shown (clever idea btw - I might steal some ideas from this)

1 Like

I think one particular thing that might be useful, is that instead of defining watches, you hash the git status output instead. You could offer this as extension in .direnv like “vcs_watch”

git status --porcelain=v2 2>/dev/null | (grep -Po '.*(?=\s+[^\s]+$)' 2>/dev/null || true)

Probably other vcses have similar thing to hash. Sadly the git status does not output absolute file paths and the file paths change depending on your CWD so I grep them out there … Anyhow, this lets you do change detection using the tool that’s already good at detecting changes :slight_smile:

1 Like