From e89cf20d4310070a877c2e24a287659546b561c9 Mon Sep 17 00:00:00 2001 From: tv Date: Wed, 28 Feb 2018 21:02:21 +0100 Subject: import stockholm's deployment tools https://cgit.krebsco.de/stockholm 877b4104370c1ea9698a449e376e2842d7c372fd --- lib/default.nix | 56 ++++++++++++++++++++++++++++++ lib/types/default.nix | 8 +++++ lib/types/populate.nix | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++ lib/types/posix.nix | 54 +++++++++++++++++++++++++++++ 4 files changed, 211 insertions(+) create mode 100644 lib/default.nix create mode 100644 lib/types/default.nix create mode 100644 lib/types/populate.nix create mode 100644 lib/types/posix.nix (limited to 'lib') diff --git a/lib/default.nix b/lib/default.nix new file mode 100644 index 0000000..7197fe9 --- /dev/null +++ b/lib/default.nix @@ -0,0 +1,56 @@ +let { + + body = lib; + + lib = nixpkgs.lib // builtins // { + + evalSource = let + eval = source: lib.evalModules { + modules = lib.singleton { + _file = toString ./.; + imports = map (source: { inherit source; }) (lib.toList source); + options.source = lib.mkOption { + default = {}; + type = lib.types.attrsOf lib.types.source; + }; + }; + }; + sanitize = x: lib.getAttr (lib.typeOf x) { + set = lib.mapAttrs + (lib.const sanitize) + (lib.filterAttrs + (name: value: name != "_module" && value != null) x); + string = x; + }; + in + # This function's return value can be used as pkgs.populate input. + source: sanitize (eval source).config.source; + + getHostName = let + # We're parsing /etc/hostname here because reading + # /proc/sys/kernel/hostname yields "" + y = lib.filter lib.types.label.check (lib.splitString "\n" (lib.readFile /etc/hostname)); + in + if lib.length y != 1 then throw "malformed /etc/hostname" else + lib.elemAt y 0; + + mkTarget = s: let + default = defVal: val: if val != null then val else defVal; + parse = lib.match "(([^@]+)@)?(([^:/]+))?(:([^/]+))?(/.*)?" s; + elemAt' = xs: i: if lib.length xs > i then lib.elemAt xs i else null; + in { + user = default (lib.getEnv "LOGNAME") (elemAt' parse 1); + host = default (lib.maybeEnv "HOSTNAME" lib.getHostName) (elemAt' parse 3); + port = default "22" /* "ssh"? */ (elemAt' parse 5); + path = default "/var/src" /* no default? */ (elemAt' parse 6); + }; + + test = re: x: lib.isString x && lib.testString re x; + testString = re: x: lib.match re x != null; + + types = nixpkgs.lib.types // import ./types { lib = body; }; + }; + + nixpkgs.lib = import ; + +} diff --git a/lib/types/default.nix b/lib/types/default.nix new file mode 100644 index 0000000..c4bf517 --- /dev/null +++ b/lib/types/default.nix @@ -0,0 +1,8 @@ +{ lib }@args: let { + + body = lib.foldl' (res: path: res // import path args) {} [ + ./populate.nix + ./posix.nix + ]; + +} diff --git a/lib/types/populate.nix b/lib/types/populate.nix new file mode 100644 index 0000000..3b13df0 --- /dev/null +++ b/lib/types/populate.nix @@ -0,0 +1,93 @@ +{ lib }: rec { + + source = lib.types.submodule ({ config, ... }: { + options = { + type = let + known-types = lib.attrNames source-types; + type-candidates = lib.filter (k: config.${k} != null) known-types; + in lib.mkOption { + default = if lib.length type-candidates == 1 + then lib.head type-candidates + else throw "cannot determine type"; + type = lib.types.enum known-types; + }; + file = lib.mkOption { + apply = x: + if lib.types.absolute-pathname.check x + then { path = x; } + else x; + default = null; + type = lib.types.nullOr (lib.types.either lib.types.absolute-pathname source-types.file); + }; + git = lib.mkOption { + default = null; + type = lib.types.nullOr source-types.git; + }; + pass = lib.mkOption { + default = null; + type = lib.types.nullOr source-types.pass; + }; + pipe = lib.mkOption { + apply = x: + if lib.types.absolute-pathname.check x + then { command = x; } + else x; + default = null; + type = lib.types.nullOr (lib.types.either lib.types.absolute-pathname source-types.pipe); + }; + symlink = lib.mkOption { + apply = x: + if lib.types.pathname.check x + then { target = x; } + else x; + default = null; + type = lib.types.nullOr (lib.types.either lib.types.pathname source-types.symlink); + }; + }; + }); + + source-types = { + file = lib.types.submodule { + options = { + path = lib.mkOption { + type = lib.types.absolute-pathname; + }; + }; + }; + git = lib.types.submodule { + options = { + ref = lib.mkOption { + type = lib.types.str; # TODO lib.types.git.ref + }; + url = lib.mkOption { + type = lib.types.str; # TODO lib.types.git.url + }; + }; + }; + pass = lib.types.submodule { + options = { + dir = lib.mkOption { + type = lib.types.absolute-pathname; + }; + name = lib.mkOption { + type = lib.types.pathname; # TODO relative-pathname + }; + }; + }; + pipe = lib.types.submodule { + options = { + command = lib.mkOption { + type = lib.types.absolute-pathname; + }; + }; + }; + symlink = lib.types.submodule { + options = { + target = lib.mkOption { + type = lib.types.pathname; # TODO relative-pathname + }; + }; + }; + }; + +} diff --git a/lib/types/posix.nix b/lib/types/posix.nix new file mode 100644 index 0000000..e8f464e --- /dev/null +++ b/lib/types/posix.nix @@ -0,0 +1,54 @@ +{ lib }: rec { + + # RFC952, B. Lexical grammar, + hostname = lib.mkOptionType { + name = "hostname"; + check = x: lib.isString x && lib.all label.check (lib.splitString "." x); + merge = lib.mergeOneOption; + }; + + # RFC952, B. Lexical grammar, + # RFC1123, 2.1 Host Names and Numbers + label = lib.mkOptionType { + name = "label"; + # TODO case-insensitive labels + check = lib.test "[0-9A-Za-z]([0-9A-Za-z-]*[0-9A-Za-z])?"; + merge = lib.mergeOneOption; + }; + + # POSIX.1‐2013, 3.278 Portable Filename Character Set + filename = lib.mkOptionType { + name = "POSIX filename"; + check = lib.test "([0-9A-Za-z._])[0-9A-Za-z._-]*"; + merge = lib.mergeOneOption; + }; + + # POSIX.1‐2013, 3.2 Absolute Pathname + absolute-pathname = lib.mkOptionType { + name = "POSIX absolute pathname"; + check = x: lib.isString x && lib.substring 0 1 x == "/" && pathname.check x; + merge = lib.mergeOneOption; + }; + + # POSIX.1‐2013, 3.267 Pathname + pathname = lib.mkOptionType { + name = "POSIX pathname"; + check = x: + let + # The filter is used to normalize paths, i.e. to remove duplicated and + # trailing slashes. It also removes leading slashes, thus we have to + # check for "/" explicitly below. + xs = lib.filter (s: lib.stringLength s > 0) (lib.splitString "/" x); + in + lib.isString x && (x == "/" || (lib.length xs > 0 && lib.all filename.check xs)); + merge = lib.mergeOneOption; + }; + + # POSIX.1-2013, 3.431 User Name + username = lib.mkOptionType { + name = "POSIX username"; + check = filename.check; + merge = lib.mergeOneOption; + }; + +} -- cgit v1.2.3