diff --git a/pkgs/default.nix b/pkgs/default.nix index 4657283..bbaf690 100644 --- a/pkgs/default.nix +++ b/pkgs/default.nix @@ -122,6 +122,7 @@ in { uevent-watch = callPackage ./uevent-watch { }; usb-modeswitch = callPackage ./usb-modeswitch { }; watch-outputs = callPackage ./watch-outputs { }; + watch-ssh-keys = callPackage ./watch-ssh-keys { }; writeAshScript = callPackage ./write-ash-script { }; writeAshScriptBin = callPackage ./write-ash-script/bin.nix { }; writeFennel = callPackage ./write-fennel { }; diff --git a/pkgs/watch-ssh-keys/default.nix b/pkgs/watch-ssh-keys/default.nix new file mode 100644 index 0000000..25645a4 --- /dev/null +++ b/pkgs/watch-ssh-keys/default.nix @@ -0,0 +1,40 @@ +{ + fetchurl, + writeFennel, + fennel, + fennelrepl, + runCommand, + lua, + anoia, + linotify, + lualinux, + stdenv +}: +let name = "watch-ssh-keys"; +in stdenv.mkDerivation { + inherit name; + src = ./.; + + buildInputs = [lua]; + nativeBuildInputs = [fennelrepl]; + + buildPhase = '' + fennelrepl --test ./watch-ssh-keys.fnl + cp -p ${writeFennel name { + packages = [ + anoia + lualinux + linotify + fennel + ]; + macros = [ + anoia.dev + ]; + mainFunction = "run"; + } ./watch-ssh-keys.fnl } ${name} + ''; + + installPhase = '' + install -D ${name} $out/bin/${name} + ''; +} diff --git a/pkgs/watch-ssh-keys/watch-ssh-keys.fnl b/pkgs/watch-ssh-keys/watch-ssh-keys.fnl new file mode 100644 index 0000000..a38f525 --- /dev/null +++ b/pkgs/watch-ssh-keys/watch-ssh-keys.fnl @@ -0,0 +1,59 @@ +(local { : system : assoc : split : dup : table= : dig } (require :anoia)) +(local svc (require :anoia.svc)) +(import-macros { : define-tests : expect : expect= } :anoia.assert) + +(fn parse-args [args] + (match args + ["-d" path & rest] (assoc (parse-args rest) + :out-path path) + [watched-service path] { : watched-service + : path })) + +(fn write-changes [path old-tree new-tree] + (when (not (table= old-tree new-tree)) + (each [username pubkeys (pairs new-tree)] + (with-open [f (assert (io.open (.. path "/" username) :w))] + (each [_ k (ipairs pubkeys)] + (f:write k) + (f:write "\n"))))) + (each [k v (pairs old-tree)] + (when (not (. new-tree k)) + (os.remove (.. path "/" k)))) + new-tree) + +(define-tests + (local { : file-exists? } (require :anoia)) + (print "running tests") + (let [tree { + "dan" ["f1" "f2"] + "root" ["f1"] + } + out-dir (: (assert (io.popen "mktemp -d -p '' fennel-XXXXXXX" :r)) :read "l")] + ;; if the trees are identical, nothing is written + (write-changes out-dir tree tree) + (expect (not (file-exists? (.. out-dir "/dan")))) + + ;; add an entry + (write-changes out-dir tree (assoc (dup tree) "geoffrey" ["rr"])) + (expect (file-exists? (.. out-dir "/dan"))) + (expect= (with-open [f (io.open (.. out-dir "/geoffrey"))] (f:read "*a")) "rr\n") + + ;; newly-missing entries are removed + (write-changes out-dir (assoc (dup tree) "geoffrey" ["rr"]) tree) + (expect (not (file-exists? (.. out-dir "/geoffrey")))) + + (write-changes out-dir tree {}) + (os.remove out-dir) + )) + + +(fn run [] + (let [{: out-path : watched-service : path } (parse-args arg) + dir (.. watched-service "/.outputs") + service (assert (svc.open dir))] + (accumulate [tree (service:output path) + v (service:events)] + (write-changes out-path tree (service:output path))))) + + +{ : run }