nixos/dconf: add settings support

linsui fb52d5df cce75fa5

Changed files
+158 -5
lib
nixos
modules
programs
+8
lib/generators.nix
···
in
toINI_ (gitFlattenAttrs attrs);
+
# mkKeyValueDefault wrapper that handles dconf INI quirks.
+
# The main differences of the format is that it requires strings to be quoted.
+
mkDconfKeyValue = mkKeyValueDefault { mkValueString = v: toString (lib.gvariant.mkValue v); } "=";
+
+
# Generates INI in dconf keyfile style. See https://help.gnome.org/admin/system-admin-guide/stable/dconf-keyfiles.html.en
+
# for details.
+
toDconfINI = toINI { mkKeyValue = mkDconfKeyValue; };
+
/* Generates JSON from an arbitrary (non-function) value.
* For more information see the documentation of the builtin.
*/
+150 -5
nixos/modules/programs/dconf.nix
···
let
cfg = config.programs.dconf;
+
# Compile keyfiles to dconf DB
+
compileDconfDb = dir: pkgs.runCommand "dconf-db"
+
{
+
nativeBuildInputs = [ (lib.getBin pkgs.dconf) ];
+
} "dconf compile $out ${dir}";
+
+
# Check if dconf keyfiles are valid
+
checkDconfKeyfiles = dir: pkgs.runCommand "check-dconf-keyfiles"
+
{
+
nativeBuildInputs = [ (lib.getBin pkgs.dconf) ];
+
} ''
+
if [[ -f ${dir} ]]; then
+
echo "dconf keyfiles should be a directory but a file is provided: ${dir}"
+
exit 1
+
fi
+
+
dconf compile db ${dir} || (
+
echo "The dconf keyfiles are invalid: ${dir}"
+
exit 1
+
)
+
cp -R ${dir} $out
+
'';
+
+
# Generate dconf DB from dconfDatabase and keyfiles
+
mkDconfDb = val: compileDconfDb (pkgs.symlinkJoin {
+
name = "nixos-generated-dconf-keyfiles";
+
paths = [
+
(pkgs.writeTextDir "nixos-generated-dconf-keyfiles" (lib.generators.toDconfINI val.settings))
+
] ++ (map checkDconfKeyfiles val.keyfiles);
+
});
+
+
# Check if a dconf DB file is valid. The dconf cli doesn't return 1 when it can't
+
# open the database file so we have to check if the output is empty.
+
checkDconfDb = file: pkgs.runCommand "check-dconf-db"
+
{
+
nativeBuildInputs = [ (lib.getBin pkgs.dconf) ];
+
} ''
+
if [[ -d ${file} ]]; then
+
echo "dconf DB should be a file but a directory is provided: ${file}"
+
exit 1
+
fi
+
+
echo "file-db:${file}" > profile
+
DCONF_PROFILE=$(pwd)/profile dconf dump / > output 2> error
+
if [[ ! -s output ]] && [[ -s error ]]; then
+
cat error
+
echo "The dconf DB file is invalid: ${file}"
+
exit 1
+
fi
+
+
cp ${file} $out
+
'';
+
# Generate dconf profile
mkDconfProfile = name: value:
-
pkgs.runCommand "dconf-profile" { } ''
-
mkdir -p $out/etc/dconf/profile/
-
cp ${value} $out/etc/dconf/profile/${name}
-
'';
+
if lib.isDerivation value || lib.isPath value then
+
pkgs.runCommand "dconf-profile" { } ''
+
if [[ -d ${value} ]]; then
+
echo "Dconf profile should be a file but a directory is provided."
+
exit 1
+
fi
+
mkdir -p $out/etc/dconf/profile/
+
cp ${value} $out/etc/dconf/profile/${name}
+
''
+
else
+
pkgs.writeTextDir "etc/dconf/profile/${name}" (
+
lib.concatMapStrings (x: "${x}\n") ((
+
lib.optional value.enableUserDb "user-db:user"
+
) ++ (
+
map
+
(value:
+
let
+
db = if lib.isAttrs value && !lib.isDerivation value then mkDconfDb value else checkDconfDb value;
+
in
+
"file-db:${db}")
+
value.databases
+
))
+
);
+
+
dconfDatabase = with lib.types; submodule {
+
options = {
+
keyfiles = lib.mkOption {
+
type = listOf (oneOf [ path package ]);
+
default = [ ];
+
description = lib.mdDoc "A list of dconf keyfile directories.";
+
};
+
settings = lib.mkOption {
+
type = attrs;
+
default = { };
+
description = lib.mdDoc "An attrset used to generate dconf keyfile.";
+
example = literalExpression ''
+
with lib.gvariant;
+
{
+
"com/raggesilver/BlackBox" = {
+
scrollback-lines = mkUint32 10000;
+
theme-dark = "Tommorow Night";
+
};
+
}
+
'';
+
};
+
};
+
};
+
+
dconfProfile = with lib.types; submodule {
+
options = {
+
enableUserDb = lib.mkOption {
+
type = bool;
+
default = true;
+
description = lib.mdDoc "Add `user-db:user` at the beginning of the profile.";
+
};
+
+
databases = lib.mkOption {
+
type = with lib.types; listOf (oneOf [
+
path
+
package
+
dconfDatabase
+
]);
+
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. Each element is converted
+
to a file-db.
+
+
A key is searched from up to down and the first result takes the
+
priority. If a lock for a particular key is installed then the value from
+
the last database in the profile where the key is locked will be used.
+
This can be used to enforce mandatory settings.
+
'';
+
};
+
};
+
};
+
in
{
options = {
···
type = with lib.types; attrsOf (oneOf [
path
package
+
dconfProfile
]);
-
description = lib.mdDoc "Attrset of dconf profiles.";
+
default = { };
+
description = lib.mdDoc ''
+
Attrset of dconf profiles. By default the `user` profile is used which
+
ends up in `/etc/dconf/profile/user`.
+
'';
+
example = lib.literalExpression ''
+
{
+
# A "user" profile with a database
+
user.databases = [
+
{
+
settings = { };
+
}
+
];
+
# A "bar" profile from a package
+
bar = pkgs.bar-dconf-profile;
+
# A "foo" profile from a path
+
foo = ''${./foo}
+
};
+
'';
};
packages = lib.mkOption {