{ pkgs, lib, } @ args: let inherit (lib) types isAttrs filterAttrs mapAttrs mkOption mkOptionType isType literalExpression pipe hasInfix concatMapAttrs optionalAttrs ; 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: pipe value [ builtins.toJSON builtins.unsafeDiscardOutputDependency builtins.unsafeDiscardStringContext (builtins.toFile name) ]; 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 = shouldContain: job: concatMapAttrs ( name: value: optionalAttrs ((hasInfix "/nix/store/" value) == shouldContain) { ${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 = typ: (types.either unsetType typ) // { inherit (typ) description getSubOptions; }; mkUnsetOption = opts: mkOption (opts // { type = unsetOr opts.type; default = opts.default or unset; defaultText = literalExpression "unset"; }); eitherWithSubOptions = typ_one: typ_two: (types.either typ_one typ_two) // { getSubOptions = if (typ_one.getSubOptions "test" != {}) then typ_one.getSubOptions else typ_two.getSubOptions; }; 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 = []; }; }