nixos/fn.nix

237 lines
6.2 KiB
Nix

{ 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"
]
[
""
"::"
""
]
)
);
}