Merge pull request #234615 from linsui/dconf

nixos/dconf: support generating from attrs
This commit is contained in:
Jan Tojnar 2023-08-28 18:31:52 +02:00 committed by GitHub
commit 434d160d7c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 664 additions and 87 deletions

View File

@ -21,6 +21,7 @@ let
{ name = "filesystem"; description = "filesystem functions"; }
{ name = "sources"; description = "source filtering functions"; }
{ name = "cli"; description = "command-line serialization functions"; }
{ name = "gvariant"; description = "GVariant formatted string serialization functions"; }
];
};

View File

@ -41,6 +41,7 @@ let
# serialization
cli = callLibs ./cli.nix;
gvariant = callLibs ./gvariant.nix;
generators = callLibs ./generators.nix;
# misc

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.
*/

290
lib/gvariant.nix Normal file
View File

@ -0,0 +1,290 @@
# This file is based on https://github.com/nix-community/home-manager
# Copyright (c) 2017-2022 Home Manager contributors
#
{ lib }:
/* A partial and basic implementation of GVariant formatted strings.
See https://docs.gtk.org/glib/gvariant-format-strings.html for detauls.
Note, this API is not considered fully stable and it might therefore
change in backwards incompatible ways without prior notice.
*/
let
inherit (lib)
concatMapStringsSep concatStrings escape head replaceStrings;
mkPrimitive = t: v: {
_type = "gvariant";
type = t;
value = v;
__toString = self: "@${self.type} ${toString self.value}"; # https://docs.gtk.org/glib/gvariant-text.html
};
type = {
arrayOf = t: "a${t}";
maybeOf = t: "m${t}";
tupleOf = ts: "(${concatStrings ts})";
dictionaryEntryOf = nameType: valueType: "{${nameType}${valueType}}";
string = "s";
boolean = "b";
uchar = "y";
int16 = "n";
uint16 = "q";
int32 = "i";
uint32 = "u";
int64 = "x";
uint64 = "t";
double = "d";
variant = "v";
};
/* Check if a value is a GVariant value
Type:
isGVariant :: Any -> Bool
*/
isGVariant = v: v._type or "" == "gvariant";
in
rec {
inherit type isGVariant;
/* Returns the GVariant value that most closely matches the given Nix value.
If no GVariant value can be found unambiguously then error is thrown.
Type:
mkValue :: Any -> gvariant
*/
mkValue = v:
if builtins.isBool v then
mkBoolean v
else if builtins.isFloat v then
mkDouble v
else if builtins.isString v then
mkString v
else if builtins.isList v then
mkArray v
else if isGVariant v then
v
else
throw "The GVariant type of ${v} can't be inferred.";
/* Returns the GVariant array from the given type of the elements and a Nix list.
Type:
mkArray :: [Any] -> gvariant
Example:
# Creating a string array
lib.gvariant.mkArray [ "a" "b" "c" ]
*/
mkArray = elems:
let
vs = map mkValue (lib.throwIf (elems == [ ]) "Please create empty array with mkEmptyArray." elems);
elemType = lib.throwIfNot (lib.all (t: (head vs).type == t) (map (v: v.type) vs))
"Elements in a list should have same type."
(head vs).type;
in
mkPrimitive (type.arrayOf elemType) vs // {
__toString = self:
"@${self.type} [${concatMapStringsSep "," toString self.value}]";
};
/* Returns the GVariant array from the given empty Nix list.
Type:
mkEmptyArray :: gvariant.type -> gvariant
Example:
# Creating an empty string array
lib.gvariant.mkEmptyArray (lib.gvariant.type.string)
*/
mkEmptyArray = elemType: mkPrimitive (type.arrayOf elemType) [ ] // {
__toString = self: "@${self.type} []";
};
/* Returns the GVariant variant from the given Nix value. Variants are containers
of different GVariant type.
Type:
mkVariant :: Any -> gvariant
Example:
lib.gvariant.mkArray [
(lib.gvariant.mkVariant "a string")
(lib.gvariant.mkVariant (lib.gvariant.mkInt32 1))
]
*/
mkVariant = elem:
let gvarElem = mkValue elem;
in mkPrimitive type.variant gvarElem // {
__toString = self: "<${toString self.value}>";
};
/* Returns the GVariant dictionary entry from the given key and value.
Type:
mkDictionaryEntry :: String -> Any -> gvariant
Example:
# A dictionary describing an Epiphanys search provider
[
(lib.gvariant.mkDictionaryEntry "url" (lib.gvariant.mkVariant "https://duckduckgo.com/?q=%s&t=epiphany"))
(lib.gvariant.mkDictionaryEntry "bang" (lib.gvariant.mkVariant "!d"))
(lib.gvariant.mkDictionaryEntry "name" (lib.gvariant.mkVariant "DuckDuckGo"))
]
*/
mkDictionaryEntry =
# The key of the entry
name:
# The value of the entry
value:
let
name' = mkValue name;
value' = mkValue value;
dictionaryType = type.dictionaryEntryOf name'.type value'.type;
in
mkPrimitive dictionaryType { inherit name value; } // {
__toString = self: "@${self.type} {${name'},${value'}}";
};
/* Returns the GVariant maybe from the given element type.
Type:
mkMaybe :: gvariant.type -> Any -> gvariant
*/
mkMaybe = elemType: elem:
mkPrimitive (type.maybeOf elemType) elem // {
__toString = self:
if self.value == null then
"@${self.type} nothing"
else
"just ${toString self.value}";
};
/* Returns the GVariant nothing from the given element type.
Type:
mkNothing :: gvariant.type -> gvariant
*/
mkNothing = elemType: mkMaybe elemType null;
/* Returns the GVariant just from the given Nix value.
Type:
mkJust :: Any -> gvariant
*/
mkJust = elem: let gvarElem = mkValue elem; in mkMaybe gvarElem.type gvarElem;
/* Returns the GVariant tuple from the given Nix list.
Type:
mkTuple :: [Any] -> gvariant
*/
mkTuple = elems:
let
gvarElems = map mkValue elems;
tupleType = type.tupleOf (map (e: e.type) gvarElems);
in
mkPrimitive tupleType gvarElems // {
__toString = self:
"@${self.type} (${concatMapStringsSep "," toString self.value})";
};
/* Returns the GVariant boolean from the given Nix bool value.
Type:
mkBoolean :: Bool -> gvariant
*/
mkBoolean = v:
mkPrimitive type.boolean v // {
__toString = self: if self.value then "true" else "false";
};
/* Returns the GVariant string from the given Nix string value.
Type:
mkString :: String -> gvariant
*/
mkString = v:
let sanitize = s: replaceStrings [ "\n" ] [ "\\n" ] (escape [ "'" "\\" ] s);
in mkPrimitive type.string v // {
__toString = self: "'${sanitize self.value}'";
};
/* Returns the GVariant object path from the given Nix string value.
Type:
mkObjectpath :: String -> gvariant
*/
mkObjectpath = v:
mkPrimitive type.string v // {
__toString = self: "objectpath '${escape [ "'" ] self.value}'";
};
/* Returns the GVariant uchar from the given Nix int value.
Type:
mkUchar :: Int -> gvariant
*/
mkUchar = mkPrimitive type.uchar;
/* Returns the GVariant int16 from the given Nix int value.
Type:
mkInt16 :: Int -> gvariant
*/
mkInt16 = mkPrimitive type.int16;
/* Returns the GVariant uint16 from the given Nix int value.
Type:
mkUint16 :: Int -> gvariant
*/
mkUint16 = mkPrimitive type.uint16;
/* Returns the GVariant int32 from the given Nix int value.
Type:
mkInt32 :: Int -> gvariant
*/
mkInt32 = v:
mkPrimitive type.int32 v // {
__toString = self: toString self.value;
};
/* Returns the GVariant uint32 from the given Nix int value.
Type:
mkUint32 :: Int -> gvariant
*/
mkUint32 = mkPrimitive type.uint32;
/* Returns the GVariant int64 from the given Nix int value.
Type:
mkInt64 :: Int -> gvariant
*/
mkInt64 = mkPrimitive type.int64;
/* Returns the GVariant uint64 from the given Nix int value.
Type:
mkUint64 :: Int -> gvariant
*/
mkUint64 = mkPrimitive type.uint64;
/* Returns the GVariant double from the given Nix float value.
Type:
mkDouble :: Float -> gvariant
*/
mkDouble = v:
mkPrimitive type.double v // {
__toString = self: toString self.value;
};
}

