Unflake: flake dependencies for non-flake projects (and a way to stop writing .follows)

Hi!

I’m announcing the first public version of unflake. It is a tool that allows non-flake projects to depend on flakes while unifying all the transitive dependencies. You can use it to avoid writing a lot of .inputs.nixpkgs.follows while still getting deduplicated dependencies; or to make dev shells for external projects without commiting flake.nix to the repo or copying the whole directory into store.

In a nutshell, it parses the same inputs format flakes use, enumerates all the inputs it needs (including transitive ones) and produces a Nix expression that fetches all these inputs, passes them their inputs and hands them to you. If your flake.nix looked like this

{
  inputs = ...;
  outputs = inputs: # your code
}

you can replace it with an unflaked version that looks like this:

let inputs = import ./unflake.nix;
in # your code

The rest of your code stays exactly the same.

unflake ignores flake.lock files in your dependencies: with unflake, you only get one copy of every distinct input. (There’s even a way to deduplicate it further, e.g. merge github:nixos/nixpkgs/nixos-unstable with github:nixos/nixpkgs/nixpkgs-unstable — check README for that). All the dependencies (including transitive ones) are locked at the top level — unflake can do it itself or integrate with npins.

It’s a first public release, so there’re probably some bugs (I’d appreciate bug reports!), but I use it for my own system closure which has a lot of dependencies and it works fine. README has more info and I’m happy to answer any questions you might have!

33 Likes

It’s also a proof of concept that emulating flakes is feasible. While flake-the-format is most probably here to stay, we’re not locked into any specific implementation of flakes and we can explore this design space even outside the Nix implementations. I’d be happy to see more projects in this space!

12 Likes

Amazing userland implementation of “flake” !!!

Testing it on my drupol/infra repo at testing: remove flake by drupol · Pull Request #122 · drupol/infra · GitHub

I am currently finding out the commands that I used to use with Flake (e.g., nh os switch -a -u ., nix develop, nix run .#<package>, etc etc), and then I think I’ll merge this.

3 Likes

some related work:

4 Likes

I’ve seen flake-compat, but I haven’t seen flake-inputs before, thanks!

One substantial difference is that both flake-compat and flake-inputs (which I think is an extended fork of flake-compat?) operate on flake.locks, so they use the default resolver. unflake instead ignores any flake.lock files and reimplements the resolver + locking to avoid dependency duplication.

Both unflake and flake-compat / flake-inputs implement flake runtime (i.e. code to import flakes from non-flake code).

3 Likes

This is really interesting! I’ve thought about re-implementing flakes in userland for a while, cool to see it be pulled off! I didn’t have time to dive into the code yet, but from what I understand, you don’t implement pure evaluation, is that right?

I think it should work with pure evaluation (I specify narHash for every input), but I didn’t have time to test it properly yet. Working in pure evaluation mode is a goal though, so I’d consider it a bug if it’s broken.

Ah sorry, I meant pure evaluation of the “flake”, where files that are outside the directory of flake.nix/unflake.nix or not tracked by git are invisible to the evaluator. This is implemented in nix by copying the entire directory (excluding untracked files) to the store first and then evaluating the flake there. It’s possible to do it another way, but this isn’t supported by nix natively at the moment.

Ah, no, that’s not something unflake provides. I’m not sure it’s something we want to provide, but I’m at least open to possibility of integrating with your nix-instantiate-pure if requested. It feels somewhat outside of the scope of the project and I’d be curious to know what the use case is.

1 Like

Do you intend to keep using the dendritic pattern and flake-parts but without (directly) using flakes? Unflake being able to do that is very interesting.

Yes. That’s probably what I will be using.

Currently, I’m testing things out, I need to have a mapping of the commands I used to run when using flake, without flake.

1 Like

Ah h’mmm I’m sure there was a name for that…

6 Likes