lib.mkMerge
Определение, описание и использование
lib.mkMerge — это функция, которая позволяет объединять несколько наборов атрибутов в один, решая конфликты через приоритеты.
Это особенно полезно в модульной конфигурации NixOS, когда вы хотите разделить конфигурацию на части и объединить их без ручного разрешения конфликтов.
Когда вы объединяете наборы атрибутов в Nix, могут возникать конфликты (одинаковые ключи с разными значениями). lib.mkMerge решает эти конфликты, используя систему приоритетов: каждый элемент получает приоритет (по умолчанию 0), и выигрывает значение с наивысшим приоритетом.
Примеры
Основной
Исходный код
{ lib, ... }:
let
# Три конфигурации, которые мы хотим объединить
config1 = { boot.loader.grub.enable = true; };
config2 = { boot.loader.systemd-boot.enable = true; };
config3 = { networking.hostName = "myhost"; };
in
lib.mkMerge [
config1
config2 # Конфликт с config1 по boot.loader.*
config3
]
Описание
В этом примере config1 и config2 конфликтуют (оба определяют загрузчик). По умолчанию lib.mkMerge просто выберет последнее значение, но с приоритетами можно контролировать результат.
Расстановка приоритетов
{ lib, ... }:
lib.mkMerge [
# Приоритет 1000 (высокий)
(lib.mkIf false {
services.nginx.enable = true;
})
# Приоритет 100 (средний)
{
services.nginx.enable = lib.mkDefault false;
services.nginx.virtualHosts."example.com".root = "/var/www";
}
# Приоритет 150 (выше среднего)
(lib.mkForce {
services.nginx.enable = true; # Переопределит mkDefault
})
]
Описание:
-
mkIfимеет приоритет 1000 -
mkDefaultимеет приоритет 100 -
mkForceимеет приоритет 150 -
Результат:
services.nginx.enable = true(побеждаетmkForce)
Настройка сети
{ lib, config, ... }:
let
commonNetwork = {
networking.networkmanager.enable = true;
networking.firewall.enable = true;
};
homeConfig = {
networking.hostName = "home-pc";
networking.firewall.allowedTCPPorts = [ 80 443 ];
};
workConfig = {
networking.hostName = "work-laptop";
networking.firewall.allowedTCPPorts = [ 22 3389 ];
networking.proxy.default = "http://proxy.company.com:8080";
};
# Динамически выбираем конфигурацию
environmentConfig = if config.isWorkEnvironment then workConfig else homeConfig;
in
{
imports = [ ./hardware-configuration.nix ];
options.isWorkEnvironment = lib.mkOption {
type = lib.types.bool;
default = false;
};
config = lib.mkMerge [
commonNetwork
environmentConfig
{
# Этот блок имеет самый высокий приоритет
networking.nameservers = lib.mkForce [ "1.1.1.1" "8.8.8.8" ];
}
];
}
Модуль для разных аппаратных конфигураций
# hardware/base.nix
{ lib, ... }:
{
options.hardware = {
profile = lib.mkOption {
type = lib.types.enum [ "desktop" "laptop" "server" ];
default = "desktop";
};
hasBluetooth = lib.mkOption {
type = lib.types.bool;
default = false;
};
};
config = lib.mkMerge [
# Базовая конфигурация для всех систем
{
hardware.enableRedistributableFirmware = true;
powerManagement.enable = true;
}
# Конфигурация для ноутбуков
(lib.mkIf (config.hardware.profile == "laptop") {
services.tlp.enable = true;
services.auto-cpufreq.enable = true;
hardware.hasBluetooth = lib.mkDefault true;
})
# Конфигурация для серверов
(lib.mkIf (config.hardware.profile == "server") {
powerManagement.enable = lib.mkForce false; # Отключаем на серверах
services.openssh.enable = true;
})
# Конфигурация Bluetooth
(lib.mkIf config.hardware.hasBluetooth {
hardware.bluetooth.enable = true;
services.blueman.enable = true;
})
];
}
Вложенные объединения
{ lib, ... }:
lib.mkMerge [
{
services.postgresql = lib.mkMerge [
{
enable = true;
package = pkgs.postgresql_15;
}
(lib.mkIf config.services.grafana.enable {
authentication = ''
host grafana all ::1/128 md5
'';
})
];
}
{
environment.systemPackages = with pkgs; [
vim
htop
];
}
]
Работа приоритетов
Приоритеты по умолчанию (от высокого к низкому):
-
lib.mkIf(1000) — условное включение -
lib.mkOverride— явное указание приоритета -
lib.mkForce(50) — принудительное значение -
lib.mkDefault(1000 для false, 100 для true) — значения по умолчанию -
Обычные значения (0) — стандартный приоритет
Важные моменты
-
Порядок важен только при равных приоритетах — при одинаковых приоритетах побеждает последнее значение
-
Глубокое слияние —
mkMergeрекурсивно объединяет вложенные атрибуты -
Не путать с
mkOverride—mkMergeобъединяет списки наборов, аmkOverrideизменяет приоритет конкретного атрибута
Краткая шпаргалка
# Объединение конфигураций
config = lib.mkMerge [
commonConfig
(lib.mkIf condition conditionalConfig)
(lib.mkForce forcedConfig)
userConfig
];
# Эквивалентно (но более читаемо и модульно):
# {
# commonConfig
# conditionalConfig (если condition == true)
# forcedConfig (с приоритетом)
# userConfig
# }
No comments to display
No comments to display