View File

@ -0,0 +1,93 @@
{ config, lib, ... }:
let inherit (lib) concatStringsSep mapAttrsToList mkMerge mkOption types gvariant;
in {
options.examples = mkOption { type = types.attrsOf gvariant; };
config = {
examples = with gvariant;
mkMerge [
{ bool = true; }
{ bool = true; }
{ float = 3.14; }
{ int32 = mkInt32 (- 42); }
{ int32 = mkInt32 (- 42); }
{ uint32 = mkUint32 42; }
{ uint32 = mkUint32 42; }
{ int16 = mkInt16 (-42); }
{ int16 = mkInt16 (-42); }
{ uint16 = mkUint16 42; }
{ uint16 = mkUint16 42; }
{ int64 = mkInt64 (-42); }
{ int64 = mkInt64 (-42); }
{ uint64 = mkUint64 42; }
{ uint64 = mkUint64 42; }
{ array1 = [ "one" ]; }
{ array1 = mkArray [ "two" ]; }
{ array2 = mkArray [ (mkInt32 1) ]; }
{ array2 = mkArray [ (nkUint32 2) ]; }
{ emptyArray1 = [ ]; }
{ emptyArray2 = mkEmptyArray type.uint32; }
{ string = "foo"; }
{ string = "foo"; }
{
escapedString = ''
'\
'';
}
{ tuple = mkTuple [ (mkInt32 1) [ "foo" ] ]; }
{ maybe1 = mkNothing type.string; }
{ maybe2 = mkJust (mkUint32 4); }
{ variant1 = mkVariant "foo"; }
{ variant2 = mkVariant 42; }
{ dictionaryEntry = mkDictionaryEntry (mkInt32 1) [ "foo" ]; }
];
assertions = [
{
assertion = (
let
mkLine = n: v: "${n} = ${toString (gvariant.mkValue v)}";
result = concatStringsSep "\n" (mapAttrsToList mkLine config.examples);
in
result + "\n"
) == ''
array1 = @as ['one','two']
array2 = @au [1,2]
bool = true
dictionaryEntry = @{ias} {1,@as ['foo']}
emptyArray1 = @as []
emptyArray2 = @au []
escapedString = '\'\\\n'
float = 3.140000
int = -42
int16 = @n -42
int64 = @x -42
maybe1 = @ms nothing
maybe2 = just @u 4
string = 'foo'
tuple = @(ias) (1,@as ['foo'])
uint16 = @q 42
uint32 = @u 42
uint64 = @t 42
variant1 = @v <'foo'>
variant2 = @v <42>
'';
}
];
};
}

