diff options
author | Jörg Thalheim <Mic92@users.noreply.github.com> | 2021-09-07 06:28:57 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-09-07 06:28:57 +0100 |
commit | c7d32059b126e2ba61a9d2422a0baf76fbf7101a (patch) | |
tree | 0a9083eb77613dba05b6147c06c95ed780bcda4f /nixos/doc/manual/from_md/development | |
parent | 929f0156ccb2eeed30afa968d3663238ca086fcc (diff) | |
parent | 165d6bd20c478c8c4f201c26eaa4c62e3c1b87ce (diff) | |
download | nixpkgs-c7d32059b126e2ba61a9d2422a0baf76fbf7101a.tar nixpkgs-c7d32059b126e2ba61a9d2422a0baf76fbf7101a.tar.gz nixpkgs-c7d32059b126e2ba61a9d2422a0baf76fbf7101a.tar.bz2 nixpkgs-c7d32059b126e2ba61a9d2422a0baf76fbf7101a.tar.lz nixpkgs-c7d32059b126e2ba61a9d2422a0baf76fbf7101a.tar.xz nixpkgs-c7d32059b126e2ba61a9d2422a0baf76fbf7101a.tar.zst nixpkgs-c7d32059b126e2ba61a9d2422a0baf76fbf7101a.zip |
Merge pull request #129136 from bobby285271/pr14
nixos/doc: convert "Chapter 58. Writing NixOS Modules" to CommonMark
Diffstat (limited to 'nixos/doc/manual/from_md/development')
8 files changed, 1838 insertions, 0 deletions
diff --git a/nixos/doc/manual/from_md/development/freeform-modules.section.xml b/nixos/doc/manual/from_md/development/freeform-modules.section.xml new file mode 100644 index 00000000000..86a9cf3140d --- /dev/null +++ b/nixos/doc/manual/from_md/development/freeform-modules.section.xml @@ -0,0 +1,87 @@ +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-freeform-modules"> + <title>Freeform modules</title> + <para> + Freeform modules allow you to define values for option paths that + have not been declared explicitly. This can be used to add + attribute-specific types to what would otherwise have to be + <literal>attrsOf</literal> options in order to accept all attribute + names. + </para> + <para> + This feature can be enabled by using the attribute + <literal>freeformType</literal> to define a freeform type. By doing + this, all assignments without an associated option will be merged + using the freeform type and combined into the resulting + <literal>config</literal> set. Since this feature nullifies name + checking for entire option trees, it is only recommended for use in + submodules. + </para> + <anchor xml:id="ex-freeform-module" /> + <para> + <emphasis role="strong">Example: Freeform submodule</emphasis> + </para> + <para> + The following shows a submodule assigning a freeform type that + allows arbitrary attributes with <literal>str</literal> values below + <literal>settings</literal>, but also declares an option for the + <literal>settings.port</literal> attribute to have it type-checked + and assign a default value. See + <link linkend="ex-settings-typed-attrs">Example: Declaring a + type-checked <literal>settings</literal> attribute</link> for a more + complete example. + </para> + <programlisting language="bash"> +{ lib, config, ... }: { + + options.settings = lib.mkOption { + type = lib.types.submodule { + + freeformType = with lib.types; attrsOf str; + + # We want this attribute to be checked for the correct type + options.port = lib.mkOption { + type = lib.types.port; + # Declaring the option also allows defining a default value + default = 8080; + }; + + }; + }; +} +</programlisting> + <para> + And the following shows what such a module then allows + </para> + <programlisting language="bash"> +{ + # Not a declared option, but the freeform type allows this + settings.logLevel = "debug"; + + # Not allowed because the the freeform type only allows strings + # settings.enable = true; + + # Allowed because there is a port option declared + settings.port = 80; + + # Not allowed because the port option doesn't allow strings + # settings.port = "443"; +} +</programlisting> + <note> + <para> + Freeform attributes cannot depend on other attributes of the same + set without infinite recursion: + </para> + <programlisting language="bash"> +{ + # This throws infinite recursion encountered + settings.logLevel = lib.mkIf (config.settings.port == 80) "debug"; +} +</programlisting> + <para> + To prevent this, declare options for all attributes that need to + depend on others. For above example this means to declare + <literal>logLevel</literal> to be an option. + </para> + </note> +</section> diff --git a/nixos/doc/manual/from_md/development/importing-modules.section.xml b/nixos/doc/manual/from_md/development/importing-modules.section.xml new file mode 100644 index 00000000000..cb04dde67c8 --- /dev/null +++ b/nixos/doc/manual/from_md/development/importing-modules.section.xml @@ -0,0 +1,47 @@ +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-importing-modules"> + <title>Importing Modules</title> + <para> + Sometimes NixOS modules need to be used in configuration but exist + outside of Nixpkgs. These modules can be imported: + </para> + <programlisting language="bash"> +{ config, lib, pkgs, ... }: + +{ + imports = + [ # Use a locally-available module definition in + # ./example-module/default.nix + ./example-module + ]; + + services.exampleModule.enable = true; +} +</programlisting> + <para> + The environment variable <literal>NIXOS_EXTRA_MODULE_PATH</literal> + is an absolute path to a NixOS module that is included alongside the + Nixpkgs NixOS modules. Like any NixOS module, this module can import + additional modules: + </para> + <programlisting language="bash"> +# ./module-list/default.nix +[ + ./example-module1 + ./example-module2 +] +</programlisting> + <programlisting language="bash"> +# ./extra-module/default.nix +{ imports = import ./module-list.nix; } +</programlisting> + <programlisting language="bash"> +# NIXOS_EXTRA_MODULE_PATH=/absolute/path/to/extra-module +{ config, lib, pkgs, ... }: + +{ + # No `imports` needed + + services.exampleModule1.enable = true; +} +</programlisting> +</section> diff --git a/nixos/doc/manual/from_md/development/meta-attributes.section.xml b/nixos/doc/manual/from_md/development/meta-attributes.section.xml new file mode 100644 index 00000000000..f535d94602b --- /dev/null +++ b/nixos/doc/manual/from_md/development/meta-attributes.section.xml @@ -0,0 +1,55 @@ +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-meta-attributes"> + <title>Meta Attributes</title> + <para> + Like Nix packages, NixOS modules can declare meta-attributes to + provide extra information. Module meta attributes are defined in the + <literal>meta.nix</literal> special module. + </para> + <para> + <literal>meta</literal> is a top level attribute like + <literal>options</literal> and <literal>config</literal>. Available + meta-attributes are <literal>maintainers</literal> and + <literal>doc</literal>. + </para> + <para> + Each of the meta-attributes must be defined at most once per module + file. + </para> + <programlisting language="bash"> +{ config, lib, pkgs, ... }: +{ + options = { + ... + }; + + config = { + ... + }; + + meta = { + maintainers = with lib.maintainers; [ ericsagnes ]; + doc = ./default.xml; + }; +} +</programlisting> + <itemizedlist> + <listitem> + <para> + <literal>maintainers</literal> contains a list of the module + maintainers. + </para> + </listitem> + <listitem> + <para> + <literal>doc</literal> points to a valid DocBook file containing + the module documentation. Its contents is automatically added to + <xref linkend="ch-configuration" />. Changes to a module + documentation have to be checked to not break building the NixOS + manual: + </para> + <programlisting> +$ nix-build nixos/release.nix -A manual.x86_64-linux +</programlisting> + </listitem> + </itemizedlist> +</section> diff --git a/nixos/doc/manual/from_md/development/option-declarations.section.xml b/nixos/doc/manual/from_md/development/option-declarations.section.xml new file mode 100644 index 00000000000..85a59a543d1 --- /dev/null +++ b/nixos/doc/manual/from_md/development/option-declarations.section.xml @@ -0,0 +1,203 @@ +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-option-declarations"> + <title>Option Declarations</title> + <para> + An option declaration specifies the name, type and description of a + NixOS configuration option. It is invalid to define an option that + hasn’t been declared in any module. An option declaration generally + looks like this: + </para> + <programlisting language="bash"> +options = { + name = mkOption { + type = type specification; + default = default value; + example = example value; + description = "Description for use in the NixOS manual."; + }; +}; +</programlisting> + <para> + The attribute names within the <literal>name</literal> attribute + path must be camel cased in general but should, as an exception, + match the + <link xlink:href="https://nixos.org/nixpkgs/manual/#sec-package-naming"> + package attribute name</link> when referencing a Nixpkgs package. + For example, the option + <literal>services.nix-serve.bindAddress</literal> references the + <literal>nix-serve</literal> Nixpkgs package. + </para> + <para> + The function <literal>mkOption</literal> accepts the following + arguments. + </para> + <variablelist> + <varlistentry> + <term> + <literal>type</literal> + </term> + <listitem> + <para> + The type of the option (see + <xref linkend="sec-option-types" />). It may be omitted, but + that’s not advisable since it may lead to errors that are hard + to diagnose. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>default</literal> + </term> + <listitem> + <para> + The default value used if no value is defined by any module. A + default is not required; but if a default is not given, then + users of the module will have to define the value of the + option, otherwise an error will be thrown. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>example</literal> + </term> + <listitem> + <para> + An example value that will be shown in the NixOS manual. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>description</literal> + </term> + <listitem> + <para> + A textual description of the option, in DocBook format, that + will be included in the NixOS manual. + </para> + </listitem> + </varlistentry> + </variablelist> + <section xml:id="sec-option-declarations-eot"> + <title>Extensible Option Types</title> + <para> + Extensible option types is a feature that allow to extend certain + types declaration through multiple module files. This feature only + work with a restricted set of types, namely + <literal>enum</literal> and <literal>submodules</literal> and any + composed forms of them. + </para> + <para> + Extensible option types can be used for <literal>enum</literal> + options that affects multiple modules, or as an alternative to + related <literal>enable</literal> options. + </para> + <para> + As an example, we will take the case of display managers. There is + a central display manager module for generic display manager + options and a module file per display manager backend (sddm, gdm + ...). + </para> + <para> + There are two approach to this module structure: + </para> + <itemizedlist> + <listitem> + <para> + Managing the display managers independently by adding an + enable option to every display manager module backend. (NixOS) + </para> + </listitem> + <listitem> + <para> + Managing the display managers in the central module by adding + an option to select which display manager backend to use. + </para> + </listitem> + </itemizedlist> + <para> + Both approaches have problems. + </para> + <para> + Making backends independent can quickly become hard to manage. For + display managers, there can be only one enabled at a time, but the + type system can not enforce this restriction as there is no + relation between each backend <literal>enable</literal> option. As + a result, this restriction has to be done explicitely by adding + assertions in each display manager backend module. + </para> + <para> + On the other hand, managing the display managers backends in the + central module will require to change the central module option + every time a new backend is added or removed. + </para> + <para> + By using extensible option types, it is possible to create a + placeholder option in the central module + (<link linkend="ex-option-declaration-eot-service">Example: + Extensible type placeholder in the service module</link>), and to + extend it in each backend module + (<link linkend="ex-option-declaration-eot-backend-gdm">Example: + Extending + <literal>services.xserver.displayManager.enable</literal> in the + <literal>gdm</literal> module</link>, + <link linkend="ex-option-declaration-eot-backend-sddm">Example: + Extending + <literal>services.xserver.displayManager.enable</literal> in the + <literal>sddm</literal> module</link>). + </para> + <para> + As a result, <literal>displayManager.enable</literal> option + values can be added without changing the main service module file + and the type system automatically enforce that there can only be a + single display manager enabled. + </para> + <anchor xml:id="ex-option-declaration-eot-service" /> + <para> + <emphasis role="strong">Example: Extensible type placeholder in + the service module</emphasis> + </para> + <programlisting language="bash"> +services.xserver.displayManager.enable = mkOption { + description = "Display manager to use"; + type = with types; nullOr (enum [ ]); +}; +</programlisting> + <anchor xml:id="ex-option-declaration-eot-backend-gdm" /> + <para> + <emphasis role="strong">Example: Extending + <literal>services.xserver.displayManager.enable</literal> in the + <literal>gdm</literal> module</emphasis> + </para> + <programlisting language="bash"> +services.xserver.displayManager.enable = mkOption { + type = with types; nullOr (enum [ "gdm" ]); +}; +</programlisting> + <anchor xml:id="ex-option-declaration-eot-backend-sddm" /> + <para> + <emphasis role="strong">Example: Extending + <literal>services.xserver.displayManager.enable</literal> in the + <literal>sddm</literal> module</emphasis> + </para> + <programlisting language="bash"> +services.xserver.displayManager.enable = mkOption { + type = with types; nullOr (enum [ "sddm" ]); +}; +</programlisting> + <para> + The placeholder declaration is a standard + <literal>mkOption</literal> declaration, but it is important that + extensible option declarations only use the + <literal>type</literal> argument. + </para> + <para> + Extensible option types work with any of the composed variants of + <literal>enum</literal> such as + <literal>with types; nullOr (enum [ "foo" "bar" ])</literal> + or + <literal>with types; listOf (enum [ "foo" "bar" ])</literal>. + </para> + </section> +</section> diff --git a/nixos/doc/manual/from_md/development/option-def.section.xml b/nixos/doc/manual/from_md/development/option-def.section.xml new file mode 100644 index 00000000000..8c9ef181aff --- /dev/null +++ b/nixos/doc/manual/from_md/development/option-def.section.xml @@ -0,0 +1,104 @@ +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-option-definitions"> + <title>Option Definitions</title> + <para> + Option definitions are generally straight-forward bindings of values + to option names, like + </para> + <programlisting language="bash"> +config = { + services.httpd.enable = true; +}; +</programlisting> + <para> + However, sometimes you need to wrap an option definition or set of + option definitions in a <emphasis>property</emphasis> to achieve + certain effects: + </para> + <section xml:id="sec-option-definitions-delaying-conditionals"> + <title>Delaying Conditionals</title> + <para> + If a set of option definitions is conditional on the value of + another option, you may need to use <literal>mkIf</literal>. + Consider, for instance: + </para> + <programlisting language="bash"> +config = if config.services.httpd.enable then { + environment.systemPackages = [ ... ]; + ... +} else {}; +</programlisting> + <para> + This definition will cause Nix to fail with an <quote>infinite + recursion</quote> error. Why? Because the value of + <literal>config.services.httpd.enable</literal> depends on the + value being constructed here. After all, you could also write the + clearly circular and contradictory: + </para> + <programlisting language="bash"> +config = if config.services.httpd.enable then { + services.httpd.enable = false; +} else { + services.httpd.enable = true; +}; +</programlisting> + <para> + The solution is to write: + </para> + <programlisting language="bash"> +config = mkIf config.services.httpd.enable { + environment.systemPackages = [ ... ]; + ... +}; +</programlisting> + <para> + The special function <literal>mkIf</literal> causes the evaluation + of the conditional to be <quote>pushed down</quote> into the + individual definitions, as if you had written: + </para> + <programlisting language="bash"> +config = { + environment.systemPackages = if config.services.httpd.enable then [ ... ] else []; + ... +}; +</programlisting> + </section> + <section xml:id="sec-option-definitions-setting-priorities"> + <title>Setting Priorities</title> + <para> + A module can override the definitions of an option in other + modules by setting a <emphasis>priority</emphasis>. All option + definitions that do not have the lowest priority value are + discarded. By default, option definitions have priority 1000. You + can specify an explicit priority by using + <literal>mkOverride</literal>, e.g. + </para> + <programlisting language="bash"> +services.openssh.enable = mkOverride 10 false; +</programlisting> + <para> + This definition causes all other definitions with priorities above + 10 to be discarded. The function <literal>mkForce</literal> is + equal to <literal>mkOverride 50</literal>. + </para> + </section> + <section xml:id="sec-option-definitions-merging"> + <title>Merging Configurations</title> + <para> + In conjunction with <literal>mkIf</literal>, it is sometimes + useful for a module to return multiple sets of option definitions, + to be merged together as if they were declared in separate + modules. This can be done using <literal>mkMerge</literal>: + </para> + <programlisting language="bash"> +config = mkMerge + [ # Unconditional stuff. + { environment.systemPackages = [ ... ]; + } + # Conditional stuff. + (mkIf config.services.bla.enable { + environment.systemPackages = [ ... ]; + }) + ]; +</programlisting> + </section> +</section> diff --git a/nixos/doc/manual/from_md/development/option-types.section.xml b/nixos/doc/manual/from_md/development/option-types.section.xml new file mode 100644 index 00000000000..c83ffa2add5 --- /dev/null +++ b/nixos/doc/manual/from_md/development/option-types.section.xml @@ -0,0 +1,987 @@ +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-option-types"> + <title>Options Types</title> + <para> + Option types are a way to put constraints on the values a module + option can take. Types are also responsible of how values are merged + in case of multiple value definitions. + </para> + <section xml:id="sec-option-types-basic"> + <title>Basic Types</title> + <para> + Basic types are the simplest available types in the module system. + Basic types include multiple string types that mainly differ in + how definition merging is handled. + </para> + <variablelist> + <varlistentry> + <term> + <literal>types.bool</literal> + </term> + <listitem> + <para> + A boolean, its values can be <literal>true</literal> or + <literal>false</literal>. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>types.path</literal> + </term> + <listitem> + <para> + A filesystem path, defined as anything that when coerced to + a string starts with a slash. Even if derivations can be + considered as path, the more specific + <literal>types.package</literal> should be preferred. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>types.package</literal> + </term> + <listitem> + <para> + A derivation or a store path. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>types.anything</literal> + </term> + <listitem> + <para> + A type that accepts any value and recursively merges + attribute sets together. This type is recommended when the + option type is unknown. + </para> + <anchor xml:id="ex-types-anything" /> + <para> + <emphasis role="strong">Example: + <literal>types.anything</literal> Example</emphasis> + </para> + <para> + Two definitions of this type like + </para> + <programlisting language="bash"> +{ + str = lib.mkDefault "foo"; + pkg.hello = pkgs.hello; + fun.fun = x: x + 1; +} +</programlisting> + <programlisting language="bash"> +{ + str = lib.mkIf true "bar"; + pkg.gcc = pkgs.gcc; + fun.fun = lib.mkForce (x: x + 2); +} +</programlisting> + <para> + will get merged to + </para> + <programlisting language="bash"> +{ + str = "bar"; + pkg.gcc = pkgs.gcc; + pkg.hello = pkgs.hello; + fun.fun = x: x + 2; +} +</programlisting> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>types.attrs</literal> + </term> + <listitem> + <para> + A free-form attribute set. + </para> + <warning> + <para> + This type will be deprecated in the future because it + doesn't recurse into attribute sets, silently drops + earlier attribute definitions, and doesn't discharge + <literal>lib.mkDefault</literal>, + <literal>lib.mkIf</literal> and co. For allowing arbitrary + attribute sets, prefer + <literal>types.attrsOf types.anything</literal> instead + which doesn't have these problems. + </para> + </warning> + </listitem> + </varlistentry> + </variablelist> + <para> + Integer-related types: + </para> + <variablelist> + <varlistentry> + <term> + <literal>types.int</literal> + </term> + <listitem> + <para> + A signed integer. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>types.ints.{s8, s16, s32}</literal> + </term> + <listitem> + <para> + Signed integers with a fixed length (8, 16 or 32 bits). They + go from −2^n/2 to 2^n/2−1 respectively (e.g. + <literal>−128</literal> to <literal>127</literal> for 8 + bits). + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>types.ints.unsigned</literal> + </term> + <listitem> + <para> + An unsigned integer (that is >= 0). + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>types.ints.{u8, u16, u32}</literal> + </term> + <listitem> + <para> + Unsigned integers with a fixed length (8, 16 or 32 bits). + They go from 0 to 2^n−1 respectively (e.g. + <literal>0</literal> to <literal>255</literal> for 8 bits). + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>types.ints.positive</literal> + </term> + <listitem> + <para> + A positive integer (that is > 0). + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>types.port</literal> + </term> + <listitem> + <para> + A port number. This type is an alias to + <literal>types.ints.u16</literal>. + </para> + </listitem> + </varlistentry> + </variablelist> + <para> + String-related types: + </para> + <variablelist> + <varlistentry> + <term> + <literal>types.str</literal> + </term> + <listitem> + <para> + A string. Multiple definitions cannot be merged. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>types.lines</literal> + </term> + <listitem> + <para> + A string. Multiple definitions are concatenated with a new + line <literal>"\n"</literal>. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>types.commas</literal> + </term> + <listitem> + <para> + A string. Multiple definitions are concatenated with a comma + <literal>","</literal>. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>types.envVar</literal> + </term> + <listitem> + <para> + A string. Multiple definitions are concatenated with a + collon <literal>":"</literal>. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>types.strMatching</literal> + </term> + <listitem> + <para> + A string matching a specific regular expression. Multiple + definitions cannot be merged. The regular expression is + processed using <literal>builtins.match</literal>. + </para> + </listitem> + </varlistentry> + </variablelist> + </section> + <section xml:id="sec-option-types-value"> + <title>Value Types</title> + <para> + Value types are types that take a value parameter. + </para> + <variablelist> + <varlistentry> + <term> + <literal>types.enum</literal> + <emphasis><literal>l</literal></emphasis> + </term> + <listitem> + <para> + One element of the list + <emphasis><literal>l</literal></emphasis>, e.g. + <literal>types.enum [ "left" "right" ]</literal>. + Multiple definitions cannot be merged. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>types.separatedString</literal> + <emphasis><literal>sep</literal></emphasis> + </term> + <listitem> + <para> + A string with a custom separator + <emphasis><literal>sep</literal></emphasis>, e.g. + <literal>types.separatedString "|"</literal>. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>types.ints.between</literal> + <emphasis><literal>lowest highest</literal></emphasis> + </term> + <listitem> + <para> + An integer between + <emphasis><literal>lowest</literal></emphasis> and + <emphasis><literal>highest</literal></emphasis> (both + inclusive). Useful for creating types like + <literal>types.port</literal>. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>types.submodule</literal> + <emphasis><literal>o</literal></emphasis> + </term> + <listitem> + <para> + A set of sub options + <emphasis><literal>o</literal></emphasis>. + <emphasis><literal>o</literal></emphasis> can be an + attribute set, a function returning an attribute set, or a + path to a file containing such a value. Submodules are used + in composed types to create modular options. This is + equivalent to + <literal>types.submoduleWith { modules = toList o; shorthandOnlyDefinesConfig = true; }</literal>. + Submodules are detailed in + <link linkend="section-option-types-submodule">Submodule</link>. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>types.submoduleWith</literal> { + <emphasis><literal>modules</literal></emphasis>, + <emphasis><literal>specialArgs</literal></emphasis> ? {}, + <emphasis><literal>shorthandOnlyDefinesConfig</literal></emphasis> + ? false } + </term> + <listitem> + <para> + Like <literal>types.submodule</literal>, but more flexible + and with better defaults. It has parameters + </para> + <itemizedlist> + <listitem> + <para> + <emphasis><literal>modules</literal></emphasis> A list + of modules to use by default for this submodule type. + This gets combined with all option definitions to build + the final list of modules that will be included. + </para> + <note> + <para> + Only options defined with this argument are included + in rendered documentation. + </para> + </note> + </listitem> + <listitem> + <para> + <emphasis><literal>specialArgs</literal></emphasis> An + attribute set of extra arguments to be passed to the + module functions. The option + <literal>_module.args</literal> should be used instead + for most arguments since it allows overriding. + <emphasis><literal>specialArgs</literal></emphasis> + should only be used for arguments that can't go through + the module fixed-point, because of infinite recursion or + other problems. An example is overriding the + <literal>lib</literal> argument, because + <literal>lib</literal> itself is used to define + <literal>_module.args</literal>, which makes using + <literal>_module.args</literal> to define it impossible. + </para> + </listitem> + <listitem> + <para> + <emphasis><literal>shorthandOnlyDefinesConfig</literal></emphasis> + Whether definitions of this type should default to the + <literal>config</literal> section of a module (see + <link linkend="ex-module-syntax">Example: Structure of + NixOS Modules</link>) if it is an attribute set. + Enabling this only has a benefit when the submodule + defines an option named <literal>config</literal> or + <literal>options</literal>. In such a case it would + allow the option to be set with + <literal>the-submodule.config = "value"</literal> + instead of requiring + <literal>the-submodule.config.config = "value"</literal>. + This is because only when modules + <emphasis>don't</emphasis> set the + <literal>config</literal> or <literal>options</literal> + keys, all keys are interpreted as option definitions in + the <literal>config</literal> section. Enabling this + option implicitly puts all attributes in the + <literal>config</literal> section. + </para> + <para> + With this option enabled, defining a + non-<literal>config</literal> section requires using a + function: + <literal>the-submodule = { ... }: { options = { ... }; }</literal>. + </para> + </listitem> + </itemizedlist> + </listitem> + </varlistentry> + </variablelist> + </section> + <section xml:id="sec-option-types-composed"> + <title>Composed Types</title> + <para> + Composed types are types that take a type as parameter. + <literal>listOf int</literal> and + <literal>either int str</literal> are examples of composed types. + </para> + <variablelist> + <varlistentry> + <term> + <literal>types.listOf</literal> + <emphasis><literal>t</literal></emphasis> + </term> + <listitem> + <para> + A list of <emphasis><literal>t</literal></emphasis> type, + e.g. <literal>types.listOf int</literal>. Multiple + definitions are merged with list concatenation. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>types.attrsOf</literal> + <emphasis><literal>t</literal></emphasis> + </term> + <listitem> + <para> + An attribute set of where all the values are of + <emphasis><literal>t</literal></emphasis> type. Multiple + definitions result in the joined attribute set. + </para> + <note> + <para> + This type is <emphasis>strict</emphasis> in its values, + which in turn means attributes cannot depend on other + attributes. See <literal> types.lazyAttrsOf</literal> for + a lazy version. + </para> + </note> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>types.lazyAttrsOf</literal> + <emphasis><literal>t</literal></emphasis> + </term> + <listitem> + <para> + An attribute set of where all the values are of + <emphasis><literal>t</literal></emphasis> type. Multiple + definitions result in the joined attribute set. This is the + lazy version of <literal>types.attrsOf </literal>, allowing + attributes to depend on each other. + </para> + <warning> + <para> + This version does not fully support conditional + definitions! With an option <literal>foo</literal> of this + type and a definition + <literal>foo.attr = lib.mkIf false 10</literal>, + evaluating <literal>foo ? attr</literal> will return + <literal>true</literal> even though it should be false. + Accessing the value will then throw an error. For types + <emphasis><literal>t</literal></emphasis> that have an + <literal>emptyValue</literal> defined, that value will be + returned instead of throwing an error. So if the type of + <literal>foo.attr</literal> was + <literal>lazyAttrsOf (nullOr int)</literal>, + <literal>null</literal> would be returned instead for the + same <literal>mkIf false</literal> definition. + </para> + </warning> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>types.nullOr</literal> + <emphasis><literal>t</literal></emphasis> + </term> + <listitem> + <para> + <literal>null</literal> or type + <emphasis><literal>t</literal></emphasis>. Multiple + definitions are merged according to type + <emphasis><literal>t</literal></emphasis>. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>types.uniq</literal> + <emphasis><literal>t</literal></emphasis> + </term> + <listitem> + <para> + Ensures that type <emphasis><literal>t</literal></emphasis> + cannot be merged. It is used to ensure option definitions + are declared only once. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>types.either</literal> + <emphasis><literal>t1 t2</literal></emphasis> + </term> + <listitem> + <para> + Type <emphasis><literal>t1</literal></emphasis> or type + <emphasis><literal>t2</literal></emphasis>, e.g. + <literal>with types; either int str</literal>. Multiple + definitions cannot be merged. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>types.oneOf</literal> [ + <emphasis><literal>t1 t2</literal></emphasis> ... ] + </term> + <listitem> + <para> + Type <emphasis><literal>t1</literal></emphasis> or type + <emphasis><literal>t2</literal></emphasis> and so forth, + e.g. <literal>with types; oneOf [ int str bool ]</literal>. + Multiple definitions cannot be merged. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>types.coercedTo</literal> + <emphasis><literal>from f to</literal></emphasis> + </term> + <listitem> + <para> + Type <emphasis><literal>to</literal></emphasis> or type + <emphasis><literal>from</literal></emphasis> which will be + coerced to type <emphasis><literal>to</literal></emphasis> + using function <emphasis><literal>f</literal></emphasis> + which takes an argument of type + <emphasis><literal>from</literal></emphasis> and return a + value of type <emphasis><literal>to</literal></emphasis>. + Can be used to preserve backwards compatibility of an option + if its type was changed. + </para> + </listitem> + </varlistentry> + </variablelist> + </section> + <section xml:id="section-option-types-submodule"> + <title>Submodule</title> + <para> + <literal>submodule</literal> is a very powerful type that defines + a set of sub-options that are handled like a separate module. + </para> + <para> + It takes a parameter <emphasis><literal>o</literal></emphasis>, + that should be a set, or a function returning a set with an + <literal>options</literal> key defining the sub-options. Submodule + option definitions are type-checked accordingly to the + <literal>options</literal> declarations. Of course, you can nest + submodule option definitons for even higher modularity. + </para> + <para> + The option set can be defined directly + (<link linkend="ex-submodule-direct">Example: Directly defined + submodule</link>) or as reference + (<link linkend="ex-submodule-reference">Example: Submodule defined + as a reference</link>). + </para> + <anchor xml:id="ex-submodule-direct" /> + <para> + <emphasis role="strong">Example: Directly defined + submodule</emphasis> + </para> + <programlisting language="bash"> +options.mod = mkOption { + description = "submodule example"; + type = with types; submodule { + options = { + foo = mkOption { + type = int; + }; + bar = mkOption { + type = str; + }; + }; + }; +}; +</programlisting> + <anchor xml:id="ex-submodule-reference" /> + <para> + <emphasis role="strong">Example: Submodule defined as a + reference</emphasis> + </para> + <programlisting language="bash"> +let + modOptions = { + options = { + foo = mkOption { + type = int; + }; + bar = mkOption { + type = int; + }; + }; + }; +in +options.mod = mkOption { + description = "submodule example"; + type = with types; submodule modOptions; +}; +</programlisting> + <para> + The <literal>submodule</literal> type is especially interesting + when used with composed types like <literal>attrsOf</literal> or + <literal>listOf</literal>. When composed with + <literal>listOf</literal> + (<link linkend="ex-submodule-listof-declaration">Example: + Declaration of a list of submodules</link>), + <literal>submodule</literal> allows multiple definitions of the + submodule option set + (<link linkend="ex-submodule-listof-definition">Example: + Definition of a list of submodules</link>). + </para> + <anchor xml:id="ex-submodule-listof-declaration" /> + <para> + <emphasis role="strong">Example: Declaration of a list of + submodules</emphasis> + </para> + <programlisting language="bash"> +options.mod = mkOption { + description = "submodule example"; + type = with types; listOf (submodule { + options = { + foo = mkOption { + type = int; + }; + bar = mkOption { + type = str; + }; + }; + }); +}; +</programlisting> + <anchor xml:id="ex-submodule-listof-definition" /> + <para> + <emphasis role="strong">Example: Definition of a list of + submodules</emphasis> + </para> + <programlisting language="bash"> +config.mod = [ + { foo = 1; bar = "one"; } + { foo = 2; bar = "two"; } +]; +</programlisting> + <para> + When composed with <literal>attrsOf</literal> + (<link linkend="ex-submodule-attrsof-declaration">Example: + Declaration of attribute sets of submodules</link>), + <literal>submodule</literal> allows multiple named definitions of + the submodule option set + (<link linkend="ex-submodule-attrsof-definition">Example: + Definition of attribute sets of submodules</link>). + </para> + <anchor xml:id="ex-submodule-attrsof-declaration" /> + <para> + <emphasis role="strong">Example: Declaration of attribute sets of + submodules</emphasis> + </para> + <programlisting language="bash"> +options.mod = mkOption { + description = "submodule example"; + type = with types; attrsOf (submodule { + options = { + foo = mkOption { + type = int; + }; + bar = mkOption { + type = str; + }; + }; + }); +}; +</programlisting> + <anchor xml:id="ex-submodule-attrsof-definition" /> + <para> + <emphasis role="strong">Example: Definition of attribute sets of + submodules</emphasis> + </para> + <programlisting language="bash"> +config.mod.one = { foo = 1; bar = "one"; }; +config.mod.two = { foo = 2; bar = "two"; }; +</programlisting> + </section> + <section xml:id="sec-option-types-extending"> + <title>Extending types</title> + <para> + Types are mainly characterized by their <literal>check</literal> + and <literal>merge</literal> functions. + </para> + <variablelist> + <varlistentry> + <term> + <literal>check</literal> + </term> + <listitem> + <para> + The function to type check the value. Takes a value as + parameter and return a boolean. It is possible to extend a + type check with the <literal>addCheck</literal> function + (<link linkend="ex-extending-type-check-1">Example: Adding a + type check</link>), or to fully override the check function + (<link linkend="ex-extending-type-check-2">Example: + Overriding a type check</link>). + </para> + <anchor xml:id="ex-extending-type-check-1" /> + <para> + <emphasis role="strong">Example: Adding a type + check</emphasis> + </para> + <programlisting language="bash"> +byte = mkOption { + description = "An integer between 0 and 255."; + type = types.addCheck types.int (x: x >= 0 && x <= 255); +}; +</programlisting> + <anchor xml:id="ex-extending-type-check-2" /> + <para> + <emphasis role="strong">Example: Overriding a type + check</emphasis> + </para> + <programlisting language="bash"> +nixThings = mkOption { + description = "words that start with 'nix'"; + type = types.str // { + check = (x: lib.hasPrefix "nix" x) + }; +}; +</programlisting> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>merge</literal> + </term> + <listitem> + <para> + Function to merge the options values when multiple values + are set. The function takes two parameters, + <literal>loc</literal> the option path as a list of strings, + and <literal>defs</literal> the list of defined values as a + list. It is possible to override a type merge function for + custom needs. + </para> + </listitem> + </varlistentry> + </variablelist> + </section> + <section xml:id="sec-option-types-custom"> + <title>Custom Types</title> + <para> + Custom types can be created with the + <literal>mkOptionType</literal> function. As type creation + includes some more complex topics such as submodule handling, it + is recommended to get familiar with <literal>types.nix</literal> + code before creating a new type. + </para> + <para> + The only required parameter is <literal>name</literal>. + </para> + <variablelist> + <varlistentry> + <term> + <literal>name</literal> + </term> + <listitem> + <para> + A string representation of the type function name. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>definition</literal> + </term> + <listitem> + <para> + Description of the type used in documentation. Give + information of the type and any of its arguments. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>check</literal> + </term> + <listitem> + <para> + A function to type check the definition value. Takes the + definition value as a parameter and returns a boolean + indicating the type check result, <literal>true</literal> + for success and <literal>false</literal> for failure. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>merge</literal> + </term> + <listitem> + <para> + A function to merge multiple definitions values. Takes two + parameters: + </para> + <variablelist> + <varlistentry> + <term> + <emphasis><literal>loc</literal></emphasis> + </term> + <listitem> + <para> + The option path as a list of strings, e.g. + <literal>["boot" "loader "grub" "enable"]</literal>. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <emphasis><literal>defs</literal></emphasis> + </term> + <listitem> + <para> + The list of sets of defined <literal>value</literal> + and <literal>file</literal> where the value was + defined, e.g. + <literal>[ { file = "/foo.nix"; value = 1; } { file = "/bar.nix"; value = 2 } ]</literal>. + The <literal>merge</literal> function should return + the merged value or throw an error in case the values + are impossible or not meant to be merged. + </para> + </listitem> + </varlistentry> + </variablelist> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>getSubOptions</literal> + </term> + <listitem> + <para> + For composed types that can take a submodule as type + parameter, this function generate sub-options documentation. + It takes the current option prefix as a list and return the + set of sub-options. Usually defined in a recursive manner by + adding a term to the prefix, e.g. + <literal>prefix: elemType.getSubOptions (prefix ++ ["prefix"])</literal> + where + <emphasis><literal>"prefix"</literal></emphasis> + is the newly added prefix. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>getSubModules</literal> + </term> + <listitem> + <para> + For composed types that can take a submodule as type + parameter, this function should return the type parameters + submodules. If the type parameter is called + <literal>elemType</literal>, the function should just + recursively look into submodules by returning + <literal>elemType.getSubModules;</literal>. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>substSubModules</literal> + </term> + <listitem> + <para> + For composed types that can take a submodule as type + parameter, this function can be used to substitute the + parameter of a submodule type. It takes a module as + parameter and return the type with the submodule options + substituted. It is usually defined as a type function call + with a recursive call to <literal>substSubModules</literal>, + e.g for a type <literal>composedType</literal> that take an + <literal>elemtype</literal> type parameter, this function + should be defined as + <literal>m: composedType (elemType.substSubModules m)</literal>. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>typeMerge</literal> + </term> + <listitem> + <para> + A function to merge multiple type declarations. Takes the + type to merge <literal>functor</literal> as parameter. A + <literal>null</literal> return value means that type cannot + be merged. + </para> + <variablelist> + <varlistentry> + <term> + <emphasis><literal>f</literal></emphasis> + </term> + <listitem> + <para> + The type to merge <literal>functor</literal>. + </para> + </listitem> + </varlistentry> + </variablelist> + <para> + Note: There is a generic <literal>defaultTypeMerge</literal> + that work with most of value and composed types. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>functor</literal> + </term> + <listitem> + <para> + An attribute set representing the type. It is used for type + operations and has the following keys: + </para> + <variablelist> + <varlistentry> + <term> + <literal>type</literal> + </term> + <listitem> + <para> + The type function. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>wrapped</literal> + </term> + <listitem> + <para> + Holds the type parameter for composed types. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>payload</literal> + </term> + <listitem> + <para> + Holds the value parameter for value types. The types + that have a <literal>payload</literal> are the + <literal>enum</literal>, + <literal>separatedString</literal> and + <literal>submodule</literal> types. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>binOp</literal> + </term> + <listitem> + <para> + A binary operation that can merge the payloads of two + same types. Defined as a function that take two + payloads as parameters and return the payloads merged. + </para> + </listitem> + </varlistentry> + </variablelist> + </listitem> + </varlistentry> + </variablelist> + </section> +</section> diff --git a/nixos/doc/manual/from_md/development/replace-modules.section.xml b/nixos/doc/manual/from_md/development/replace-modules.section.xml new file mode 100644 index 00000000000..cf8a39ba844 --- /dev/null +++ b/nixos/doc/manual/from_md/development/replace-modules.section.xml @@ -0,0 +1,70 @@ +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-replace-modules"> + <title>Replace Modules</title> + <para> + Modules that are imported can also be disabled. The option + declarations, config implementation and the imports of a disabled + module will be ignored, allowing another to take it's place. This + can be used to import a set of modules from another channel while + keeping the rest of the system on a stable release. + </para> + <para> + <literal>disabledModules</literal> is a top level attribute like + <literal>imports</literal>, <literal>options</literal> and + <literal>config</literal>. It contains a list of modules that will + be disabled. This can either be the full path to the module or a + string with the filename relative to the modules path (eg. + <nixpkgs/nixos/modules> for nixos). + </para> + <para> + This example will replace the existing postgresql module with the + version defined in the nixos-unstable channel while keeping the rest + of the modules and packages from the original nixos channel. This + only overrides the module definition, this won't use postgresql from + nixos-unstable unless explicitly configured to do so. + </para> + <programlisting language="bash"> +{ config, lib, pkgs, ... }: + +{ + disabledModules = [ "services/databases/postgresql.nix" ]; + + imports = + [ # Use postgresql service from nixos-unstable channel. + # sudo nix-channel --add https://nixos.org/channels/nixos-unstable nixos-unstable + <nixos-unstable/nixos/modules/services/databases/postgresql.nix> + ]; + + services.postgresql.enable = true; +} +</programlisting> + <para> + This example shows how to define a custom module as a replacement + for an existing module. Importing this module will disable the + original module without having to know it's implementation details. + </para> + <programlisting language="bash"> +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.man; +in + +{ + disabledModules = [ "services/programs/man.nix" ]; + + options = { + programs.man.enable = mkOption { + type = types.bool; + default = true; + description = "Whether to enable manual pages."; + }; + }; + + config = mkIf cfg.enabled { + warnings = [ "disabled manpages for production deployments." ]; + }; +} +</programlisting> +</section> diff --git a/nixos/doc/manual/from_md/development/settings-options.section.xml b/nixos/doc/manual/from_md/development/settings-options.section.xml new file mode 100644 index 00000000000..c9430b77579 --- /dev/null +++ b/nixos/doc/manual/from_md/development/settings-options.section.xml @@ -0,0 +1,285 @@ +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-settings-options"> + <title>Options for Program Settings</title> + <para> + Many programs have configuration files where program-specific + settings can be declared. File formats can be separated into two + categories: + </para> + <itemizedlist> + <listitem> + <para> + Nix-representable ones: These can trivially be mapped to a + subset of Nix syntax. E.g. JSON is an example, since its values + like <literal>{"foo":{"bar":10}}</literal> + can be mapped directly to Nix: + <literal>{ foo = { bar = 10; }; }</literal>. Other examples are + INI, YAML and TOML. The following section explains the + convention for these settings. + </para> + </listitem> + <listitem> + <para> + 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 + <literal>if true; then echo hi; fi</literal> doesn't have a + trivial representation in Nix. + </para> + <para> + Currently there are no fixed conventions for these, but it is + common to have a <literal>configFile</literal> option for + setting the configuration file path directly. The default value + of <literal>configFile</literal> can be an auto-generated file, + with convenient options for controlling the contents. For + example an option of type <literal>attrsOf str</literal> can be + used for representing environment variables which generates a + section like <literal>export FOO="foo"</literal>. + Often it can also be useful to also include an + <literal>extraConfig</literal> option of type + <literal>lines</literal> to allow arbitrary text after the + autogenerated part of the file. + </para> + </listitem> + </itemizedlist> + <section xml:id="sec-settings-nix-representable"> + <title>Nix-representable Formats (JSON, YAML, TOML, INI, + ...)</title> + <para> + By convention, formats like this are handled with a generic + <literal>settings</literal> 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 + <literal>pkgs.formats</literal>: + </para> + <variablelist> + <varlistentry> + <term> + <literal>pkgs.formats.json</literal> { } + </term> + <listitem> + <para> + A function taking an empty attribute set (for future + extensibility) and returning a set with JSON-specific + attributes <literal>type</literal> and + <literal>generate</literal> as specified + <link linkend="pkgs-formats-result">below</link>. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>pkgs.formats.yaml</literal> { } + </term> + <listitem> + <para> + A function taking an empty attribute set (for future + extensibility) and returning a set with YAML-specific + attributes <literal>type</literal> and + <literal>generate</literal> as specified + <link linkend="pkgs-formats-result">below</link>. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>pkgs.formats.ini</literal> { + <emphasis><literal>listsAsDuplicateKeys</literal></emphasis> ? + false, <emphasis><literal>listToValue</literal></emphasis> ? + null, ... } + </term> + <listitem> + <para> + A function taking an attribute set with values + </para> + <variablelist> + <varlistentry> + <term> + <literal>listsAsDuplicateKeys</literal> + </term> + <listitem> + <para> + A boolean for controlling whether list values can be + used to represent duplicate INI keys + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>listToValue</literal> + </term> + <listitem> + <para> + A function for turning a list of values into a single + value. + </para> + </listitem> + </varlistentry> + </variablelist> + <para> + It returns a set with INI-specific attributes + <literal>type</literal> and <literal>generate</literal> as + specified <link linkend="pkgs-formats-result">below</link>. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>pkgs.formats.toml</literal> { } + </term> + <listitem> + <para> + A function taking an empty attribute set (for future + extensibility) and returning a set with TOML-specific + attributes <literal>type</literal> and + <literal>generate</literal> as specified + <link linkend="pkgs-formats-result">below</link>. + </para> + </listitem> + </varlistentry> + </variablelist> + <para xml:id="pkgs-formats-result"> + These functions all return an attribute set with these values: + </para> + <variablelist> + <varlistentry> + <term> + <literal>type</literal> + </term> + <listitem> + <para> + A module system type representing a value of the format + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <literal>generate</literal> + <emphasis><literal>filename jsonValue</literal></emphasis> + </term> + <listitem> + <para> + A function that can render a value of the format to a file. + Returns a file path. + </para> + <note> + <para> + This function puts the value contents in the Nix store. So + this should be avoided for secrets. + </para> + </note> + </listitem> + </varlistentry> + </variablelist> + <anchor xml:id="ex-settings-nix-representable" /> + <para> + <emphasis role="strong">Example: Module with conventional + <literal>settings</literal> option</emphasis> + </para> + <para> + 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. + </para> + <programlisting language="bash"> +{ 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; }; + + # ... + }; +} +</programlisting> + <section xml:id="sec-settings-attrs-options"> + <title>Option declarations for attributes</title> + <para> + Some <literal>settings</literal> 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 + <xref linkend="sec-freeform-modules" />. + </para> + <para> + 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. + </para> + <anchor xml:id="ex-settings-typed-attrs" /> + <para> + <emphasis role="strong">Example: Declaring a type-checked + <literal>settings</literal> attribute</emphasis> + </para> + <programlisting language="bash"> +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. + ''; +}; +</programlisting> + </section> + </section> +</section> |