nixos/dconf: add settings support

This commit is contained in:
linsui 2023-08-15 17:58:02 +08:00
parent cce75fa51e
commit fb52d5df86
2 changed files with 158 additions and 5 deletions

View File

@ -230,6 +230,14 @@ rec {
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.
*/

View File

@ -3,12 +3,138 @@
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 = {
@ -19,8 +145,27 @@ in
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 {