refactor: modules/users/s6-rc.nix

This commit is contained in:
Colin 2024-03-21 13:10:42 +00:00
parent d4f217a4f5
commit 218072b2fe

View File

@ -1,73 +1,85 @@
{ lib, pkgs, ... }:
let
logBase = "$HOME/.local/state/s6/logs";
maybe = cond: value: if cond then value else null;
normalizeName = name: lib.removeSuffix ".service" (lib.removeSuffix ".target" name);
# infers the service type from the arguments and dispatches appropriately
genService = { name, run, finish, depends }: let
# create a derivation whose output is the on-disk representation of some attrset.
# @path: /foo/bar/...
# @obj: one of:
# - { file = { text = "foo bar"; executable = true|false; } }
# - { dir = <obj>; }
fsItemToDerivation = path: obj: if obj ? dir then
pkgs.symlinkJoin {
name = "s6-${path}";
paths = lib.mapAttrsToList
(n: v: fsItemToDerivation "${path}/${n}" v)
obj.dir
;
}
else if obj ? text then
if obj.text != null then
pkgs.writeTextFile {
name = "s6-${path}";
destination = path;
text = obj.text;
}
else
pkgs.emptyDirectory
else if obj ? executable then
if obj.executable != null then
pkgs.writeTextFile {
name = "s6-${path}";
destination = path;
executable = true;
text = obj.executable;
}
else
pkgs.emptyDirectory
else throw "don't know how to convert fs item at path ${path} to derivation: ${obj}";
# call with an AttrSet of fs items:
# example:
# ```
# fsToDerivation {
# usr.dir = {
# normal.text = "i'm /usr/normal";
# exec.executable = ''
# #!/bin/sh
# echo "i'm executable"
# '';
# lib.dir = { ... };
# };
# bin.dir = { ... };
# }
fsToDerivation = fs: fsItemToDerivation "/" { dir = fs; };
# infers the service type from the arguments and creates an attrset usable by `fsToDerivation`.
serviceToFs = { name, run, finish, depends }: let
name' = normalizeName name;
type = if run != null then "longrun" else "bundle";
in genService' {
name = name';
inherit type;
# TODO: a bundle can totally have dependencies. i can't just map them *all* to contents.
depends = lib.optionals (type == "longrun") depends;
finish = finish;
longrun-run = run;
bundle-contents = lib.optionals (type == "bundle") depends;
};
genService' = {
name,
type,
depends,
finish,
longrun-run,
bundle-contents,
}: pkgs.symlinkJoin {
name = "s6-${name}";
paths = [
(pkgs.writeTextFile {
name = "s6-${name}-type";
destination = "/${name}/type";
text = type;
})
] ++ lib.optionals (finish != null) [
(pkgs.writeTextFile {
# TODO: use 'writeShellScript'?
name = "s6-${name}-finish";
destination = "/${name}/finish";
executable = true;
text = ''
#!/bin/sh
${finish}
'';
})
] ++ lib.optionals (longrun-run != null) [
(pkgs.writeTextFile {
name = "s6-${name}-run";
destination = "/${name}/run";
executable = true;
# TODO: consider using `makeWrapper`/`makeBinaryWrapper`?
text = ''
#!/bin/sh
echo "starting: s6-${name}"
${longrun-run} 2>&1
'';
})
] ++ lib.optionals (bundle-contents != null) [
(pkgs.writeTextFile {
name = "s6-${name}-contents";
destination = "/${name}/contents";
text = lib.concatStringsSep "\n" (builtins.map normalizeName bundle-contents);
})
] ++ builtins.map
(d: pkgs.writeTextFile {
name = "s6-${name}-depends-${d}";
destination = "/${name}/dependencies.d/${normalizeName d}";
text = "";
})
depends;
in {
"${name}".dir = {
"type".text = type;
"finish".executable = maybe (finish != null) ''
#!/bin/sh
${finish}
'';
"run".executable = maybe (run != null) ''
#!/bin/sh
echo "starting: s6-${name}"
${run} 2>&1
'';
"contents".text = maybe (type == "bundle") (
lib.concatStringsSep "\n" (builtins.map normalizeName depends)
);
# TODO: a bundle can also have dependencies
"dependencies.d".dir = lib.optionalAttrs (type == "longrun") (
lib.genAttrs
(builtins.map normalizeName depends)
(dep: { text = ""; })
);
};
};
# create a directory, containing N subdirectories:
@ -78,10 +90,11 @@ let
# - type
# - run
# - ...
genServices = svcs: pkgs.symlinkJoin {
name = "s6-user-services";
paths = builtins.map genService svcs;
};
genServices = svcs: fsToDerivation (lib.foldl'
(acc: srv: acc // (serviceToFs srv))
{}
svcs
);
# output is a directory containing:
# - db