PoC part 2

This commit is contained in:
Qyriad 2026-02-03 22:31:32 +01:00
parent 0580ad02bd
commit 15ed56d8ad
3 changed files with 117 additions and 40 deletions

View file

@ -30,15 +30,22 @@
dynamicBefore = nixosBefore.config.dynamicism.finalSettings;
nixosAfter = evalNixos {
configuration = lib.mkMerge [
nixosBefore
configuration = { ... }: {
imports = [
./configuration.nix
(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 {
inherit nixosBefore nixosAfter withActivationScripts;
};
in dynix.overrideAttrs (final: prev: let
@ -56,5 +63,9 @@ in lib.recursiveUpdate prev {
c = self.nixos;
nixos-vm = self.nixos.config.system.build.vm;
doChange = builtins.seq self.nixos.config.dynamicism doChange;
withVox = self.doChange {
option = lib.splitString "." "services.gotosocial.settings.application-name";
value = "Vox is an asshole";
};
};
})

View file

@ -13,19 +13,43 @@ let
cfg = config.dynamicism;
opts = options.dynamicism;
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
next =
assert lib.isList optPath;
lib.setAttrByPath optPath (lib.getAttrFromPath optPath config);
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
{
#
# Interface.
#
options.dynamicism = {
for = mkOption {
type = t.attrsOf (t.submoduleWith {
modules = [ ./submodule.nix ];
shorthandOnlyDefinesConfig = false;
specialArgs = { inherit pkgs; };
});
default = { };
};
@ -40,39 +64,30 @@ in
};
};
config.assertions = let
unitsExist = lib.foldl' (acc: 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 acc ++ lib.optionals submod.enable.value next) [ ] (lib.attrValues subOpts);
# Assertions.
config.assertions = ourAssertions;
optsExist = lib.foldl' (acc: submod: let
next = lib.map (optPath: {
assertion = lib.hasAttrByPath optPath options;
message = "'${showOption submod.source-options.loc}' specified non-existent option '${showOption optPath}'";
}) submod.source-options.value;
in acc ++ lib.optionals submod.enable.value next) [ ] (lib.attrValues subOpts);
in lib.concatLists [
unitsExist
optsExist
];
#
# Generic implementation.
#
config.dynamicism.finalSettings = lib.asserts.checkAssertWarn ourAssertions [ ] (
recUpdateFoldlAttrs (name: { ... }@submod: finalSettingsFor submod) config.dynamicism.for
);
config.dynamicism.for = {
gotosocial = {
# Implementations.
config.dynamicism.for.gotosocial = let
cfg = config.dynamicism.for.gotosocial;
in {
source-options = [
"services.gotosocial.setting"
"services.gotosocial.settings"
];
systemd-services-updated = [
"gotosocial.service"
];
};
};
config.dynamicism.finalSettings = lib.foldlAttrs (acc: name: { ... }@submod: let
next = finalSettingsFor submod;
in lib.recursiveUpdate acc next) { } config.dynamicism.for;
configFile = settingsFormat.generate "gotosocial-override.yml" config.services.gotosocial.settings;
unitDropins."gotosocial.service" = pkgs.writeText "gotosocial-override.conf" ''
[Service]
ExecStart=
ExecStart=${lib.getExe' pkgs.gotosocial "gotosocial"} --config-path ${cfg.configFile} start
'';
};
}

View file

@ -1,5 +1,6 @@
{
name,
pkgs,
lib,
config,
...
@ -13,10 +14,26 @@ let
mkEnableOption
literalExpression
;
inherit (lib.types)
mkOptionType
;
t = lib.types;
/** Either a list of strings, or a dotted string that will be split. */
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
{
options = {
@ -36,12 +53,46 @@ in
A list of systemd unit names (including the suffix, e.g. `.service`) that need to be updated.
'';
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 {
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";
};
};
}