From 218072b2fe5c3e7ca1f6e2e39acea30666c4b86a Mon Sep 17 00:00:00 2001 From: Colin Date: Thu, 21 Mar 2024 13:10:42 +0000 Subject: [PATCH] refactor: modules/users/s6-rc.nix --- modules/users/s6-rc.nix | 147 ++++++++++++++++++++++------------------ 1 file changed, 80 insertions(+), 67 deletions(-) diff --git a/modules/users/s6-rc.nix b/modules/users/s6-rc.nix index 6edb5f20..d7e85a8b 100644 --- a/modules/users/s6-rc.nix +++ b/modules/users/s6-rc.nix @@ -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 = ; } + 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