PoC part 2
This commit is contained in:
parent
0580ad02bd
commit
15ed56d8ad
3 changed files with 117 additions and 40 deletions
19
default.nix
19
default.nix
|
|
@ -30,15 +30,22 @@
|
||||||
dynamicBefore = nixosBefore.config.dynamicism.finalSettings;
|
dynamicBefore = nixosBefore.config.dynamicism.finalSettings;
|
||||||
|
|
||||||
nixosAfter = evalNixos {
|
nixosAfter = evalNixos {
|
||||||
configuration = lib.mkMerge [
|
configuration = { ... }: {
|
||||||
nixosBefore
|
imports = [
|
||||||
|
./configuration.nix
|
||||||
(lib.setAttrByPath option (lib.mkOverride (-999) value))
|
(lib.setAttrByPath option (lib.mkOverride (-999) value))
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
};
|
||||||
|
|
||||||
dynamicAfter = nixosAfter.config.dynamicism.finalSettings;
|
withActivationScripts = evalNixos {
|
||||||
|
configuration = ({ ... }: {
|
||||||
|
imports = [ ./configuration.nix ];
|
||||||
|
config.environment.systemPackages = [ nixosAfter.config.dynamicism.for.gotosocial.activate ];
|
||||||
|
});
|
||||||
|
};
|
||||||
in {
|
in {
|
||||||
|
inherit nixosBefore nixosAfter withActivationScripts;
|
||||||
};
|
};
|
||||||
|
|
||||||
in dynix.overrideAttrs (final: prev: let
|
in dynix.overrideAttrs (final: prev: let
|
||||||
|
|
@ -56,5 +63,9 @@ in lib.recursiveUpdate prev {
|
||||||
c = self.nixos;
|
c = self.nixos;
|
||||||
nixos-vm = self.nixos.config.system.build.vm;
|
nixos-vm = self.nixos.config.system.build.vm;
|
||||||
doChange = builtins.seq self.nixos.config.dynamicism doChange;
|
doChange = builtins.seq self.nixos.config.dynamicism doChange;
|
||||||
|
withVox = self.doChange {
|
||||||
|
option = lib.splitString "." "services.gotosocial.settings.application-name";
|
||||||
|
value = "Vox is an asshole";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -13,19 +13,43 @@ let
|
||||||
cfg = config.dynamicism;
|
cfg = config.dynamicism;
|
||||||
opts = options.dynamicism;
|
opts = options.dynamicism;
|
||||||
subOpts = lib.mapAttrs (_: metaAttr: metaAttr.configuration.options) options.dynamicism.for.valueMeta.attrs;
|
subOpts = lib.mapAttrs (_: metaAttr: metaAttr.configuration.options) options.dynamicism.for.valueMeta.attrs;
|
||||||
|
settingsFormat = pkgs.formats.yaml { };
|
||||||
|
|
||||||
|
concatFoldl = f: list: lib.foldl' (acc: value: acc ++ (f value)) [ ] list;
|
||||||
|
recUpdateFoldlAttrs = f: attrs: lib.foldlAttrs (acc: name: value: lib.recursiveUpdate acc (f name value)) { } attrs;
|
||||||
|
|
||||||
finalSettingsFor = { ... }@submod: lib.foldl (acc: optPath: let
|
finalSettingsFor = { ... }@submod: lib.foldl (acc: optPath: let
|
||||||
next =
|
next =
|
||||||
assert lib.isList optPath;
|
assert lib.isList optPath;
|
||||||
lib.setAttrByPath optPath (lib.getAttrFromPath optPath config);
|
lib.setAttrByPath optPath (lib.getAttrFromPath optPath config);
|
||||||
in lib.recursiveUpdate acc next) { } submod.source-options;
|
in lib.recursiveUpdate acc next) { } submod.source-options;
|
||||||
|
|
||||||
|
ourAssertions = lib.concatAttrValues {
|
||||||
|
unitsExist = concatFoldl (submod: let
|
||||||
|
next = lib.map (unit: assert lib.isString unit; {
|
||||||
|
assertion = config.systemd.units.${unit}.enable or false;
|
||||||
|
message = ''
|
||||||
|
'${showOption submod.systemd-services-updated.loc}' specified non-existent unit '${unit}'
|
||||||
|
'';
|
||||||
|
}) submod.systemd-services-updated.value;
|
||||||
|
in lib.optionals submod.enable.value next) (lib.attrValues subOpts);
|
||||||
|
|
||||||
|
optsExist = concatFoldl (submod: lib.optionals submod.enable.value (lib.map (optPath: {
|
||||||
|
assertion = lib.hasAttrByPath optPath options;
|
||||||
|
message = "'${showOption submod.source-options.loc}' specified non-existent option '${showOption optPath}'";
|
||||||
|
}) submod.source-options.value)) (lib.attrValues subOpts);
|
||||||
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
|
#
|
||||||
|
# Interface.
|
||||||
|
#
|
||||||
options.dynamicism = {
|
options.dynamicism = {
|
||||||
for = mkOption {
|
for = mkOption {
|
||||||
type = t.attrsOf (t.submoduleWith {
|
type = t.attrsOf (t.submoduleWith {
|
||||||
modules = [ ./submodule.nix ];
|
modules = [ ./submodule.nix ];
|
||||||
shorthandOnlyDefinesConfig = false;
|
shorthandOnlyDefinesConfig = false;
|
||||||
|
specialArgs = { inherit pkgs; };
|
||||||
});
|
});
|
||||||
default = { };
|
default = { };
|
||||||
};
|
};
|
||||||
|
|
@ -40,39 +64,30 @@ in
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
config.assertions = let
|
# Assertions.
|
||||||
unitsExist = lib.foldl' (acc: submod: let
|
config.assertions = ourAssertions;
|
||||||
next = lib.map (unit: assert lib.isString unit; {
|
|
||||||
assertion = config.systemd.units.${unit}.enable or false;
|
|
||||||
message = ''
|
|
||||||
'${showOption submod.systemd-services-updated.loc}' specified non-existent unit '${unit}'
|
|
||||||
'';
|
|
||||||
}) submod.systemd-services-updated.value;
|
|
||||||
in acc ++ lib.optionals submod.enable.value next) [ ] (lib.attrValues subOpts);
|
|
||||||
|
|
||||||
optsExist = lib.foldl' (acc: submod: let
|
#
|
||||||
next = lib.map (optPath: {
|
# Generic implementation.
|
||||||
assertion = lib.hasAttrByPath optPath options;
|
#
|
||||||
message = "'${showOption submod.source-options.loc}' specified non-existent option '${showOption optPath}'";
|
config.dynamicism.finalSettings = lib.asserts.checkAssertWarn ourAssertions [ ] (
|
||||||
}) submod.source-options.value;
|
recUpdateFoldlAttrs (name: { ... }@submod: finalSettingsFor submod) config.dynamicism.for
|
||||||
in acc ++ lib.optionals submod.enable.value next) [ ] (lib.attrValues subOpts);
|
);
|
||||||
in lib.concatLists [
|
|
||||||
unitsExist
|
|
||||||
optsExist
|
|
||||||
];
|
|
||||||
|
|
||||||
config.dynamicism.for = {
|
# Implementations.
|
||||||
gotosocial = {
|
config.dynamicism.for.gotosocial = let
|
||||||
|
cfg = config.dynamicism.for.gotosocial;
|
||||||
|
in {
|
||||||
source-options = [
|
source-options = [
|
||||||
"services.gotosocial.setting"
|
"services.gotosocial.settings"
|
||||||
];
|
];
|
||||||
systemd-services-updated = [
|
|
||||||
"gotosocial.service"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config.dynamicism.finalSettings = lib.foldlAttrs (acc: name: { ... }@submod: let
|
configFile = settingsFormat.generate "gotosocial-override.yml" config.services.gotosocial.settings;
|
||||||
next = finalSettingsFor submod;
|
|
||||||
in lib.recursiveUpdate acc next) { } config.dynamicism.for;
|
unitDropins."gotosocial.service" = pkgs.writeText "gotosocial-override.conf" ''
|
||||||
|
[Service]
|
||||||
|
ExecStart=
|
||||||
|
ExecStart=${lib.getExe' pkgs.gotosocial "gotosocial"} --config-path ${cfg.configFile} start
|
||||||
|
'';
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
{
|
{
|
||||||
name,
|
name,
|
||||||
|
pkgs,
|
||||||
lib,
|
lib,
|
||||||
config,
|
config,
|
||||||
...
|
...
|
||||||
|
|
@ -13,10 +14,26 @@ let
|
||||||
mkEnableOption
|
mkEnableOption
|
||||||
literalExpression
|
literalExpression
|
||||||
;
|
;
|
||||||
|
inherit (lib.types)
|
||||||
|
mkOptionType
|
||||||
|
;
|
||||||
t = lib.types;
|
t = lib.types;
|
||||||
|
|
||||||
/** Either a list of strings, or a dotted string that will be split. */
|
/** Either a list of strings, or a dotted string that will be split. */
|
||||||
convenientAttrPath = t.coercedTo t.str (lib.splitString ".") (t.listOf t.str);
|
convenientAttrPath = t.coercedTo t.str (lib.splitString ".") (t.listOf t.str);
|
||||||
|
|
||||||
|
executablePathInStore = mkOptionType {
|
||||||
|
name = "exepath";
|
||||||
|
description = "executable path in the Nix store";
|
||||||
|
descriptionClass = "noun";
|
||||||
|
merge = lib.mergeEqualOption;
|
||||||
|
functor = lib.defaultFunctor "exepath";
|
||||||
|
check = x: if lib.isDerivation x then (
|
||||||
|
x.meta.mainProgram or null != null
|
||||||
|
) else (
|
||||||
|
lib.pathInStore.check x
|
||||||
|
);
|
||||||
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options = {
|
options = {
|
||||||
|
|
@ -36,12 +53,46 @@ in
|
||||||
A list of systemd unit names (including the suffix, e.g. `.service`) that need to be updated.
|
A list of systemd unit names (including the suffix, e.g. `.service`) that need to be updated.
|
||||||
'';
|
'';
|
||||||
example = literalExpression ''
|
example = literalExpression ''
|
||||||
"gotosocial.service"
|
[ "gotosocial.service" ]
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
default = lib.attrNames config.unitDropins;
|
||||||
|
};
|
||||||
|
|
||||||
|
configFile = mkOption {
|
||||||
|
type = t.pathInStore;
|
||||||
|
internal = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
unitDropins = mkOption {
|
||||||
|
type = t.attrsOf t.pathInStore;
|
||||||
|
internal = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
activate = mkOption {
|
||||||
|
type = executablePathInStore;
|
||||||
|
internal = true;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
config = mkIf config.enable {
|
config = mkIf config.enable {
|
||||||
|
activate = pkgs.writeShellApplication {
|
||||||
|
name = "dynamicism-for-${name}-activate";
|
||||||
|
runtimeInputs = [ pkgs.systemd ];
|
||||||
|
text = let
|
||||||
|
doEdits = config.unitDropins
|
||||||
|
|> lib.mapAttrsToList (service: dropin: ''
|
||||||
|
cat "${dropin}" | systemctl edit "${service}" --runtime --stdin
|
||||||
|
'');
|
||||||
|
doReloads = config.unitDropins
|
||||||
|
|> lib.mapAttrsToList (service: _: ''
|
||||||
|
systemctl reload-or-restart "${service}"
|
||||||
|
'');
|
||||||
|
in [
|
||||||
|
doEdits
|
||||||
|
doReloads
|
||||||
|
] |> lib.concatLists
|
||||||
|
|> lib.concatStringsSep "\n";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue