grafana-alloy: don't build the frontend twice
[NixPkgs.git] / nixos / modules / programs / dconf.nix
blobe6738617c8e35d405d57e5c43689166665bbd1a4
1 { config, lib, pkgs, ... }:
3 let
4   cfg = config.programs.dconf;
6   # Compile keyfiles to dconf DB
7   compileDconfDb = dir: pkgs.runCommand "dconf-db"
8     {
9       nativeBuildInputs = [ (lib.getBin pkgs.dconf) ];
10     } "dconf compile $out ${dir}";
12   # Check if dconf keyfiles are valid
13   checkDconfKeyfiles = dir: pkgs.runCommand "check-dconf-keyfiles"
14     {
15       nativeBuildInputs = [ (lib.getBin pkgs.dconf) ];
16     } ''
17     if [[ -f ${dir} ]]; then
18       echo "dconf keyfiles should be a directory but a file is provided: ${dir}"
19       exit 1
20     fi
22     dconf compile db ${dir} || (
23       echo "The dconf keyfiles are invalid: ${dir}"
24       exit 1
25     )
26     cp -R ${dir} $out
27   '';
29   mkAllLocks = settings: lib.flatten (
30     lib.mapAttrsToList (k: v: lib.mapAttrsToList (k': _: "/${k}/${k'}") v) settings);
32   # Generate dconf DB from dconfDatabase and keyfiles
33   mkDconfDb = val: compileDconfDb (pkgs.symlinkJoin {
34     name = "nixos-generated-dconf-keyfiles";
35     paths = [
36       (pkgs.writeTextDir "nixos-generated-dconf-keyfiles" (lib.generators.toDconfINI val.settings))
37       (pkgs.writeTextDir "locks/nixos-generated-dconf-locks" (lib.concatStringsSep "\n"
38         (if val.lockAll then mkAllLocks val.settings else val.locks)
39       ))
40     ] ++ (map checkDconfKeyfiles val.keyfiles);
41   });
43   # Check if a dconf DB file is valid. The dconf cli doesn't return 1 when it can't
44   # open the database file so we have to check if the output is empty.
45   checkDconfDb = file: pkgs.runCommand "check-dconf-db"
46     {
47       nativeBuildInputs = [ (lib.getBin pkgs.dconf) ];
48     } ''
49     if [[ -d ${file} ]]; then
50       echo "dconf DB should be a file but a directory is provided: ${file}"
51       exit 1
52     fi
54     echo "file-db:${file}" > profile
55     DCONF_PROFILE=$(pwd)/profile dconf dump / > output 2> error
56     if [[ ! -s output ]] && [[ -s error ]]; then
57       cat error
58       echo "The dconf DB file is invalid: ${file}"
59       exit 1
60     fi
62     cp ${file} $out
63   '';
65   # Generate dconf profile
66   mkDconfProfile = name: value:
67     if lib.isDerivation value || lib.isPath value then
68       pkgs.runCommand "dconf-profile" { } ''
69         if [[ -d ${value} ]]; then
70           echo "Dconf profile should be a file but a directory is provided."
71           exit 1
72         fi
73         mkdir -p $out/etc/dconf/profile/
74         cp ${value} $out/etc/dconf/profile/${name}
75       ''
76     else
77       pkgs.writeTextDir "etc/dconf/profile/${name}" (
78         lib.concatMapStrings (x: "${x}\n") ((
79           lib.optional value.enableUserDb "user-db:user"
80         ) ++ (
81           map
82             (value:
83               let
84                 db = if lib.isAttrs value && !lib.isDerivation value then mkDconfDb value else checkDconfDb value;
85               in
86               "file-db:${db}")
87             value.databases
88         ))
89       );
91   dconfDatabase = with lib.types; submodule {
92     options = {
93       keyfiles = lib.mkOption {
94         type = listOf (oneOf [ path package ]);
95         default = [ ];
96         description = "A list of dconf keyfile directories.";
97       };
98       settings = lib.mkOption {
99         type = attrs;
100         default = { };
101         description = "An attrset used to generate dconf keyfile.";
102         example = literalExpression ''
103           with lib.gvariant;
104           {
105             "com/raggesilver/BlackBox" = {
106               scrollback-lines = mkUint32 10000;
107               theme-dark = "Tommorow Night";
108             };
109           }
110         '';
111       };
112       locks = lib.mkOption {
113         type = with lib.types; listOf str;
114         default = [ ];
115         description = ''
116           A list of dconf keys to be lockdown. This doesn't take effect if `lockAll`
117           is set.
118         '';
119         example = literalExpression ''
120           [ "/org/gnome/desktop/background/picture-uri" ]
121         '';
122       };
123       lockAll = lib.mkOption {
124         type = lib.types.bool;
125         default = false;
126         description = "Lockdown all dconf keys in `settings`.";
127       };
128     };
129   };
131   dconfProfile = with lib.types; submodule {
132     options = {
133       enableUserDb = lib.mkOption {
134         type = bool;
135         default = true;
136         description = "Add `user-db:user` at the beginning of the profile.";
137       };
139       databases = lib.mkOption {
140         type = with lib.types; listOf (oneOf [
141           path
142           package
143           dconfDatabase
144         ]);
145         default = [ ];
146         description = ''
147           List of data sources for the profile. An element can be an attrset,
148           or the path of an already compiled database. Each element is converted
149           to a file-db.
151           A key is searched from up to down and the first result takes the
152           priority. If a lock for a particular key is installed then the value from
153           the last database in the profile where the key is locked will be used.
154           This can be used to enforce mandatory settings.
155         '';
156       };
157     };
158   };
162   options = {
163     programs.dconf = {
164       enable = lib.mkEnableOption "dconf";
166       profiles = lib.mkOption {
167         type = with lib.types; attrsOf (oneOf [
168           path
169           package
170           dconfProfile
171         ]);
172         default = { };
173         description = ''
174           Attrset of dconf profiles. By default the `user` profile is used which
175           ends up in `/etc/dconf/profile/user`.
176         '';
177         example = lib.literalExpression ''
178           {
179             # A "user" profile with a database
180             user.databases = [
181               {
182                 settings = { };
183               }
184             ];
185             # A "bar" profile from a package
186             bar = pkgs.bar-dconf-profile;
187             # A "foo" profile from a path
188             foo = ''${./foo}
189           };
190         '';
191       };
193       packages = lib.mkOption {
194         type = lib.types.listOf lib.types.package;
195         default = [ ];
196         description = "A list of packages which provide dconf profiles and databases in {file}`/etc/dconf`.";
197       };
198     };
199   };
201   config = lib.mkIf (cfg.profiles != { } || cfg.enable) {
202     programs.dconf.packages = lib.mapAttrsToList mkDconfProfile cfg.profiles;
204     environment.etc.dconf = lib.mkIf (cfg.packages != [ ]) {
205       source = pkgs.symlinkJoin {
206         name = "dconf-system-config";
207         paths = map (x: "${x}/etc/dconf") cfg.packages;
208         nativeBuildInputs = [ (lib.getBin pkgs.dconf) ];
209         postBuild = ''
210           if test -d $out/db; then
211             dconf update $out/db
212           fi
213         '';
214       };
215     };
217     services.dbus.packages = [ pkgs.dconf ];
219     systemd.packages = [ pkgs.dconf ];
221     # For dconf executable
222     environment.systemPackages = [ pkgs.dconf ];
224     environment.sessionVariables = lib.mkIf cfg.enable {
225       # Needed for unwrapped applications
226       GIO_EXTRA_MODULES = [ "${pkgs.dconf.lib}/lib/gio/modules" ];
227     };
228   };