{ lib }: with builtins; with lib; rec { /** Shorthand for "if condition then a else b" # Type ``` ifelse :: Bool -> Any -> Any -> Any ``` # Arguments - [condition] A condition evaluating to a boolean - [a] Result if condition evaluates to true - [b] Result if condition evaluates to false # Example ```nix let myData = { val = "custom"; }; in ifelse (myData ? val) myData.val "default" => "custom" ``` */ ifelse = condition: a: b: if condition then a else b; /** Value of PWD environment variable at evaluation time */ cwd = builtins.getEnv "PWD"; /** lst (string PATH) (string FILETYPE) (bool RETURNFULLPATH) */ lst = { path ? cwd, fileType ? "regular", fullPath ? false, }: assert (builtins.isString path) || throw "Argument path needs to be a string."; (lists.forEach (attrNames (filterAttrs (n: v: v == fileType) (readDir path))) ( v: ((optionalString fullPath "${path}/") + v) )); lsf = path: (lst { inherit path; }); lsd = path: (lst { inherit path; fileType = "directory"; fullPath = true; }); lsfRec = path: fullPath: flatten ( (map (nextPath: lsfRec nextPath fullPath) (lsd path)) ++ (lst { inherit path fullPath; }) ); hasAttrs = aList: d: (map (a: (ifelse (isList a) (hasAttrByPath a d) (hasAttr a d))) aList); # Not sure how list operations are implemented in Nix # This might be a tad bit inefficient. # Sequentially checks elements of list (l) for condition (cond) and executes do on first match. /** Run lambda "(do element)" on the first element of list "l" where (cond element) returns true otherwise return false. # Type ``` meetsConDo :: (any -> bool) -> (any -> any) -> [any] -> any ``` # Arguments: - [cond] Condition function - [do] Function to run on element if cond returns true - [list] List of elements # Example: ```nix meetsConDo (element: element >= 5) (element: element + 1) [ 4 2 0 5 1 ] => 6 meetsConDo (element: element >= 5) (element: element + 1) [ 4 2 0 1 ] => false ``` */ meetsConDo = cond: do: list: let listLen = length list; meetsConDo' = currentIndex: ifelse (currentIndex == listLen) false ( let head = elemAt list currentIndex; in ifelse (cond head) (do head) (meetsConDo' (currentIndex + 1)) ); in meetsConDo' 0; deps = p: ifelse (isAttrs p) (filter isAttrs ( p.buildInputs ++ p.nativeBuildInputs ++ p.propagatedBuildInputs ++ p.propagatedNativeBuildInputs )) [ ]; importFilter = l: filter (n: elem (nameFromURL (toString n) ".") l); depsRec = ld: ifelse (ld == [ ]) [ ] ( (toList ld) ++ (depsRec (lists.unique (lists.flatten (map deps (toList ld))))) ); isBroken = p: meetsConDo (s: ((hasAttrByPath s.path p) && (s.check (getAttrFromPath s.path p)))) (s: s.msg) [ { path = [ "meta" "broken" ]; msg = warn "Package ${p.name} is marked as broken." true; check = m: m; } { path = [ "meta" "knownVulnerabilities" ]; msg = warn "Package ${p.name} has known Vulnerabilities.." true; check = m: m != [ ]; } { path = [ "name" ]; msg = warn "${p.name}: python2 is depricated." false; check = m: (strings.hasInfix "python2" m) || (strings.hasInfix "python-2" m); } # not sure if the following test creates false positives (AFAIK every derivation/package needs to have an outPath) # , definitely should catch all corner cases/everything that fails to evaluate. { path = [ "outPath" ]; msg = warn "Package ${p.name} has no outPath" true; check = m: !(tryEval m).success; } ]; depsBroken = p: lists.any (p: (isBroken p)) (deps p); # No more magic 🧙 here 😢 # But at least it now (hopefully) checks ONLY dependencies (and all of them at that). depsBrokenRec = p: (meetsConDo (p: ifelse (depsBroken p) true (depsBrokenRec (deps p))) (p: true) (deps p)); /** Helper function to generate secret definitions for sops-nix. # Type ``` sopsHelper :: () ``` # Arguments # Examples ```nix sopsHelper (name: "services/nextcloud/${name}") [ "adminPass" "dbPass" ] { owner = "nextcloud"; group = "nextcloud"; } => { "services/nextcloud/adminPass" = { group = "nextcloud"; owner = "nextcloud"; }; "services/nextcloud/dbPass" = { group = "nextcloud"; owner = "nextcloud"; }; } sopsHelper (user: "users/${user}/publicKey") [ "alice" "bob" "eve" ] (user: { path = "/etc/ssh/authorized_keys.d/${user}"; mode = "444"; }) => { "users/alice/publicKey" = { mode = "444"; path = "/etc/ssh/authorized_keys.d/alice"; }; "users/bob/publicKey" = { mode = "444"; path = "/etc/ssh/authorized_keys.d/bob"; }; "users/eve/publicKey" = { mode = "444"; path = "/etc/ssh/authorized_keys.d/eve"; }; } ``` */ sopsHelper = template: names: options: let optionsIsFunction = (typeOf options) == "lambda"; in listToAttrs ( map (name: { name = template name; value = ifelse optionsIsFunction (options name) options; }) names ); pkgFilter = ld: (filter ( p: (ifelse (isBroken p) false ( ifelse (depsBrokenRec p) (warn "Dependency of ${p.name} is marked as broken." false) true )) ) ld); makeOptionTypeList = path: (lists.forEach # get a list of all files ending in .nix in path (filter (hasSuffix ".nix") (lsfRec path true)) # remove leading path and trailing ".nix", replace every slash with "::" ( replaceStrings [ "${path}/" "/" ".nix" ] [ "" "::" "" ] ) ); }