View File

@ -1,55 +1,217 @@
{ 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
'';
# 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
'';
mkAllLocks = settings: lib.flatten (
lib.mapAttrsToList (k: v: lib.mapAttrsToList (k': _: "/${k}/${k'}") v) settings);
# 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))
(pkgs.writeTextDir "locks/nixos-generated-dconf-locks" (lib.concatStringsSep "\n"
(if val.lockAll then mkAllLocks val.settings else val.locks)
))
] ++ (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:
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";
};
}
'';
};
locks = lib.mkOption {
type = with lib.types; listOf str;
default = [ ];
description = lib.mdDoc ''
A list of dconf keys to be lockdown. This doesn't take effect if `lockAll`
is set.
'';
example = literalExpression ''
[ "/org/gnome/desktop/background/picture-uri" ]
'';
};
lockAll = lib.mkOption {
type = lib.types.bool;
default = false;
description = lib.mdDoc "Lockdown all dconf keys in `settings`.";
};
};
};
in
{
###### interface
options = {
programs.dconf = {
enable = mkEnableOption (lib.mdDoc "dconf");
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;
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.";
};
packages = mkOption {
type = types.listOf types.package;
default = [];
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 = {
programs.dconf = {
enable = lib.mkEnableOption (lib.mdDoc "dconf");
profiles = lib.mkOption {
type = with lib.types; attrsOf (oneOf [
path
package
dconfProfile
]);
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 {
type = lib.types.listOf lib.types.package;
default = [ ];
description = lib.mdDoc "A list of packages which provide dconf profiles and databases in {file}`/etc/dconf`.";
};
};
};
###### implementation
config = lib.mkIf (cfg.profiles != { } || cfg.enable) {
programs.dconf.packages = lib.mapAttrsToList mkDconfProfile cfg.profiles;
config = mkIf (cfg.profiles != {} || cfg.enable) {
environment.etc.dconf = mkIf (cfg.profiles != {} || cfg.packages != []) {
source = cfgDir;
environment.etc.dconf = lib.mkIf (cfg.packages != [ ]) {
source = pkgs.symlinkJoin {
name = "dconf-system-config";
paths = map (x: "${x}/etc/dconf") cfg.packages;
nativeBuildInputs = [ (lib.getBin pkgs.dconf) ];
postBuild = ''
if test -d $out/db; then
dconf update $out/db
fi
'';
};
};
services.dbus.packages = [ pkgs.dconf ];
@ -59,8 +221,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 = lib.mkIf cfg.enable {
# Needed for unwrapped applications
GIO_EXTRA_MODULES = [ "${pkgs.dconf.lib}/lib/gio/modules" ];
};
};
}

View File

@ -231,40 +231,14 @@ 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 = lib.optionals (!cfg.gdm.autoSuspend) [{
settings."org/gnome/settings-daemon/plugins/power" = {
sleep-inactive-ac-type = "nothing";
sleep-inactive-battery-type = "nothing";
sleep-inactive-ac-timeout = lib.gvariant.mkInt32 0;
sleep-inactive-battery-timeout = lib.gvariant.mkInt32 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
'';
};
}] ++ [ "${gdm}/share/gdm/greeter-dconf-defaults" ];
# Use AutomaticLogin if delay is zero, because it's immediate.
# Otherwise with TimedLogin with zero seconds the prompt is still

View File

@ -210,6 +210,7 @@ in {
custom-ca = handleTest ./custom-ca.nix {};
croc = handleTest ./croc.nix {};
darling = handleTest ./darling.nix {};
dconf = handleTest ./dconf.nix {};
deepin = handleTest ./deepin.nix {};
deluge = handleTest ./deluge.nix {};
dendrite = handleTest ./matrix/dendrite.nix {};

34
nixos/tests/dconf.nix Normal file
View File

@ -0,0 +1,34 @@
import ./make-test-python.nix
({ lib, ... }:
{
name = "dconf";
meta.maintainers = with lib.maintainers; [
linsui
];
nodes.machine = { config, pkgs, lib, ... }: {
users.extraUsers.alice = { isNormalUser = true; };
programs.dconf = with lib.gvariant; {
enable = true;
profiles.user.databases = [
{
settings = {
"test/not/locked" = mkInt32 1;
"test/is/locked" = "locked";
};
locks = [
"/test/is/locked"
];
}
];
};
};
testScript = ''
machine.succeed("test $(dconf read -d /test/not/locked) == 1")
machine.succeed("test $(dconf read -d /test/is/locked) == \"'locked'\"")
machine.fail("sudo -u alice dbus-run-session -- dconf write /test/is/locked \"@s 'unlocked'\"")
machine.succeed("sudo -u alice dbus-run-session -- dconf write /test/not/locked \"@i 2\"")
'';
})

View File

@ -1,4 +1,5 @@
{ lib, stdenv
{ lib
, stdenv
, fetchurl
, fetchpatch
, substituteAll
@ -8,7 +9,6 @@
, pkg-config
, glib
, itstool
, libxml2
, xorg
, accountsservice
, libX11
@ -24,12 +24,12 @@
, audit
, gobject-introspection
, plymouth
, librsvg
, coreutils
, xorgserver
, xwayland
, dbus
, nixos-icons
, runCommand
}:
let
@ -41,21 +41,21 @@ let
in
stdenv.mkDerivation rec {
stdenv.mkDerivation (finalAttrs: {
pname = "gdm";
version = "44.1";
outputs = [ "out" "dev" ];
src = fetchurl {
url = "mirror://gnome/sources/gdm/${lib.versions.major version}/${pname}-${version}.tar.xz";
url = "mirror://gnome/sources/gdm/${lib.versions.major finalAttrs.version}/${finalAttrs.pname}-${finalAttrs.version}.tar.xz";
sha256 = "aCZrOr59KPxGnQBnqsnF2rsMp5UswffCOKBJUfPcWw0=";
};
mesonFlags = [
"-Dgdm-xsession=true"
# TODO: Setup a default-path? https://gitlab.gnome.org/GNOME/gdm/-/blob/6fc40ac6aa37c8ad87c32f0b1a5d813d34bf7770/meson_options.txt#L6
"-Dinitial-vt=${passthru.initialVT}"
"-Dinitial-vt=${finalAttrs.passthru.initialVT}"
"-Dudev-dir=${placeholder "out"}/lib/udev/rules.d"
"-Dsystemdsystemunitdir=${placeholder "out"}/lib/systemd/system"
"-Dsystemduserunitdir=${placeholder "out"}/lib/systemd/user"
@ -131,21 +131,21 @@ stdenv.mkDerivation rec {
'';
preInstall = ''
install -D ${override} ${DESTDIR}/$out/share/glib-2.0/schemas/org.gnome.login-screen.gschema.override
install -D ${override} $DESTDIR/$out/share/glib-2.0/schemas/org.gnome.login-screen.gschema.override
'';
postInstall = ''
# Move stuff from DESTDIR to proper location.
# We use rsync to merge the directories.
rsync --archive "${DESTDIR}/etc" "$out"
rm --recursive "${DESTDIR}/etc"
rsync --archive "$DESTDIR/etc" "$out"
rm --recursive "$DESTDIR/etc"
for o in $(getAllOutputNames); do
if [[ "$o" = "debug" ]]; then continue; fi
rsync --archive "${DESTDIR}/''${!o}" "$(dirname "''${!o}")"
rm --recursive "${DESTDIR}/''${!o}"
rsync --archive "$DESTDIR/''${!o}" "$(dirname "''${!o}")"
rm --recursive "$DESTDIR/''${!o}"
done
# Ensure the DESTDIR is removed.
rmdir "${DESTDIR}/nix/store" "${DESTDIR}/nix" "${DESTDIR}"
rmdir "$DESTDIR/nix/store" "$DESTDIR/nix" "$DESTDIR"
# We are setting DESTDIR so the post-install script does not compile the schemas.
glib-compile-schemas "$out/share/glib-2.0/schemas"
@ -170,6 +170,18 @@ stdenv.mkDerivation rec {
# Used in GDM NixOS module
# Don't remove.
initialVT = "7";
dconfDb = "${finalAttrs.finalPackage}/share/gdm/greeter-dconf-defaults";
dconfProfile = "user-db:user\nfile-db:${finalAttrs.passthru.dconfDb}";
tests = {
profile = runCommand "gdm-profile-test" { } ''
if test "${finalAttrs.passthru.dconfProfile}" != "$(cat ${finalAttrs.finalPackage}/share/dconf/profile/gdm)"; then
echo "GDM dconf profile changed, please update gdm.nix"
exit 1
fi
touch $out
'';
};
};
meta = with lib; {
@ -179,4 +191,4 @@ stdenv.mkDerivation rec {
maintainers = teams.gnome.members;
platforms = platforms.linux;
};
}
})