niv
niv copied to clipboard
Create default.nix during niv init
The niv init
command used to create a few files (default.nix
, shell.nix
, etc) which some people didn't like. Some people now complain that they want it back. I think the best solution here is to have niv init
create a single extra file, default.nix
, which
- shows how to
import nix/sources.nix
- builds a single (
pkgs.hello
) package, - potentially create a shell if
inNixShell
Why not just have options to niv init
? e.g. niv init --sources --default --shell
for everything.
I'll be honest, requiring extra options is less than ideal. Instead have nix init
check for a ./shell.nix
file and a ./nix/default.nix
file. If there is only a ./shell.nix
then create a ./nix/default.nix
that has comments at the top on how to import nix/sources.nix
. If there is already a ./nix/default.nix
, don't modify it and tell the user. Otherwise, generate everything.
I gave this some thoughts over the past few days and came up with this (feedback welcome):
niv init
generates nix/sources.json
, nix/sources.nix
and default.nix
(unless they exist, of course). The default.nix
would look like this:
let
sources = import ./nix/sources.nix;
pkgs = import sources.nixpkgs {};
in pkgs.hello
should be simple enough that people might actually use it, and still it showcases how to use niv.
I agree that having a simple enough example to build off of is a good idea. I would extend this idea by also generating a shell.nix
that imports everything from sources.nix
. However, I just realized if you wanted to import every attribute (package) into your buildInputs
then the way you generate sources.nix
would need to be adjusted to be {nixpkgs = nixpkgs; buildInputs = [snack niv]; }
instead of making each package a top level attribute. To be fair you could filter out nixpkgs
and import the rest, but it would complicate the resulting shell.nix
.
So I think there are two big use cases for niv
:
- A repository specific package that has a
default.nix
that defines just that package. - A repository full of nix expressions for doing various things, maybe including packages, library functions, etc.
Importing dependencies and using them in buildInputs
makes sense for the first kind and no sense at all for the second kind (e.g. nixpkgs). I don't think we should make assumptions about what people put in there, so I'm against doing anything with a source except importing it as a (mystery) attribute.
Why not have both? Keep the existing behavior of sources.nix
and just tack on an extra attribute called buildInputs
to make creating a shell.nix
easy (or have niv generate the shell.nix with an extra flag). So you could do:
{ sources ? ./nix/sources.nix }:
pkgs.mkShell {
buildInputs = (import sources).buildInputs;
}
I end up setting up every repo I use niv
in using an interesting method. I originally took inspiration for it from @nmattia's homies and have taken small ideas from other repos as well. I have a nix/default.nix
that essentially acts as the standard "prelude" import. That is, let pkgs = import ./nix {};
imports the pinned nixpkgs. However, I also end up defining any overlays, nixpkgs configuration, and "globally available util stuff" in nix/default.nix
which makes my life really convenient.
Recently, I've also taken to adding "sources" as an overlay so I can do pkgs.sources.my-niv-managed-dependency
but I only do that to save on line-noise in the rest of the nix files.
So it seems to me that default.nix
and shell.nix
aren't really the biggest things I end up setting up every time but actually nix/default.nix
. Specifically, something like:
# nix/default.nix
{ sources ? import ./sources.nix, system ? builtins.currentSystem }:
let
srcs = self: super: { inherit sources; };
overlays = [ srcs ];
config = { };
in import sources.nixpkgs { inherit overlays config system; }
is essentially my starter boilerplate in nix/
. Then, should niv
want to bootstrap the default and the shell, they can be:
# default.nix (at repo root)
let pkgs = import ./nix {};
in pkgs.hello
# shell.nix (at repo root)
let pkgs = import ./nix {};
in pkgs.stdenv.mkShell {
buildInputs = [ pkgs.hello ];
}
How well do you think that pattern would work?
One thing I'd be concerned about is adding a bunch of extra utility to the nix/default.nix
or nix/sources.nix
, but only really for keeping maintenance burden to a minimum.
edit: updated proposed default.nix to account for system.
👍 , I think that's close to the perfect nix/default.nix
! One thing that we may add as well is a system
parameter, if you want to build stuff on a difference architecture (through a remote builder):
{ sources ? ...
, system ? builtins.currentSystem
}:
...
in sources.nixpkgs { inherit overlays config system; }
I think we should just go for it, maybe hiding it behind a flag (e.g. niv init --full
).
I find injecting the niv
sources into nixpkgs
via an overlay feels somewhat unhygienic: the sources aren't packages, and I usually want to manage them separately. A matter of taste, I guess, I appreciate the appeal of "everything is all in one attribute set".
I want something different from nix/default.nix
, which is just to give me a shortcut to precisely the bits managed by niv
, i.e. sources.nix
. So it's usually exactly import sources.nix
.
(One small rant: importing nixpkgs
is pretty non-trivial (it has a lot of significant arguments), and the number of things that do it wrong (e.g. not passing system
as you're doing here), is high. I really wish tools would let me do it the way I want to do it and pass it in, rather than trying to import nixpkgs
for me and doing it wrong.)
@michaelpj the nix flakes branch actually does a lot of that for you, they replace 80% of what niv does (sorry nmattia), creates a standard entrypoint for repositories, handles system
and a few other things. You can test it by setting nix.package = pkgs.nixFlakes;
in NixOS.
(One small rant: importing
nixpkgs
is pretty non-trivial (it has a lot of significant arguments), and the number of things that do it wrong (e.g. not passingsystem
as you're doing here), is high.
You're absolutely right, my apologies; I didn't actually know I should include system as I've only seen it done once in someone's config online. Is there a wiki page for "how to import nixpkgs properly" or is it a sort of cult knowledge that's acquired by making mistakes over time?
I really wish tools would let me do it the way I want to do it and pass it in, rather than trying to import
nixpkgs
for me and doing it wrong.)
Fortunately, even if niv
had a default.nix, it can easily be replaced or modified to suit your needs.
Although, since niv includes nixpkgs by default in its init, that actually suggests to me that it's even more important that niv's init also come with a way to fully correctly import nixpkgs. Otherwise it's far too easy to just make the mistake of writing the naive let sources = import ./nix/sources.nix; pkgs = import sources.nixpkgs {};
and then, whoops, that'll no longer work correctly on mac systems as you've pointed out.
I know about flakes :) my feeling is rather that niv
does 80% of what flakes do...
Is there a wiki page for "how to import nixpkgs properly" or is it a sort of cult knowledge that's acquired by making mistakes over time?
I got it the latter way :sweat_smile:
Although, since niv includes nixpkgs by default in its init, that actually suggests to me that it's even more important that niv's init also come with a way to fully correctly import nixpkgs.
Is it? I like that niv
does one thing: get me a set of sources. It could do more, and be a full-fledged Nix project scaffolding manager. But I, personally, would prefer if it continued just doing the one thing well.
Hey guys, would love your feedback on https://github.com/nmattia/niv/issues/272