How to extend NixOS options' defaults

I’m trying to use the Jenkins service on NixOS, and git-lfs is causing me problems.

I saw it has a list of packages with some default ones, and git-lfs is indeed missing:

packages = mkOption {
  default = [ pkgs.stdenv pkgs.git pkgs.jdk11 config.programs.ssh.package pkgs.nix ];
  type = types.listOf types.package;
};

:thought_balloon: I thought “I could copy those over and add pkgs.git-lfs, but if the defaults change, I won’t see the drift until it causes problems”.

So I tried to append git-lfs to the default packages like this:

services.jenkins = {
  enable = true;
  home = "/home/jenkins";
  port = 10420;
  extraGroups = [ "docker" ];
  packages = config.services.jenkins.packages.default ++ [ pkgs.git-lfs ];
};

:x: This errored out with infinite recursion encountered.

Then I saw this GH issue and learned about mkOption { apply = old: ... };, so I tried:

services.jenkins = {
  enable = true;
  home = "/home/jenkins";
  port = 10420;
  extraGroups = [ "docker" ];
  packages = lib.mkOption {
    apply = old: old ++ [ pkgs.git-lfs ];
    type = lib.types.listOf lib.types.package;
  };
};

:x: This errored out with the same infinite recursion error.

I’d love help understanding:

a) Why this causes an infinite recursion

I can imagine in the first example I’m creating a definition of packages that depends on packages's itself, where perhaps something like what we do with overlays is necessary. The problem is this is what I expected the mkOption version to be, so I’m confused.

b) How to do this correctly

2 Likes
{ config, lib, options, pkgs, ... }: {
  services.jenkins = {
    enable = true;
    home = "/home/jenkins";
    port = 10420;
    extraGroups = [ "docker" ];
    packages = options.services.jenkins.packages.default ++ [ pkgs.git-lfs ];
  };
}
3 Likes

It worked, thank you! :tada:

Edit: tiny change—it was options.services.jenkins.packages.default.

1 Like

Whenever you are referencing config in the the modules config definition, you have to be careful about infinite recursion.

It might help to rewrite your example like this:

config.services.jenkins = {
  enable = true;
  home = "/home/jenkins";
  port = 10420;
  extraGroups = [ "docker" ];
  packages = config.services.jenkins.packages.default ++ [ pkgs.git-lfs ];
};

You’ve made the definition of config.services.jenkins depend on the value of config.services.jenkins, which creates an infinite recursion because each time you change config.services.jenkins you have to look at the old value of config.services.jenkins to define it.

2 Likes