{ pkgs, lib, } @ args: let inherit (lib) types isAttrs filterAttrs mapAttrs mkOption mkOptionType isType; in rec { prepend = key: arr: job: { ${key} = arr ++ (job.${key} or []); }; append = key: arr: job: { ${key} = (job.${key} or []) ++ arr; }; prependToBeforeScript = prepend "before_script"; appendToAfterScript = append "after_script"; # json is also valid yaml and this removes dependency on jq and/or remarshal # (used in pkgs.formats.json and pkgs.formats.yaml respectively) toYaml = name: value: builtins.toFile name (builtins.toJSON value); toYamlPretty = (pkgs.formats.yaml {}).generate; customMapAttrs = cb: set: builtins.listToAttrs (builtins.map (key: cb key (builtins.getAttr key set)) (builtins.attrNames set)); filterAttrsRec = pred: v: if isAttrs v then filterAttrs pred (mapAttrs (_path: filterAttrsRec pred) v) else v; # filter job's variables to either only those containing store paths # or those that do not filterJobVariables = nix: job: lib.concatMapAttrs ( name: value: lib.optionalAttrs ((lib.hasInfix "/nix/store/" value) == nix) { ${name} = value; } ) (job.variables or {}); deepMerge = lhs: rhs: lhs // rhs // (builtins.mapAttrs ( rName: rValue: let lValue = lhs.${rName} or null; in if builtins.isAttrs lValue && builtins.isAttrs rValue then deepMerge lValue rValue else if builtins.isList lValue && builtins.isList rValue then lValue ++ rValue else rValue ) rhs); unsetType = mkOptionType { name = "unset"; description = "unset"; descriptionClass = "noun"; check = _value: true; }; unset = { _type = "unset"; }; isUnset = isType "unset"; unsetOr = types.either unsetType; mkUnsetOption = opts: mkOption (opts // { type = unsetOr opts.type; default = opts.default or unset; }); filterUnset = value: if builtins.isAttrs value && !builtins.hasAttr "_type" value then let filteredAttrs = builtins.mapAttrs (_n: filterUnset) value; in filterAttrs (_name: value: (!isUnset value)) filteredAttrs else if builtins.isList value then builtins.filter (elem: !isUnset elem) (map filterUnset value) else value; # args.pkgs so "pkgs" does not need to be passed all the time stdenvMinimal = args.pkgs.stdenvNoCC.override { cc = null; preHook = ""; allowedRequisites = null; initialPath = with args.pkgs; [coreutils findutils]; extraNativeBuildInputs = []; }; }