How should an automatic tool install packages?

Why do we have two ways to install a program on NixOS? Some programs, you can just add them to environment.systemPackages. Others (that require additional setup like Systemd services or other configuration) require NixOS options to be installed. I’m trying to build a tool that hides Nix’s complexity behind what looks like a normal package manager, so I’m having trouble knowing when to use environment.systemPackages or when I should use programs..enable or even when I should use services.. enable

Is there any way I can efficiently get a list of options to use for a package install? Maybe some other SQL database I can query (like command-not-found does)? Or maybe there is a quick way to query the list of options locally?

environment.systemPackages is kind of a generic list of packages that are supposed to be available in the PATH. Some other parts of NixOS also rely on it, for example Desktop-files listed in here will be shown in a generic application menu in common GUIs.
programs.*.enable is for programs that need special treatment, like ping or mtr (both need setuid wrappers), etc. Or that have some common configuration that might be useful, like an ssh agent that should be started automatically.

You can query the options locally if you have an uptodate channel for example through nixos-option.

What’s a good way to script with that? Maybe I can use nix-instantiate? I want to hide all of this behind something like sudo cpkg install foo. It will first check if foo is a command or a package name using the command-not-found db. Let’s assume it’s a package. It then checks if packages.foo.enable is an option. Then it will check if services.foo.enable is an option. Then it will check if nixpkgs.foo is a thing, then it will check the NUR and other package sets the user has installed. Finally it decides which method of installation to use (NixOS option, nixpkgs, or NUR) and installs it.

In order to pull this off effectively I need a way to query NixOS options quickly and in a simple format (JSON?).

I also need a way to query home-manager options, since cpkg will work with home-manager if it is run without sudo.

NOTE: cpkg = my tool for simplifying nix

Seems like you notion of “install” is from apt-like distros.

But did you think about why there are declarative and imperative ways to install a package? configuration.nix and nix-env?

Now you try to combine both approaches under single interface sudo cpkg install foo

Will this work by modifying configuration.nix in a magical way? Or would it be separated from configuration?

I’m a bit skeptical about proposed tool, but I’ll put my answers here.

Some programs, you can just add them to environment.systemPackages. Others (that require additional setup like Systemd services or other configuration) require NixOS options to be installed.

That’s easy. There exists two NixOS options:

  • environment.systemPackages
  • systemd.packages

Packages specified in environment.systemPackages will change only nix-profile and PATH, packages specified in systemd.packages will change /etc/systemd only. That’s separation of concerns. But usually specifying package in systemd.packages is not enough - you have to setup separate user, var-dir, conf files, probably interconnections with other services… which goes out of managing /etc/systemd.

I’m trying to build a tool that hides Nix’s complexity behind what looks like a normal package manager

Oh, c’mon. That’s “normal” package manager tries to hide complexity, while NixOS makes it explicit, but declarative and reproducible.

or when I should use programs…enable

I think, that the problem “which to chose to install a package” is a legitimate user question, but The User of NixOS doesn’t face existing solutions and their decision consequences.

If I want to install Vim, should I sudo nix-env -i vim or programs.vim.enable = true;? I can think of pros and cons for both approaches, but other users may not have this intuition.

Is there any way I can efficiently get a list of options to use for a package install?

nix-instantiate --eval -E ‘map (x: “programs.${x}”) (builtins.attrNames (import <nixpkgs/nixos> { configuration = {}; }).options.programs)’ --json --strict | jq

This will give you list of available “progams.foo.*” namespaces. You can continue namespace tree traversal in same manner.

Did you think about why there are declarative and imperative ways to install a package?

I certainly understand declarative, but not really imperative. What’s the point of installing something through nix-env other than maybe ease-of-use?

My tool will generate .nix files and manage them. That way everything from the DE to the package manager frontend can generate declarative configurations with what looks like an imperative UX. Basically cpkg install will act like apt but install packages in a declarative manner. That way, you get the reproducibility and other benefits of declarative package management, but the ease of using an imperative package manager

I can think of pros and cons

What are they?

Thanks for the nix-instantiate command. I’ll try it out later.