193 lines
6.4 KiB
Markdown
193 lines
6.4 KiB
Markdown
|
# Options for Program Settings {#sec-settings-options}
|
||
|
|
||
|
Many programs have configuration files where program-specific settings
|
||
|
can be declared. File formats can be separated into two categories:
|
||
|
|
||
|
- Nix-representable ones: These can trivially be mapped to a subset of
|
||
|
Nix syntax. E.g. JSON is an example, since its values like
|
||
|
`{"foo":{"bar":10}}` can be mapped directly to Nix:
|
||
|
`{ foo = { bar = 10; }; }`. Other examples are INI, YAML and TOML.
|
||
|
The following section explains the convention for these settings.
|
||
|
|
||
|
- Non-nix-representable ones: These can\'t be trivially mapped to a
|
||
|
subset of Nix syntax. Most generic programming languages are in this
|
||
|
group, e.g. bash, since the statement `if true; then echo hi; fi`
|
||
|
doesn\'t have a trivial representation in Nix.
|
||
|
|
||
|
Currently there are no fixed conventions for these, but it is common
|
||
|
to have a `configFile` option for setting the configuration file
|
||
|
path directly. The default value of `configFile` can be an
|
||
|
auto-generated file, with convenient options for controlling the
|
||
|
contents. For example an option of type `attrsOf str` can be used
|
||
|
for representing environment variables which generates a section
|
||
|
like `export FOO="foo"`. Often it can also be useful to also include
|
||
|
an `extraConfig` option of type `lines` to allow arbitrary text
|
||
|
after the autogenerated part of the file.
|
||
|
|
||
|
## Nix-representable Formats (JSON, YAML, TOML, INI, \...) {#sec-settings-nix-representable}
|
||
|
|
||
|
By convention, formats like this are handled with a generic `settings`
|
||
|
option, representing the full program configuration as a Nix value. The
|
||
|
type of this option should represent the format. The most common formats
|
||
|
have a predefined type and string generator already declared under
|
||
|
`pkgs.formats`:
|
||
|
|
||
|
`pkgs.formats.json` { }
|
||
|
|
||
|
: A function taking an empty attribute set (for future extensibility)
|
||
|
and returning a set with JSON-specific attributes `type` and
|
||
|
`generate` as specified [below](#pkgs-formats-result).
|
||
|
|
||
|
`pkgs.formats.yaml` { }
|
||
|
|
||
|
: A function taking an empty attribute set (for future extensibility)
|
||
|
and returning a set with YAML-specific attributes `type` and
|
||
|
`generate` as specified [below](#pkgs-formats-result).
|
||
|
|
||
|
`pkgs.formats.ini` { *`listsAsDuplicateKeys`* ? false, *`listToValue`* ? null, \... }
|
||
|
|
||
|
: A function taking an attribute set with values
|
||
|
|
||
|
`listsAsDuplicateKeys`
|
||
|
|
||
|
: A boolean for controlling whether list values can be used to
|
||
|
represent duplicate INI keys
|
||
|
|
||
|
`listToValue`
|
||
|
|
||
|
: A function for turning a list of values into a single value.
|
||
|
|
||
|
It returns a set with INI-specific attributes `type` and `generate`
|
||
|
as specified [below](#pkgs-formats-result).
|
||
|
|
||
|
`pkgs.formats.toml` { }
|
||
|
|
||
|
: A function taking an empty attribute set (for future extensibility)
|
||
|
and returning a set with TOML-specific attributes `type` and
|
||
|
`generate` as specified [below](#pkgs-formats-result).
|
||
|
|
||
|
::: {#pkgs-formats-result}
|
||
|
These functions all return an attribute set with these values:
|
||
|
:::
|
||
|
|
||
|
`type`
|
||
|
|
||
|
: A module system type representing a value of the format
|
||
|
|
||
|
`generate` *`filename jsonValue`*
|
||
|
|
||
|
: A function that can render a value of the format to a file. Returns
|
||
|
a file path.
|
||
|
|
||
|
::: {.note}
|
||
|
This function puts the value contents in the Nix store. So this
|
||
|
should be avoided for secrets.
|
||
|
:::
|
||
|
|
||
|
::: {#ex-settings-nix-representable .example}
|
||
|
::: {.title}
|
||
|
**Example: Module with conventional `settings` option**
|
||
|
:::
|
||
|
The following shows a module for an example program that uses a JSON
|
||
|
configuration file. It demonstrates how above values can be used, along
|
||
|
with some other related best practices. See the comments for
|
||
|
explanations.
|
||
|
|
||
|
```nix
|
||
|
{ options, config, lib, pkgs, ... }:
|
||
|
let
|
||
|
cfg = config.services.foo;
|
||
|
# Define the settings format used for this program
|
||
|
settingsFormat = pkgs.formats.json {};
|
||
|
in {
|
||
|
|
||
|
options.services.foo = {
|
||
|
enable = lib.mkEnableOption "foo service";
|
||
|
|
||
|
settings = lib.mkOption {
|
||
|
# Setting this type allows for correct merging behavior
|
||
|
type = settingsFormat.type;
|
||
|
default = {};
|
||
|
description = ''
|
||
|
Configuration for foo, see
|
||
|
<link xlink:href="https://example.com/docs/foo"/>
|
||
|
for supported settings.
|
||
|
'';
|
||
|
};
|
||
|
};
|
||
|
|
||
|
config = lib.mkIf cfg.enable {
|
||
|
# We can assign some default settings here to make the service work by just
|
||
|
# enabling it. We use `mkDefault` for values that can be changed without
|
||
|
# problems
|
||
|
services.foo.settings = {
|
||
|
# Fails at runtime without any value set
|
||
|
log_level = lib.mkDefault "WARN";
|
||
|
|
||
|
# We assume systemd's `StateDirectory` is used, so we require this value,
|
||
|
# therefore no mkDefault
|
||
|
data_path = "/var/lib/foo";
|
||
|
|
||
|
# Since we use this to create a user we need to know the default value at
|
||
|
# eval time
|
||
|
user = lib.mkDefault "foo";
|
||
|
};
|
||
|
|
||
|
environment.etc."foo.json".source =
|
||
|
# The formats generator function takes a filename and the Nix value
|
||
|
# representing the format value and produces a filepath with that value
|
||
|
# rendered in the format
|
||
|
settingsFormat.generate "foo-config.json" cfg.settings;
|
||
|
|
||
|
# We know that the `user` attribute exists because we set a default value
|
||
|
# for it above, allowing us to use it without worries here
|
||
|
users.users.${cfg.settings.user} = { isSystemUser = true; };
|
||
|
|
||
|
# ...
|
||
|
};
|
||
|
}
|
||
|
```
|
||
|
:::
|
||
|
|
||
|
### Option declarations for attributes {#sec-settings-attrs-options}
|
||
|
|
||
|
Some `settings` attributes may deserve some extra care. They may need a
|
||
|
different type, default or merging behavior, or they are essential
|
||
|
options that should show their documentation in the manual. This can be
|
||
|
done using [](#sec-freeform-modules).
|
||
|
|
||
|
We extend above example using freeform modules to declare an option for
|
||
|
the port, which will enforce it to be a valid integer and make it show
|
||
|
up in the manual.
|
||
|
|
||
|
::: {#ex-settings-typed-attrs .example}
|
||
|
::: {.title}
|
||
|
**Example: Declaring a type-checked `settings` attribute**
|
||
|
:::
|
||
|
```nix
|
||
|
settings = lib.mkOption {
|
||
|
type = lib.types.submodule {
|
||
|
|
||
|
freeformType = settingsFormat.type;
|
||
|
|
||
|
# Declare an option for the port such that the type is checked and this option
|
||
|
# is shown in the manual.
|
||
|
options.port = lib.mkOption {
|
||
|
type = lib.types.port;
|
||
|
default = 8080;
|
||
|
description = ''
|
||
|
Which port this service should listen on.
|
||
|
'';
|
||
|
};
|
||
|
|
||
|
};
|
||
|
default = {};
|
||
|
description = ''
|
||
|
Configuration for Foo, see
|
||
|
<link xlink:href="https://example.com/docs/foo"/>
|
||
|
for supported values.
|
||
|
'';
|
||
|
};
|
||
|
```
|
||
|
:::
|