diff --git a/lib/dconf.nix b/lib/dconf.nix new file mode 100644 index 0000000000000..8dd5e3ee26e78 --- /dev/null +++ b/lib/dconf.nix @@ -0,0 +1,15 @@ +{ lib }: + +# This module contains helpers for the `programs.dconf` NixOS module + +rec { + types = { + tuple = "_tuple"; + }; + + mkTuple = _elements: { + inherit _elements; + + _type = types.tuple; + }; +} diff --git a/lib/default.nix b/lib/default.nix index 8bb06954518b9..4791dbd628975 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -42,6 +42,7 @@ let # serialization cli = callLibs ./cli.nix; + dconf = callLibs ./dconf.nix; generators = callLibs ./generators.nix; # misc diff --git a/lib/generators.nix b/lib/generators.nix index b77cca75010f9..6f51d686647dc 100644 --- a/lib/generators.nix +++ b/lib/generators.nix @@ -175,6 +175,18 @@ rec { + "\n") + (toINI { inherit mkSectionName mkKeyValue listsAsDuplicateKeys; } sections); + # converts { a.b.c = 5; } to { "a.b".c = 5; } for toINI + flattenAttrs = with builtins; extraIsLeafPredicate: sep: let + recurse = path: value: + if isAttrs value && !lib.isDerivation value && !extraIsLeafPredicate value then + lib.mapAttrsToList (name: value: recurse ([ name ] ++ path) value) value + else if length path > 1 then { + ${concatStringsSep sep (lib.reverseList (tail path))}.${head path} = value; + } else { + ${head path} = value; + }; + in attrs: lib.foldl lib.recursiveUpdate { } (lib.flatten (recurse [ ] attrs)); + /* Generate a git-config file from an attrset. * * It has two major differences from the regular INI format: @@ -213,21 +225,27 @@ rec { let mkKeyValue = mkKeyValueDefault { } " = " k; in concatStringsSep "\n" (map (kv: "\t" + mkKeyValue kv) (lib.toList v)); - # converts { a.b.c = 5; } to { "a.b".c = 5; } for toINI - gitFlattenAttrs = let - recurse = path: value: - if isAttrs value && !lib.isDerivation value then - lib.mapAttrsToList (name: value: recurse ([ name ] ++ path) value) value - else if length path > 1 then { - ${concatStringsSep "." (lib.reverseList (tail path))}.${head path} = value; - } else { - ${head path} = value; - }; - in attrs: lib.foldl lib.recursiveUpdate { } (lib.flatten (recurse [ ] attrs)); - toINI_ = toINI { inherit mkKeyValue mkSectionName; }; in - toINI_ (gitFlattenAttrs attrs); + toINI_ (flattenAttrs (_: false) "." attrs); + + /* mkValueStringDefault wrapper that handles dconf INI quirks. */ + mkValueStringDconf = v: + if builtins.isList v then + "[${lib.concatMapStringsSep ", " mkValueStringDconf v}]" + else if lib.types.isType lib.dconf.types.tuple v then + "(${lib.concatMapStringsSep ", " mkValueStringDconf v._elements})" + else if builtins.isString v then + "'${v}'" + else mkValueStringDefault {} v; + + /* mkKeyValueDefault wrapper that handles dconf INI quirks. */ + mkKeyValueDconf = mkKeyValueDefault { mkValueString = mkValueStringDconf; } "="; + + /* Generates INI in dconf keyfile style. + * The main differences of the format is that it requires strings to be quoted and has a tuple type (`lib.dconf.mkTuple`). + */ + toDconfINI = attrs: toINI { mkKeyValue = mkKeyValueDconf; } (flattenAttrs (x: lib.types.isType lib.dconf.types.tuple x) "/" attrs); /* Generates JSON from an arbitrary (non-function) value. * For more information see the documentation of the builtin. diff --git a/nixos/doc/manual/from_md/release-notes/rl-2211.section.xml b/nixos/doc/manual/from_md/release-notes/rl-2211.section.xml index c53474144d270..bfc7d405f25cb 100644 --- a/nixos/doc/manual/from_md/release-notes/rl-2211.section.xml +++ b/nixos/doc/manual/from_md/release-notes/rl-2211.section.xml @@ -125,6 +125,18 @@ would in NixOS 22.05 and earlier. + + + The programs.dconf module now supports + configuration using the Nix language. This allows you to + configure settings of apps and DEs (if they use + dconf) declaratively without having to use + extraGSettingsOverrides which has some + problems noted in + this + issue. + + PHP now defaults to PHP 8.1, updated from 8.0. @@ -651,6 +663,14 @@ emacs-gtk. + + + The option programs.dconf.packages has been + removed, use + programs.dconf.profiles.user.databases + instead. + + riak package removed along with diff --git a/nixos/doc/manual/release-notes/rl-2211.section.md b/nixos/doc/manual/release-notes/rl-2211.section.md index 863bf95b55f3d..1c202fd8f9682 100644 --- a/nixos/doc/manual/release-notes/rl-2211.section.md +++ b/nixos/doc/manual/release-notes/rl-2211.section.md @@ -51,6 +51,11 @@ In addition to numerous new and upgraded packages, this release has the followin Alternatively, you can remove the `hostPlatform` line and use NixOS like you would in NixOS 22.05 and earlier. +- The `programs.dconf` module now supports configuration using the Nix language. + This allows you to configure settings of apps and DEs (if they use `dconf`) + declaratively without having to use `extraGSettingsOverrides` which has some + problems noted in [this issue](https://github.com/NixOS/nixpkgs/issues/54150). + - PHP now defaults to PHP 8.1, updated from 8.0. - `protonup` has been aliased to and replaced by `protonup-ng` due to upstream not maintaining it. @@ -212,6 +217,8 @@ Available as [services.patroni](options.html#opt-services.patroni.enable). - Emacs now uses the Lucid toolkit by default instead of GTK because of stability and compatibility issues. Users who still wish to remain using GTK can do so by using `emacs-gtk`. +- The option `programs.dconf.packages` has been removed, use `programs.dconf.profiles.user.databases` instead. + - riak package removed along with `services.riak` module, due to lack of maintainer to update the package. - ppd files in `pkgs.cups-drv-rastertosag-gdi` are now gzipped. If you refer to such a ppd file with its path (e.g. via [hardware.printers.ensurePrinters](options.html#opt-hardware.printers.ensurePrinters)) you will need to append `.gz` to the path. diff --git a/nixos/modules/i18n/input-method/ibus.nix b/nixos/modules/i18n/input-method/ibus.nix index 520db128acd9f..2acf27eeb3c04 100644 --- a/nixos/modules/i18n/input-method/ibus.nix +++ b/nixos/modules/i18n/input-method/ibus.nix @@ -66,7 +66,7 @@ in # Without dconf enabled it is impossible to use IBus programs.dconf.enable = true; - programs.dconf.packages = [ ibusPackage ]; + programs.dconf.profiles.ibus.databases = [ (pkgs.dconf-utils.mkDconfDb "${ibusPackage}/etc/dconf/db/ibus.d") ]; services.dbus.packages = [ ibusPackage diff --git a/nixos/modules/programs/dconf.nix b/nixos/modules/programs/dconf.nix index 7261a143528ff..802cf82af671b 100644 --- a/nixos/modules/programs/dconf.nix +++ b/nixos/modules/programs/dconf.nix @@ -1,56 +1,55 @@ { config, lib, pkgs, ... }: -with lib; - let cfg = config.programs.dconf; - cfgDir = pkgs.symlinkJoin { - name = "dconf-system-config"; - paths = map (x: "${x}/etc/dconf") cfg.packages; - postBuild = '' - mkdir -p $out/profile - mkdir -p $out/db - '' + ( - concatStringsSep "\n" ( - mapAttrsToList ( - name: path: '' - ln -s ${path} $out/profile/${name} - '' - ) cfg.profiles - ) - ) + '' - ${pkgs.dconf}/bin/dconf update $out/db - ''; - }; + + asFileDb = val: + let db = + if lib.isAttrs val && !lib.isDerivation val then + pkgs.dconf-utils.mkDconfDb "${pkgs.writeTextDir "dconf/db" (lib.generators.toDconfINI val)}/dconf" + else val; + in "file-db:${db}"; in { - ###### interface + imports = [ + (lib.mkRemovedOptionModule [ "programs" "dconf" "packages" ] "This option is not supported anymore, you should use `programs.dconf.profiles..databases` instead.") + ]; options = { programs.dconf = { - enable = mkEnableOption (lib.mdDoc "dconf"); + enable = lib.mkEnableOption (lib.mdDoc "dconf"); + + profiles = lib.mkOption { + type = with lib.types; attrsOf (submodule { + options = { + enableUserDb = lib.mkOption { + type = bool; + default = true; + description = lib.mdDoc "Add `user-db:user` at the beginning of the profile."; + }; - profiles = mkOption { - type = types.attrsOf types.path; - default = {}; - description = lib.mdDoc "Set of dconf profile files, installed at {file}`/etc/dconf/profiles/«name»`."; - internal = true; + databases = lib.mkOption { + type = with lib.types; listOf (oneOf [ attrs str path package ]); + default = []; + description = lib.mdDoc "List of data sources for the profile. An element can be an attrset, or the path of an already compiled database."; + }; + }; + }); + description = lib.mdDoc "Attrset of dconf profiles."; }; - packages = mkOption { - type = types.listOf types.package; - default = []; - description = lib.mdDoc "A list of packages which provide dconf profiles and databases in {file}`/etc/dconf`."; + defaultProfile = lib.mkOption { + type = with lib.types; nullOr str; + default = null; + description = lib.mdDoc "The default dconf profile."; }; }; }; - ###### implementation - - config = mkIf (cfg.profiles != {} || cfg.enable) { - environment.etc.dconf = mkIf (cfg.profiles != {} || cfg.packages != []) { - source = cfgDir; - }; + config = lib.mkIf cfg.enable { + environment.etc = lib.attrsets.mapAttrs' (name: value: lib.nameValuePair "dconf/profile/${name}" { + text = lib.concatMapStrings (x: "${x}\n") ((lib.optional value.enableUserDb "user-db:user") ++ (map asFileDb value.databases)); + }) cfg.profiles; services.dbus.packages = [ pkgs.dconf ]; @@ -59,8 +58,9 @@ in # For dconf executable environment.systemPackages = [ pkgs.dconf ]; - # Needed for unwrapped applications - environment.sessionVariables.GIO_EXTRA_MODULES = mkIf cfg.enable [ "${pkgs.dconf.lib}/lib/gio/modules" ]; + environment.sessionVariables = { + # Needed for unwrapped applications + GIO_EXTRA_MODULES = [ "${pkgs.dconf.lib}/lib/gio/modules" ]; + } // (if cfg.defaultProfile != null then { DCONF_PROFILE = cfg.defaultProfile; } else {}); }; - } diff --git a/nixos/modules/services/x11/display-managers/gdm.nix b/nixos/modules/services/x11/display-managers/gdm.nix index 1c3881bef2de2..f6699eb84d819 100644 --- a/nixos/modules/services/x11/display-managers/gdm.nix +++ b/nixos/modules/services/x11/display-managers/gdm.nix @@ -229,39 +229,13 @@ in systemd.user.services.dbus.wantedBy = [ "default.target" ]; - programs.dconf.profiles.gdm = - let - customDconf = pkgs.writeTextFile { - name = "gdm-dconf"; - destination = "/dconf/gdm-custom"; - text = '' - ${optionalString (!cfg.gdm.autoSuspend) '' - [org/gnome/settings-daemon/plugins/power] - sleep-inactive-ac-type='nothing' - sleep-inactive-battery-type='nothing' - sleep-inactive-ac-timeout=0 - sleep-inactive-battery-timeout=0 - ''} - ''; + programs.dconf.profiles.gdm.databases = [ "${gdm}/share/gdm/greeter-dconf-defaults" ] ++ lib.lists.optional cfg.gdm.autoSuspend { + org.gnome.settings-daemon.plugins.power = { + sleep-inactive-ac-type = "nothing"; + sleep-inactive-battery-type = "nothing"; + sleep-inactive-ac-timeout = 0; + sleep-inactive-battery-timeout = 0; }; - - customDconfDb = pkgs.stdenv.mkDerivation { - name = "gdm-dconf-db"; - buildCommand = '' - ${pkgs.dconf}/bin/dconf compile $out ${customDconf}/dconf - ''; - }; - in pkgs.stdenv.mkDerivation { - name = "dconf-gdm-profile"; - buildCommand = '' - # Check that the GDM profile starts with what we expect. - if [ $(head -n 1 ${gdm}/share/dconf/profile/gdm) != "user-db:user" ]; then - echo "GDM dconf profile changed, please update gdm.nix" - exit 1 - fi - # Insert our custom DB behind it. - sed '2ifile-db:${customDconfDb}' ${gdm}/share/dconf/profile/gdm > $out - ''; }; # Use AutomaticLogin if delay is zero, because it's immediate. diff --git a/pkgs/development/libraries/dconf/utils.nix b/pkgs/development/libraries/dconf/utils.nix new file mode 100644 index 0000000000000..d7c2955a241b2 --- /dev/null +++ b/pkgs/development/libraries/dconf/utils.nix @@ -0,0 +1,10 @@ +{ runCommand, dconf }: + +{ + # Builds a dconf database from a keyfile directory + mkDconfDb = dir: runCommand "dconf-db" { + nativeBuildInputs = [ dconf ]; + } '' + dconf compile $out ${dir} + ''; +} diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index 05d687b152e39..ba60e14af69a9 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -3703,6 +3703,8 @@ with pkgs; dconf = callPackage ../development/libraries/dconf { }; + dconf-utils = callPackage ../development/libraries/dconf/utils.nix { }; + ddate = callPackage ../tools/misc/ddate { }; ddosify = callPackage ../development/tools/ddosify { };