My painpoints with flakes

You can nix build a check

nix build .#checks.x86_64-linux.nix-format

but it would be more convenient, if like nix build with packages, it could handle the arch for you.

1 Like

Thanks, that helps indeed, though it would be nice to have it in the interface of nix flake check.

2 Likes

YIL (the Y stands for “yesterday”), that one can indeed pin entries in the registry declaratively!

  1. I had to make sure that the module system will see the inputs (not sure if this way was idiomatic though, but it works for now: provide inputs to the modules · NobbZ/nixos-config@7e4d374 · GitHub
  2. set nix.registry.<name>.flake to whatever one wants to pin for that registry entry: pin nixpkgs in flake registry · NobbZ/nixos-config@bda3cef · GitHub
1 Like

I use flakes to build my system, home-manager and now some dev-env.

I agree with all the comments on global registry:

  • strange content (projects and branches)
  • no way to remove unused stuff

Indeed I use the nix.registry.<name>.flake option to pin nixpkgs so that all references for my system and users is using the same version.

Basically my organization is:

  • One configuration flake containing:
    • All inputs to upstream branches (nixpkgs, home-manager, …) used to pin everything with the flake.lock
    • A custom package set for local stuff
    • An overlay to help combining all the packages
    • Some nixosModules.* for my custom services and to generalize my configs
    • Some nixosConfigurations.* to handle my system configurations
    • Some homeModules.* to generalize my home configs (following the nixos* pattern)
    • Some homeConfigurations.* to handle home-manager configs

Part of my packages a flake-mgr script to:

  • Update the lock file of all flake inputs
  • Build/Switch homeConfigurations
  • Build/Test/Boot/Switch nixosConfigurations

This handles my system and user persistent configurations.

For my dev-env, I write another flake depending on the first to see my overlay and pin all upstream flakes. I simply defines a devShell.

BTW: My flakes are currently not in a git repository, and it still works.

Pro Tip: when defining a dev-env, dont put your flake.nix file at the root … otherwise all the folder and subfolders will be pushed to the nix-store and caching will be very bad. I use:

  • Main Project
    • .envrc Direnv config using ./nix/flake.nix as a source
    • nix
      • git
      • flake.nix Used to pin deps, and provide CI context
      • flake.lock
    • app1 Git clone of a backend app
      • .git
      • nix Specific env for this app
        • default.nix
      • Cargo.toml
      • src
    • app2 Git clone of a frontend app
      • .git
      • nix Specific env for this app
        • default.nix
      • src
      • package.json
      • package.lock
2 Likes

I can’t reproduce…

{
  inputs.nixpkgs.url = "github:nixos/nixpkgs/unstable";

  outputs = { self, nixpkgs }:
    let
      pkgs = nixpkgs.packages.x86_64-linux;
    in
      {
        devShell.x86_64-linux = pkgs.mkShell {
          buildInputs = [ pkgs.go_1_14 pkgs.nixpkgs-fmt pkgs.lefthook pkgs.docker-compose pkgs.entr ];
        };
      };
}

This is my .flake.nix at the company project:

nix develop
warning: unknown setting 'extra-sandbox-paths'
error: --- Error ---------------------------------------------------------------------------------------------------------------------------------------------------------------- nix
source tree referenced by 'git+file:///home/nobbz/path/to/repo?ref=branch-name&rev=0e69a288c41b89dec8566b6d109f6c7a9e3fabaf' does not contain a '/flake.nix' file

If though I git add the flake.nix, it will ask for the flake.lock.

So maybe you can use flakes for things that aren’t in git yet, but I’m working with things that are already in git, and I’d prefer to have the dev shell defined through flakes, to not have to switch between niv and flake or even manually updating SHAs in the fetchGitHub or similar call depending on the flake…

1 Like

Ah yes, if it is inside a git repo, it is forcing the non dirty behavior

1 Like

Would you mind sharing the implementation of that runTests function? I’m struggling to find documentation for how to perform a check like cargo fmt -- --check or cargo clippy. It seems like the check has to be a derivation but I don’t know how to make a derivation that runs a shell command and then how/if flakes would respect the error code of the command.

1 Like

I don’t actually have a runTest function, that was just part of the simplification for the post, but you can take a look at the testing derivations:

1 Like

I’d like to address some points regarding the registry from your original post, because I have some workarounds for these problems. First of all I show the configuration for Nix:

# inputs are the flake inputs
{
  nix = {
    package = pkgs.nixFlakes;

    registry = { # (1)
      self.flake = inputs.self; # (2)
      nixpkgs = {
        from = {
          id = "nixpkgs";
          type = "indirect";
        };
        to = {
          owner = "NixOS";
          repo = "nixpkgs";
          rev = inputs.nixpkgs.rev; # (3)
          type = "github";
        };
      };
    };

    # (4)
    extraOptions = ''
      experimental-features = nix-command flakes ca-references
      flake-registry = /etc/nix/registry.json
    '';
  };
}
  1. Using nix.registry you can set entries in the local registry which is stored in /etc/nix/registry.json.

  2. First I register the system configuration itself as a flake in the registry under the name self. This prevents me from accidentally using it as a flake input elsewhere because the name self is reserved. At the same time I can export legacyPackages with all my local overlays from the self flake and get instant Nix shells.

  3. I could do the same for nixpkgs, i.e. nix.registry.nixpkgs.flake = inputs.nixpkgs;, however, that would write the local store path to the registry and when I then nix flake update in one of my other flakes it will write the store path into the lock file and others will not be able to build the flake anymore. Instead I just write the registry entry by hand but pin the rev to the revision of the nixpkgs that the system was built from.

  4. The global flakes registry can be configured in nix.conf and defaults to https://github.com/NixOS/flake-registry/raw/master/flake-registry.json. It accepts a URI or a local path. Unfortuantely, you can’t leave the field empty (flake-registry =) and settings it to /dev/null also doesn’t work because a special JSON format is expected. To this end I just use the local registry located at /etc/nix/registry.json again. Now all entries in my registry are duplicate but at least the global registry is gone.

4 Likes

After I originally posted my way to pin, I also read deeper into the documentation of nix.registry.* though I really didn’t understand the from and to attributes. Therefore I left everything as I have it.

Though I can not really understand your #3, as I never run nix flake update on other people projects and in my own projects I never rely on flake:nixpkgs but instead github:nixos/nixpkgs/<branch> to avoid any registry mismatches from the get-go.

I’m pretty sure I’ve said it several times, though missed it in this thread it seems. The registry is a nice thing when used on the terminal for quick accessing some often used repos. but in actual flake.nix I really wish flake:* URLs as well as implicit usage in the arguments to outputs function would be forbidden…

1 Like

You can build your flake from a path rather than VCS if you are explicit about it:

nix run path:$PWD
Hello World

# flake isn't in VCS -> fail
nix run git+file:$PWD
error: --- Error ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- nix
source tree referenced by 'git+file:/tmp/test?ref=master&rev=407a304d0991580a37cea8d53adefb65b447387b' does not contain a '/flake.nix' file
1 Like

What I wanted to express is that I’d like to have a flake.nix which is not version controlled within a git repository to define its corresponding dev-env. Currently I have to use nix-shell for my work related projects, though I’d prefer to have unified nix devel regardless if its work or private project.

1 Like

Agree with NobbZ. nix develop should relax the rules a little. Why should a flake.nix have to be in version control? At the moment I can’t have my flake.nix in .gitignore but must stage and commit it and only then is it happy. This feels like a workflow speedbump to any project that isn’t happy having flake.nix committed into version control… and try to remember not to push the committed flake.nix. Maybe I’m missing something but as a workflow it seems sub-optimal?

3 Likes

If I don’t want to add the flake.nix inside my git repo I just call this way:

nix develop path:$PWD

If flake.nix is in other folder I call this way:

nix develop path:/my/flake/folder
6 Likes

That way Nix will copy the entire tree into the Nix store: Copy local flakes to the store lazily · Issue #3121 · NixOS/nix · GitHub

I’d suggest to use Git pre-commit and post-commit hooks to unstage and restage the flake.{nix,lock} files.

I did notice that it only needs to be staged and not committed (maybe this is a bug?). Still seems a pretty funny requirement to me. If it’s not actually needed to be committed then where are we going with this? Seems like a song and dance over nothing.

I wasn’t aware that I can unstage/stage during the execution of those hooks. That mighht indeed be a viable solution for me.

Even though I had to hook that into our lefthook workflow without touching the YAML file or damaging the hookfiles created by lefthook…

The reason it needs to be staged is that flake evaluation will only look at files that are tracked by Git. Note that it’s the content of the file in the worktree that counts, not the staging area. In particular, you can git add -N the flake.nix file and it’ll be just fine, at least until the next commit.

While I’m at it, my solution when working with a project that does not provide a devshell is usually to put my custom devshell higher in the file hierarchy and to store the project repository under it. It works well with direnv which I use and also allows to share devshells between related projects and generally decouple devshells from projects. (Admittedly that might not be the original vision for devshells)

3 Likes

Very helpful, thanks!

The from attr in nix.registry definitions can be left out for indirect entries, so you can simply write:

registry = {
  self.flake = inputs.self;
  nixpkgs.to = {
    owner = "NixOS";
    repo = "nixpkgs";
    rev = inputs.nixpkgs.rev;
    type = "github";
  };
};

I use this:

registry = lib.mkForce({
  pkgs.flake = selfFlake;
} // lib.mapAttrs (names: flakes: { flake = flakes; }) inputs);

where selfFlake is the self of outputs in flake-parts.

Then I defined legacyPackages = (getSystem system).allModuleArgs.final; so pkgs will be the same variable I’m using in system flake.