diff --git a/builtin-modules/default-formatters.nix b/builtin-modules/default-formatters.nix deleted file mode 100644 index 8c80bd3..0000000 --- a/builtin-modules/default-formatters.nix +++ /dev/null @@ -1,7 +0,0 @@ -_: { - devTools = pkgs: with pkgs; [ nixpkgs-fmt nodePackages.prettier ]; - formatters = { - "*.nix" = "nixpkgs-fmt"; - "*.md | *.json | *.yml" = "prettier --write"; - }; -} diff --git a/builtin-modules/editorconfig.nix b/builtin-modules/editorconfig.nix deleted file mode 100644 index f6b7df6..0000000 --- a/builtin-modules/editorconfig.nix +++ /dev/null @@ -1,13 +0,0 @@ -{ src, lib }: -let - inherit (lib) getExe optionalAttrs optionalString pathExists; -in -{ - checks = optionalAttrs (pathExists (src + /.editorconfig)) { - # By default, high false-positive flags are disabled. - editorconfig = { editorconfig-checker }: - "${getExe editorconfig-checker}" - + optionalString (!pathExists (src + /.ecrc)) - " -disable-indent-size -disable-max-line-length"; - }; -} diff --git a/builtin-modules/pre-commit-hook.nix b/builtin-modules/pre-commit-hook.nix deleted file mode 100644 index 8b0e48c..0000000 --- a/builtin-modules/pre-commit-hook.nix +++ /dev/null @@ -1,10 +0,0 @@ -_: { - shellHook = { lib, flakelite }: '' - if [ -f flake.nix ] && [ -d .git/hooks ] && - [ ! -f .git/hooks/pre-commit ]; then - echo Installing git pre-commit hook... - cp ${lib.getExe flakelite.inputs'.flakelite.packages.pre-commit-hook - } .git/hooks - fi - ''; -} diff --git a/builtinModules/apps.nix b/builtinModules/apps.nix new file mode 100644 index 0000000..5b7c783 --- /dev/null +++ b/builtinModules/apps.nix @@ -0,0 +1,43 @@ +# flakelite -- Framework for making flakes simple +# Copyright (C) 2023 Archit Gupta +# SPDX-License-Identifier: MIT + +{ config, lib, flakelite, ... }: +let + inherit (lib) isFunction mapAttrs mkIf mkMerge mkOption; + inherit (lib.types) lazyAttrsOf nullOr raw; + inherit (flakelite.types) optFunctionTo; + + isApp = x: (x ? type) && (x.type == "app") && (x ? program); + + mkApp = pkgs: app: + let + app' = if isFunction app then app pkgs else app; + in + if isApp app' then app' else { type = "app"; program = "${app'}"; }; +in +{ + options = { + app = mkOption { + type = nullOr raw; + default = null; + }; + + apps = mkOption { + type = nullOr (optFunctionTo (lazyAttrsOf raw)); + default = null; + }; + }; + + config = mkMerge [ + (mkIf (config.app != null) { + apps.default = config.app; + }) + + (mkIf (config.apps != null) { + perSystem = pkgs: { + apps = mapAttrs (_: mkApp pkgs) (config.apps pkgs); + }; + }) + ]; +} diff --git a/builtinModules/builtinFormatters.nix b/builtinModules/builtinFormatters.nix new file mode 100644 index 0000000..7396ac4 --- /dev/null +++ b/builtinModules/builtinFormatters.nix @@ -0,0 +1,24 @@ +# flakelite -- Framework for making flakes simple +# Copyright (C) 2023 Archit Gupta +# SPDX-License-Identifier: MIT + +{ lib, ... }: +let + inherit (lib) mkEnableOption; +in +{ + options.flakelite.builtinFormatters = + mkEnableOption "default formatters" // { default = true; }; + + config = { + devShell.packages = pkgs: [ + pkgs.nixpkgs-fmt + pkgs.nodePackages.prettier + ]; + + formatters = { + "*.nix" = "nixpkgs-fmt"; + "*.md | *.json | *.yml" = "prettier --write"; + }; + }; +} diff --git a/builtinModules/builtinOverlay.nix b/builtinModules/builtinOverlay.nix new file mode 100644 index 0000000..422e868 --- /dev/null +++ b/builtinModules/builtinOverlay.nix @@ -0,0 +1,46 @@ +# flakelite -- Framework for making flakes simple +# Copyright (C) 2023 Archit Gupta +# SPDX-License-Identifier: MIT + +{ config, src, lib, inputs, outputs, flakelite, ... }: +let + inherit (lib) isList mkOption mkOrder mapAttrs optionalAttrs; + inherit (lib.types) listOf nullOr oneOf str; +in +{ + options = { + description = mkOption { + type = nullOr str; + default = null; + }; + + license = mkOption { + type = nullOr (oneOf [ str (listOf str) ]); + default = null; + }; + }; + + config.withOverlays = mkOrder 10 (final: prev: + let + system = prev.stdenv.hostPlatform.system; + in + { + inherit src inputs outputs flakelite system; + inputs' = mapAttrs (_: mapAttrs (_: v: v.${system} or { })) inputs; + outputs' = mapAttrs (_: v: v.${system} or { }) outputs; + + defaultMeta = { + platforms = config.systems; + } // optionalAttrs (config.description != null) { + inherit (config) description; + } // optionalAttrs (config.license != null) { + license = + let + getLicense = license: final.lib.licenses.${license} or + (final.lib.meta.getLicenseFromSpdxId license); + in + if isList config.license then map getLicense config.license + else getLicense config.license; + }; + }); +} diff --git a/builtinModules/checks.nix b/builtinModules/checks.nix new file mode 100644 index 0000000..ac365fd --- /dev/null +++ b/builtinModules/checks.nix @@ -0,0 +1,32 @@ +# flakelite -- Framework for making flakes simple +# Copyright (C) 2023 Archit Gupta +# SPDX-License-Identifier: MIT + +{ config, src, lib, flakelite, ... }: +let + inherit (lib) isDerivation isFunction mkOption mkIf mapAttrs; + inherit (lib.types) lazyAttrsOf nullOr raw; + inherit (flakelite.types) optFunctionTo; + + mkCheck = pkgs: src: name: cmd: + let + cmd' = if isFunction cmd then cmd pkgs else cmd; + in + if isDerivation cmd' then cmd' else + pkgs.runCommand "check-${name}" { } '' + cp --no-preserve=mode -r ${src} src + cd src + ${cmd'} + touch $out + ''; +in +{ + options.checks = mkOption { + type = nullOr (optFunctionTo (lazyAttrsOf raw)); + default = null; + }; + + config.perSystem = mkIf (config.checks != null) (pkgs: { + checks = mapAttrs (mkCheck pkgs src) (config.checks pkgs); + }); +} diff --git a/builtinModules/core.nix b/builtinModules/core.nix new file mode 100644 index 0000000..03476ad --- /dev/null +++ b/builtinModules/core.nix @@ -0,0 +1,69 @@ +# flakelite -- Framework for making flakes simple +# Copyright (C) 2023 Archit Gupta +# SPDX-License-Identifier: MIT + +{ config, inputs, lib, flakelite, ... }: +let + inherit (builtins) all head isAttrs length; + inherit (lib) foldAttrs getFiles getValues mapAttrs mergeAttrs mkOption + mkOptionType showFiles showOption; + inherit (lib.types) functionTo lazyAttrsOf listOf nonEmptyStr raw uniq; + inherit (flakelite.types) optListOf overlay; + + outputs = mkOptionType { + name = "outputs"; + description = "output values"; + descriptionClass = "noun"; + merge = loc: defs: + if (length defs) == 1 then (head defs).value + else if all isAttrs (getValues defs) then + (lazyAttrsOf outputs).merge loc defs + else throw "The option `${showOption loc}' has conflicting definitions in ${showFiles (getFiles defs)}"; + }; +in +{ + options = { + inputs = mkOption { + type = lazyAttrsOf raw; + }; + + systems = mkOption { + type = uniq (listOf nonEmptyStr); + default = [ "x86_64-linux" "aarch64-linux" ]; + }; + + outputs = mkOption { + type = lazyAttrsOf outputs; + default = { }; + }; + + perSystem = mkOption { + type = functionTo (lazyAttrsOf outputs); + default = _: { }; + }; + + nixpkgs.config = mkOption { + type = lazyAttrsOf raw; + default = { }; + }; + + withOverlays = mkOption { + type = optListOf overlay; + default = [ ]; + }; + }; + + config = { + _module.args = { inherit (config) inputs outputs; }; + + outputs = foldAttrs mergeAttrs { } (map + (system: mapAttrs + (_: v: { ${system} = v; }) + (config.perSystem (import inputs.nixpkgs { + inherit system; + config = config.nixpkgs.config; + overlays = config.withOverlays; + }))) + config.systems); + }; +} diff --git a/builtinModules/devShells.nix b/builtinModules/devShells.nix new file mode 100644 index 0000000..a57e5cd --- /dev/null +++ b/builtinModules/devShells.nix @@ -0,0 +1,70 @@ +# flakelite -- Framework for making flakes simple +# Copyright (C) 2023 Archit Gupta +# SPDX-License-Identifier: MIT + +{ config, lib, flakelite, ... }: +let + inherit (lib) any attrValues filterAttrs mapAttrs mkIf mkMerge mkOption + optionalAttrs; + inherit (lib.types) lazyAttrsOf functionTo lines listOf nullOr package str; + inherit (flakelite) supportedSystem; + inherit (flakelite.types) optFunctionTo packageDef; +in +{ + options = { + devShell = { + inputsFrom = mkOption { + type = nullOr + (functionTo (listOf package)); + default = null; + }; + + packages = mkOption { + type = nullOr + (functionTo (listOf package)); + default = null; + }; + + shellHook = mkOption { + type = nullOr (optFunctionTo lines); + default = null; + }; + + env = mkOption { + type = nullOr + (optFunctionTo (lazyAttrsOf str)); + default = null; + }; + }; + + devShells = mkOption { + type = lazyAttrsOf packageDef; + default = { }; + }; + }; + + config = mkMerge [ + (mkIf (any (x: x != null) (attrValues config.devShell)) { + devShells.default = { pkgs, mkShell }: mkShell ( + optionalAttrs (config.devShell.env != null) + (config.devShell.env pkgs) + // optionalAttrs (config.devShell.inputsFrom != null) { + inputsFrom = config.devShell.inputsFrom pkgs; + } + // optionalAttrs (config.devShell.packages != null) { + packages = config.devShell.packages pkgs; + } + // optionalAttrs (config.devShell.shellHook != null) { + shellHook = config.devShell.shellHook pkgs; + } + ); + }) + + (mkIf (config.devShells != { }) { + perSystem = pkgs: { + devShells = filterAttrs (_: supportedSystem pkgs) + (mapAttrs (_: v: pkgs.callPackage v { }) config.devShells); + }; + }) + ]; +} diff --git a/builtinModules/editorconfig.nix b/builtinModules/editorconfig.nix new file mode 100644 index 0000000..da821a9 --- /dev/null +++ b/builtinModules/editorconfig.nix @@ -0,0 +1,22 @@ +# flakelite -- Framework for making flakes simple +# Copyright (C) 2023 Archit Gupta +# SPDX-License-Identifier: MIT + +{ config, lib, src, ... }: +let + inherit (lib) mkEnableOption mkIf optionalString pathExists; +in +{ + options.flakelite.editorconfig = + mkEnableOption "editorconfig check" // { default = true; }; + + config.checks = mkIf + (config.flakelite.editorconfig && (pathExists (src + /.editorconfig))) + { + # By default, high false-positive flags are disabled. + editorconfig = { editorconfig-checker, ... }: + "${editorconfig-checker}/bin/editorconfig-checker" + + optionalString (!pathExists (src + /.ecrc)) + " -disable-indent-size -disable-max-line-length"; + }; +} diff --git a/builtinModules/formatter.nix b/builtinModules/formatter.nix new file mode 100644 index 0000000..bbfb5e7 --- /dev/null +++ b/builtinModules/formatter.nix @@ -0,0 +1,40 @@ +# flakelite -- Framework for making flakes simple +# Copyright (C) 2023 Archit Gupta +# SPDX-License-Identifier: MIT + +{ config, src, lib, flakelite, ... }: +let + inherit (lib) mkOption mkIf mapAttrsToList; + inherit (lib.types) lazyAttrsOf nullOr str; + inherit (flakelite.types) optFunctionTo; +in +{ + options.formatters = mkOption { + type = nullOr (optFunctionTo (lazyAttrsOf str)); + default = null; + }; + + config = mkIf (config.formatters != null) { + perSystem = { pkgs, lib, fd, coreutils, ... }: { + formatter = pkgs.writeShellScriptBin "formatter" '' + PATH=${lib.makeBinPath (config.devShell.packages pkgs)} + for f in "$@"; do + if [ -d "$f" ]; then + ${fd}/bin/fd "$f" -Htf -x "$0" + else + case "$(${coreutils}/bin/basename "$f")" in + ${toString (mapAttrsToList + (n: v: "${n}) ${v} \"$f\";;") (config.formatters pkgs))} + esac + fi + done &>/dev/null + ''; + }; + + checks.formatting = { lib, outputs', diffutils, ... }: '' + ${lib.getExe outputs'.formatter} . + ${diffutils}/bin/diff -qr ${src} . |\ + sed 's/Files .* and \(.*\) differ/File \1 not formatted/g' + ''; + }; +} diff --git a/builtinModules/homeConfigurations.nix b/builtinModules/homeConfigurations.nix new file mode 100644 index 0000000..0b502f3 --- /dev/null +++ b/builtinModules/homeConfigurations.nix @@ -0,0 +1,34 @@ +# flakelite -- Framework for making flakes simple +# Copyright (C) 2023 Archit Gupta +# SPDX-License-Identifier: MIT + +{ config, lib, ... }: +let + inherit (builtins) isAttrs; + inherit (lib) foldl mapAttrsToList mergeOneOption mkOption mkOptionType mkIf + recursiveUpdate; + inherit (lib.types) lazyAttrsOf; + + homeConfiguration = mkOptionType { + name = "homeConfiguration"; + description = "homeConfiguration"; + descriptionClass = "noun"; + check = x: isAttrs x && x ? activationPackage; + merge = mergeOneOption; + }; +in +{ + options.homeConfigurations = mkOption { + type = lazyAttrsOf homeConfiguration; + default = { }; + }; + + config.outputs = mkIf (config.homeConfigurations != { }) { + inherit (config) homeConfigurations; + checks = foldl recursiveUpdate { } (mapAttrsToList + (n: v: { + ${v.config.nixpkgs.system}."home-${n}" = v.activationPackage; + }) + config.homeConfigurations); + }; +} diff --git a/builtinModules/homeModules.nix b/builtinModules/homeModules.nix new file mode 100644 index 0000000..76b733d --- /dev/null +++ b/builtinModules/homeModules.nix @@ -0,0 +1,33 @@ +# flakelite -- Framework for making flakes simple +# Copyright (C) 2023 Archit Gupta +# SPDX-License-Identifier: MIT + +{ config, lib, flakelite, ... }: +let + inherit (lib) mkOption mkIf mkMerge; + inherit (lib.types) lazyAttrsOf nullOr; + inherit (flakelite.types) module; +in +{ + options = { + homeModule = mkOption { + type = nullOr module; + default = null; + }; + + homeModules = mkOption { + type = lazyAttrsOf module; + default = { }; + }; + }; + + config = mkMerge [ + (mkIf (config.homeModule != null) { + homeModules.default = config.homeModule; + }) + + (mkIf (config.homeModules != { }) { + outputs = { inherit (config) homeModules; }; + }) + ]; +} diff --git a/builtinModules/nixDir.nix b/builtinModules/nixDir.nix new file mode 100644 index 0000000..b8bb632 --- /dev/null +++ b/builtinModules/nixDir.nix @@ -0,0 +1,75 @@ +# flakelite -- Framework for making flakes simple +# Copyright (C) 2023 Archit Gupta +# SPDX-License-Identifier: MIT + +{ config, src, lib, flakelite, ... }@args: +let + inherit (lib) isFunction mkOption mkIf mkMerge optionalAttrs; + inherit (flakelite) autoImport; + inherit (flakelite.types) path; +in +{ + options.nixDir = mkOption { + type = path; + default = src + /nix; + }; + + config = + let + autoImport' = autoImport config.nixDir; + autoImportArgs = n: + let v = autoImport' n; in + if isFunction v then v args else v; + outputs = autoImportArgs "outputs"; + perSystem = autoImport' "perSystem"; + withOverlays = autoImport' "withOverlays"; + package = autoImport' "package"; + packages = autoImportArgs "packages"; + overlays = autoImportArgs "overlays"; + devShell = autoImportArgs "devShell"; + devShells = autoImportArgs "devShells"; + app = autoImport' "app"; + apps = autoImport' "apps"; + checks = autoImport' "checks"; + nixosModule = autoImport' "nixosModule"; + nixosModules = autoImportArgs "nixosModules"; + nixosConfigurations = autoImportArgs [ "nixosConfigurations" "nixos" ]; + homeModule = autoImport' "homeModule"; + homeModules = autoImportArgs "homeModules"; + homeConfigurations = autoImportArgs [ "homeConfigurations" "home" ]; + template = autoImportArgs "template"; + templates = autoImportArgs "templates"; + formatters = autoImport' "formatters"; + in + mkMerge [ + (mkIf (outputs != null) { inherit outputs; }) + (mkIf (perSystem != null) { inherit perSystem; }) + (mkIf (withOverlays != null) { inherit withOverlays; }) + (mkIf (package != null) { inherit package; }) + (mkIf (packages != null) { inherit packages; }) + (mkIf (overlays != null) { inherit overlays; }) + (mkIf (devShell != null) { + devShell = optionalAttrs (devShell ? inputsFrom) + { inherit (devShell) inputsFrom; } + // optionalAttrs (devShell ? packages) + { inherit (devShell) packages; } + // optionalAttrs (devShell ? shellHook) + { inherit (devShell) shellHook; } + // optionalAttrs (devShell ? env) + { inherit (devShell) env; }; + }) + (mkIf (devShells != null) { inherit devShells; }) + (mkIf (app != null) { inherit app; }) + (mkIf (apps != null) { inherit apps; }) + (mkIf (checks != null) { inherit checks; }) + (mkIf (nixosModule != null) { inherit nixosModule; }) + (mkIf (nixosModules != null) { inherit nixosModules; }) + (mkIf (nixosConfigurations != null) { inherit nixosConfigurations; }) + (mkIf (homeModule != null) { inherit homeModule; }) + (mkIf (homeModules != null) { inherit homeModules; }) + (mkIf (homeConfigurations != null) { inherit homeConfigurations; }) + (mkIf (template != null) { inherit template; }) + (mkIf (templates != null) { inherit templates; }) + (mkIf (formatters != null) { inherit formatters; }) + ]; +} diff --git a/builtinModules/nixosConfigurations.nix b/builtinModules/nixosConfigurations.nix new file mode 100644 index 0000000..c405d3d --- /dev/null +++ b/builtinModules/nixosConfigurations.nix @@ -0,0 +1,37 @@ +# flakelite -- Framework for making flakes simple +# Copyright (C) 2023 Archit Gupta +# SPDX-License-Identifier: MIT + +{ config, lib, ... }: +let + inherit (builtins) isAttrs; + inherit (lib) foldl mapAttrsToList mergeOneOption mkIf mkOption mkOptionType + recursiveUpdate; + inherit (lib.types) lazyAttrsOf; + + nixosConfiguration = mkOptionType { + name = "nixosConfiguration"; + description = "nixosConfiguration"; + descriptionClass = "noun"; + check = x: isAttrs x + && x ? config.nixpkgs.system + && x ? config.system.build.toplevel; + merge = mergeOneOption; + }; +in +{ + options.nixosConfigurations = mkOption { + type = lazyAttrsOf nixosConfiguration; + default = { }; + }; + + config.outputs = mkIf (config.nixosConfigurations != { }) { + inherit (config) nixosConfigurations; + checks = foldl recursiveUpdate { } (mapAttrsToList + (n: v: { + ${v.config.nixpkgs.system}."nixos-${n}" = + v.config.system.build.toplevel; + }) + config.nixosConfigurations); + }; +} diff --git a/builtinModules/nixosModules.nix b/builtinModules/nixosModules.nix new file mode 100644 index 0000000..053a13a --- /dev/null +++ b/builtinModules/nixosModules.nix @@ -0,0 +1,33 @@ +# flakelite -- Framework for making flakes simple +# Copyright (C) 2023 Archit Gupta +# SPDX-License-Identifier: MIT + +{ config, lib, flakelite, ... }: +let + inherit (lib) mkOption mkIf mkMerge; + inherit (lib.types) lazyAttrsOf nullOr; + inherit (flakelite.types) module; +in +{ + options = { + nixosModule = mkOption { + type = nullOr module; + default = null; + }; + + nixosModules = mkOption { + type = lazyAttrsOf module; + default = { }; + }; + }; + + config = mkMerge [ + (mkIf (config.nixosModule != null) { + nixosModules.default = config.nixosModule; + }) + + (mkIf (config.nixosModules != { }) { + outputs = { inherit (config) nixosModules; }; + }) + ]; +} diff --git a/builtinModules/overlays.nix b/builtinModules/overlays.nix new file mode 100644 index 0000000..400f48f --- /dev/null +++ b/builtinModules/overlays.nix @@ -0,0 +1,18 @@ +# flakelite -- Framework for making flakes simple +# Copyright (C) 2023 Archit Gupta +# SPDX-License-Identifier: MIT + +{ config, lib, flakelite, ... }: +let + inherit (lib) mkOption mkIf; + inherit (lib.types) lazyAttrsOf; + inherit (flakelite.types) overlay; +in +{ + options.overlays = mkOption { + type = lazyAttrsOf overlay; + default = { }; + }; + + config.outputs = mkIf (config.overlays != { }) { inherit (config) overlays; }; +} diff --git a/builtinModules/packages.nix b/builtinModules/packages.nix new file mode 100644 index 0000000..b1c470c --- /dev/null +++ b/builtinModules/packages.nix @@ -0,0 +1,68 @@ +# flakelite -- Framework for making flakes simple +# Copyright (C) 2023 Archit Gupta +# SPDX-License-Identifier: MIT + +{ config, lib, flakelite, ... }: +let + inherit (builtins) attrNames functionArgs intersectAttrs parseDrvName; + inherit (lib) composeManyExtensions filterAttrs fix genAttrs mapAttrs mapAttrs' + mkIf mkMerge mkOption mkOrder nameValuePair optional optionalAttrs remove; + inherit (lib.types) lazyAttrsOf nullOr; + inherit (flakelite) supportedSystem; + inherit (flakelite.types) packageDef; + + getName = pkg: pkg.pname or (parseDrvName pkg.name).name; + + callPkg = pkgs: def: def (intersectAttrs (functionArgs def) pkgs); +in +{ + options = { + package = mkOption { + type = nullOr packageDef; + default = null; + }; + + packages = mkOption { + type = lazyAttrsOf packageDef; + default = { }; + }; + }; + + config = mkMerge [ + (mkIf (config.package != null) { + packages.default = config.package; + }) + + (mkIf (config.packages != { }) { + withOverlays = mkOrder 2000 (final: prev: + let + pkgNames = attrNames config.packages; + pkgNames' = remove "default" pkgNames; + defaultName = getName (fix (self: + prev // (genAttrs pkgNames + (n: callPkg self config.packages.${n})))).default; + in + (optionalAttrs (config.packages ? default) { + ${defaultName} = final.callPackage config.packages.default { }; + }) // + (genAttrs pkgNames' (n: final.callPackage config.packages.${n} { }))); + + overlays.default = final: prev: + let + pkgs' = fix (composeManyExtensions config.withOverlays) prev; + defaultName = getName (callPkg pkgs' config.packages.default); + pkgNames = (remove "default" (attrNames config.packages)) + ++ (optional (config.packages ? default) defaultName); + pkgs = final.appendOverlays config.withOverlays; + in + genAttrs pkgNames (n: pkgs.${n}); + + perSystem = pkgs: rec { + packages = filterAttrs (_: supportedSystem pkgs) + (mapAttrs (_: v: pkgs.callPackage v { }) config.packages); + + checks = mapAttrs' (n: nameValuePair ("packages-" + n)) packages; + }; + }) + ]; +} diff --git a/builtinModules/templates.nix b/builtinModules/templates.nix new file mode 100644 index 0000000..04eab51 --- /dev/null +++ b/builtinModules/templates.nix @@ -0,0 +1,42 @@ +# flakelite -- Framework for making flakes simple +# Copyright (C) 2023 Archit Gupta +# SPDX-License-Identifier: MIT + +{ config, lib, ... }: +let + inherit (builtins) isPath isString; + inherit (lib) mkOption mkOptionType mkIf mkMerge; + inherit (lib.types) lazyAttrsOf nullOr; + + template = mkOptionType { + name = "template"; + description = "template definition"; + descriptionClass = "noun"; + check = x: (x ? path) && (isPath x.path) && + (x ? description) && (isString x.description) && + ((! x ? welcomeText) || (isString x.welcomeText)); + }; +in +{ + options = { + template = mkOption { + type = nullOr template; + default = null; + }; + + templates = mkOption { + type = lazyAttrsOf template; + default = { }; + }; + }; + + config = mkMerge [ + (mkIf (config.template != null) { + templates.default = config.template; + }) + + (mkIf (config.templates != { }) { + outputs = { inherit (config) templates; }; + }) + ]; +} diff --git a/default.nix b/default.nix index f6983b6..86863c6 100644 --- a/default.nix +++ b/default.nix @@ -4,65 +4,69 @@ localInputs: let - inherit (builtins) intersectAttrs isPath readDir; - inherit (localInputs.nixpkgs.lib) attrNames attrVals attrValues - callPackageWith composeManyExtensions concat concatStringsSep filter - filterAttrs findFirst foldAttrs foldl functionArgs genAttrs hasSuffix - isFunction isList isString listToAttrs mapAttrs mapAttrsToList mapAttrs' - mergeAttrs nameValuePair optional optionalAttrs parseDrvName pathExists pipe - recursiveUpdate removePrefix removeSuffix zipAttrsWith; + inherit (builtins) isAttrs isPath readDir; + inherit (localInputs.nixpkgs.lib) attrNames composeManyExtensions + filter findFirst genAttrs getValues hasSuffix isFunction isList mapAttrs + mapAttrsToList pathExists pipe removePrefix removeSuffix evalModules + mkDefault mkOptionType singleton; + inherit (localInputs.nixpkgs.lib.types) coercedTo functionTo listOf; - /* Attributes in flakelite's lib output. - */ - exports = { - inherit mkFlake systems importDir autoImport autoImportAttrs defaultPkgName - supportedSystem mergeModules moduleAttrs rootAttrs ensureFn callFn - filterArgs callPkg callPkgs tryImport mkApp mkCheck liftFn2 fnConcat - fnMergeAttrs; - }; + builtinModules = mapAttrsToList (k: _: ./builtinModules + ("/" + k)) + (readDir ./builtinModules); - /* Module which is always included as first module. - */ - baseModule = { inputs, root, args }: { - # Ensures nixpkgs and flakelite are available for modules. - inputs = { - flakelite = localInputs.self; - inherit (localInputs) nixpkgs; - }; - withOverlay = final: prev: { - # Allows access to flakelite lib functions from package sets. - # Also adds pkgs-specific additional args. - flakelite = args // { - # Inputs with system auto-selected. - # i.e. inputs.self.packages.${system} -> inputs'.self.packages - inputs' = mapAttrs - (_: mapAttrs - (_: v: v.${prev.system} or { })) - inputs; - # Default package meta attribute generated from root module attrs. - meta = { - platforms = root.systems; - } // optionalAttrs (root ? description) { - inherit (root) description; - } // optionalAttrs (root ? license) { - license = - if isList root.license - then attrVals root.license final.lib.licenses - else final.lib.licenses.${root.license}; - }; + mkFlake = src: root: (evalModules { + specialArgs.modulesPath = ./builtinModules; + modules = builtinModules ++ [ + { inputs.nixpkgs = mkDefault localInputs.nixpkgs; } + { _module.args = { inherit src flakelite; }; } + root + ]; + }).config.outputs; + + flakelite = { + inherit mkFlake supportedSystem importDir autoImport; + + types = { + overlay = mkOptionType { + name = "overlay"; + description = "nixpkgs overlay"; + descriptionClass = "noun"; + check = isFunction; + merge = _: defs: composeManyExtensions (getValues defs); }; - # These attrs are important enough for top-level pkg set. - inherit (final.flakelite) inputs inputs'; + + packageDef = mkOptionType { + name = "packageDef"; + description = "package definition"; + descriptionClass = "noun"; + check = isFunction; + }; + + path = mkOptionType { + name = "path"; + description = "path"; + descriptionClass = "noun"; + check = isPath; + }; + + module = mkOptionType { + name = "module"; + description = "module"; + descriptionClass = "noun"; + check = x: isPath x || isFunction x || isAttrs x; + merge = _: defs: { imports = getValues defs; }; + }; + + optListOf = elemType: coercedTo elemType singleton (listOf elemType); + + optFunctionTo = elemType: coercedTo elemType (x: _: x) + (functionTo elemType); }; }; - builtinModules = attrValues (importDir ./builtin-modules); + supportedSystem = { lib, stdenv, ... }: + lib.meta.availableOn stdenv.hostPlatform; - /* Import each nix file in a directory as attrs. Attr name is file name with - extension stripped. To allow use in an importable directory, default.nix is - skipped. To provide a file that will result in a "default" attr, name the - file "_default.nix". - */ importDir = path: genAttrs (pipe (readDir path) [ attrNames @@ -74,12 +78,6 @@ let (p: import (path + (if pathExists (path + "/_${p}.nix") then "/_${p}.nix" else "/${p}.nix"))); - /* Try to load a name from a directory. If a nix file by that name exits, - import it. If an importable directory with that name exists, import it. - Else, if a non-importable directory with that name exists, load the nix - files in that dir as an attrset. Returns null if the name could not be - loaded. If name is a list, tries all names in that list. - */ autoImport = dir: name: if isList name then findFirst (x: x != null) null (map (autoImport dir) name) @@ -92,428 +90,8 @@ let then importDir (dir + "/${name}") else null; - /* List of attrs that can be provided by a module. - */ - moduleAttrs = [ - "inputs" - "withOverlay" - "withOverlays" - "nixpkgsConfig" - "package" - "packages" - "devTools" - "shellHook" - "devShell" - "devShells" - "env" - "overlay" - "overlays" - "app" - "apps" - "checks" - "nixosModule" - "nixosModules" - "nixosConfigurations" - "homeModule" - "homeModules" - "homeConfigurations" - "template" - "templates" - "formatters" - ]; - - /* List of handled attrs in root module. Does not have optionally checked - attrs like name, description, or license, or attrs used by modules. - */ - rootAttrs = moduleAttrs ++ [ - "systems" - "perSystem" - "outputs" - "nixDir" - ]; - - /* Alternative names for autoloading root attrs. - */ - attrAliases = { - "nixosConfigurations" = [ "nixos" ]; - "homeConfigurations" = [ "home" ]; - # Lets `nix-shell` shell.nix be used automatically if no ./nix dir. - "devShell" = [ "shell" ]; - "nixpkgsConfig" = [ "nixpkgs/config" ]; - }; - - /* Generate an attrset by importing attrs from dir. Filters null values. - Alternative names from aliases are used. - */ - autoImportAttrs = dir: attrs: aliases: - filterAttrs (_: v: v != null) (genAttrs attrs - (attr: autoImport dir ([ attr ] ++ aliases.${attr} or [ ]))); - - /* Makes the parameter callable, if it isn't. This allows values that are not - always functions to be applied to parameters. - */ - ensureFn = v: if isFunction v then v else _: v; - - /* Takes a binary function and returns a binary function that operates on - functions that return the original parameter types. - - Type: liftFn2 :: (a -> b -> c) -> (d -> a) -> (d -> b) -> d -> c - */ - liftFn2 = fn: a: b: args: fn (a args) (b args); - - fnConcat = liftFn2 concat; - fnMergeAttrs = liftFn2 mergeAttrs; - - /* Takes a function which takes a list, and returns a binary function. - - Type: mkBinary :: ([a] -> b) -> a -> a -> b - */ - mkBinary = fn: a: b: fn [ a b ]; - - /* Merges attrsets of overlays, combining overlays with same name. - */ - mergeOverlayAttrs = mkBinary (zipAttrsWith (_: composeManyExtensions)); - - fnConcatScripts = liftFn2 (mkBinary (concatStringsSep "\n")); - - mergeModules = a: b: mapAttrs (n: v: v a.${n} b.${n}) { - inputs = mergeAttrs; - withOverlays = concat; - nixpkgsConfig = mergeAttrs; - packages = mergeAttrs; - devTools = fnConcat; - shellHook = fnConcatScripts; - devShells = fnMergeAttrs; - env = fnMergeAttrs; - overlays = mergeOverlayAttrs; - apps = fnMergeAttrs; - checks = fnMergeAttrs; - nixosModules = mergeAttrs; - nixosConfigurations = mergeAttrs; - homeModules = mergeAttrs; - homeConfigurations = mergeAttrs; - templates = mergeAttrs; - formatters = fnMergeAttrs; - }; - - /* Calls f with required arguments from args. If the function does not have - named arguments, just passes it args instead of nothing like callPackage - does. If f is not a function, returns f. - */ - callFn = args: f: - let - f' = ensureFn f; - in - if functionArgs f' == { } then f' args - else f' (intersectAttrs (functionArgs f) args); - - /* Ensures x is called with only it's required parameters. - */ - filterArgs = x: args: callFn args x; - - /* If arg is importable, imports it, else returns arg as is. - */ - tryImport = x: if (isPath x) || (isString x) then import x else x; - - /* Like callFn, but intended for functions that return derivations. Uses - callPackage so will make result overridable. Trys importing the value if a - path. - */ - callPkg = args: f: - let - f' = ensureFn (tryImport f); - in - if functionArgs f' == { } then f' args - else (args.callPackage or (callPackageWith args)) f' { }; - - callPkgs = pkgs: mapAttrs (_: callPkg pkgs); - - /* Gets the name for the default package using value set in root module or - derivation attrs. - */ - defaultPkgName = root: pkg: root.name or pkg.pname or (parseDrvName pkg).name; - - /* Merges elements of a list of sets recursively. Each element can optionally - be a function that takes the merged previous elements. - */ - recUpdateSets = foldl (acc: x: recursiveUpdate acc ((ensureFn x) acc)) { }; - - isApp = x: (x ? type) && (x.type == "app") && (x ? program); - - /* Turns app into an app attribute, if it is not already. Passes it pkgs if it - is a function. - */ - mkApp = pkgs: app: - let - app' = callFn pkgs app; - in - if isApp app' then app' - else { type = "app"; program = "${app'}"; }; - - /* Makes cmd into a derivation for a flake's checks output. If it is not - already a derivation, makes one that runs cmd on the flake source and - depends on its success. Passes cmd pkgs if it is its a function. - */ - mkCheck = pkgs: src: name: cmd: - let - cmd' = callFn pkgs cmd; - in - if pkgs.lib.isDerivation cmd' then cmd' else - pkgs.runCommand "check-${name}" { } '' - cp --no-preserve=mode -r ${src} src - cd src - ${cmd'} - touch $out - ''; - - /* Takes a packages set and a package and returns true if the package is - supported on the system for that packages set. If unknown, returns true. - */ - supportedSystem = { lib, stdenv, ... }: pkg: - if pkg ? meta.platforms - then lib.meta.availableOn stdenv.hostPlatform pkg - else true; - - systems = rec { - # Linux systems with binary caches. - linuxDefault = [ - "x86_64-linux" - "aarch64-linux" - ]; - # Linux systems supported as a host platform. - linuxAll = linuxDefault ++ [ - "armv6l-linux" - "armv7l-linux" - "i686-linux" - ]; - }; - - /* Creates flake outputs; takes the path of the flake and the root module. - */ - mkFlake = src: root: - let - # These are passed to modules and non-system-dependent module attrs. - nonSysArgs = exports // { - args = nonSysArgs; - flakelite = exports; - root = root'; - inherit src; - inherit (merged) inputs; - inherit (merged.inputs.nixpkgs) lib; - }; - - applyNonSysArgs = callFn nonSysArgs; - - moduleAttrDefaults = { - inputs = { }; - withOverlays = [ ]; - nixpkgsConfig = { }; - packages = { }; - devTools = _: [ ]; - shellHook = _: ""; - devShells = _: { }; - env = _: { }; - overlays = { }; - apps = _: { }; - checks = _: { }; - nixosModules = { }; - nixosConfigurations = { }; - homeModules = { }; - homeConfigurations = { }; - templates = { }; - formatters = _: { }; - }; - - normalizeModule = module: - let - module' = moduleAttrDefaults // module; - in - module' // { - withOverlays = (applyNonSysArgs module'.withOverlays) - ++ optional (module' ? withOverlay) module'.withOverlay; - nixpkgsConfig = applyNonSysArgs module'.nixpkgsConfig; - packages = (applyNonSysArgs module'.packages) - // optionalAttrs (module' ? package) { - default = module'.package; - }; - devTools = filterArgs module'.devTools; - shellHook = filterArgs module'.shellHook; - devShells = fnMergeAttrs (filterArgs module'.devShells) - (_: optionalAttrs (module' ? devShell) { - default = module'.devShell; - }); - env = filterArgs module'.env; - overlays = (applyNonSysArgs module'.overlays) - // optionalAttrs (module' ? overlay) { - default = module'.overlay; - }; - apps = fnMergeAttrs (filterArgs module'.apps) - (_: optionalAttrs (module' ? app) { - default = module'.app; - }); - checks = filterArgs module'.checks; - nixosModules = (applyNonSysArgs module'.nixosModules) - // optionalAttrs (module' ? nixosModule) { - default = module'.nixosModule; - }; - nixosConfigurations = applyNonSysArgs module'.nixosConfigurations; - homeModules = (applyNonSysArgs module'.homeModules) - // optionalAttrs (module' ? homeModule) { - default = module'.homeModule; - }; - homeConfigurations = applyNonSysArgs module'.homeConfigurations; - templates = (applyNonSysArgs module'.templates) - // optionalAttrs (module' ? template) { - default = module'.template; - }; - formatters = filterArgs module'.formatters; - }; - - # Root module with autoloads, normalization, and additional attrs. - root' = - let - appliedRoot = applyNonSysArgs root; - nixDir = appliedRoot.nixDir or (src + /nix); - fullRoot = (autoImportAttrs nixDir rootAttrs attrAliases) - // appliedRoot; - in - normalizeModule fullRoot // { - modules = fullRoot.modules or - (pipe (removeAttrs root'.inputs [ "self" ]) [ - (filterAttrs (_: v: v ? flakeliteModule)) - (mapAttrsToList (_: v: v.flakeliteModule)) - ]); - systems = applyNonSysArgs (fullRoot.systems or systems.linuxDefault); - perSystem = filterArgs (fullRoot.perSystem or { }); - outputs = applyNonSysArgs (fullRoot.outputs or { }); - inherit nixDir; - }; - - modules = [ baseModule ] ++ builtinModules ++ root'.modules; - - # Merge result of all the modules. - merged = foldl mergeModules moduleAttrDefaults - ((map (m: normalizeModule (applyNonSysArgs m)) modules) - ++ [ root' ]); - - # Returns package set for a system. - pkgsFor = system: import merged.inputs.nixpkgs { - inherit system; - config = merged.nixpkgsConfig; - overlays = merged.withOverlays ++ [ - (final: _: callPkgs final merged.packages) - ]; - }; - - # Attrset mapping systems to corresponding package sets. - systemPkgs = listToAttrs (map - (system: nameValuePair system (pkgsFor system)) - root'.systems); - - # Calls fn for each supported system. Fn should return an attrset whose - # attrs normally would have values in system attrs. Merges results into - # attrset with system attrs. - eachSystem = fn: foldAttrs mergeAttrs { } (map - (system: mapAttrs - (_: v: { ${system} = v; }) - (fn systemPkgs.${system})) - root'.systems); - - # Replaces the "default" attr in set with the default package name. - replaceDefault = set: - if set ? default - then (removeAttrs set [ "default" ]) // - { ${defaultPkgName root' set.default} = set.default; } - else set; - in - recUpdateSets [ - (optionalAttrs (merged.packages != { }) ({ - # Packages in overlay depend on withOverlays which are not in pkg set. - overlays.default = final: _: callPkgs - (final.appendOverlays merged.withOverlays) - (replaceDefault merged.packages); - } // eachSystem (pkgs: rec { - # Packages are generated in overlay on system pkgs; grab from there. - packages = filterAttrs (_: supportedSystem pkgs) - (intersectAttrs merged.packages pkgs); - checks = mapAttrs' (n: nameValuePair ("packages-" + n)) packages; - }))) - (prev: optionalAttrs (merged.overlays != { }) ({ - overlays = mergeOverlayAttrs (prev.overlays or { }) merged.overlays; - })) - (eachSystem ({ pkgs, lib, ... }: - optionalAttrs (merged.formatters pkgs != { }) rec { - formatter = pkgs.writeShellScriptBin "formatter" '' - PATH=${lib.makeBinPath (merged.devTools pkgs)} - for f in "$@"; do - if [ -d "$f" ]; then - ${pkgs.fd}/bin/fd "$f" -Htf -x "$0" - else - case "$(${pkgs.coreutils}/bin/basename "$f")" in - ${toString (mapAttrsToList - (n: v: "${n}) ${callFn pkgs v} \"$f\";;") - (merged.formatters pkgs))} - esac - fi - done &>/dev/null - ''; - checks.formatting = mkCheck pkgs src "formatting" '' - ${lib.getExe formatter} . - ${pkgs.diffutils}/bin/diff -qr ${src} . |\ - sed 's/Files .* and \(.*\) differ/File \1 not formatted/g' - ''; - })) - (eachSystem (pkgs: - let - checks = mapAttrs (mkCheck pkgs src) (merged.checks pkgs); - in - optionalAttrs (checks != { }) { inherit checks; })) - (eachSystem (pkgs: - let - apps = mapAttrs (_: mkApp pkgs) (merged.apps pkgs); - in - optionalAttrs (apps != { }) { inherit apps; })) - (optionalAttrs (merged.nixosModules != { }) { - inherit (merged) nixosModules; - }) - (optionalAttrs (merged.nixosConfigurations != { }) { - inherit (merged) nixosConfigurations; - checks = recUpdateSets (mapAttrsToList - (n: v: { - ${v.config.nixpkgs.system}."nixos-${n}" = - v.config.system.build.toplevel; - }) - merged.nixosConfigurations); - }) - (optionalAttrs (merged.homeModules != { }) { - inherit (merged) homeModules; - }) - (optionalAttrs (merged.homeConfigurations != { }) { - inherit (merged) homeConfigurations; - checks = recUpdateSets (mapAttrsToList - (n: v: { - ${v.config.nixpkgs.system}."home-${n}" = v.activationPackage; - }) - merged.homeConfigurations); - }) - (optionalAttrs (merged.templates != { }) { - inherit (merged) templates; - }) - (prev: eachSystem ({ pkgs, system, mkShell, ... }: { - devShells = { - default = mkShell (merged.env pkgs // { - inputsFrom = optional (prev ? packages.${system}.default) - prev.packages.${system}.default; - packages = merged.devTools pkgs; - shellHook = merged.shellHook pkgs; - }); - } // (callPkgs pkgs (merged.devShells pkgs)); - })) - (eachSystem root'.perSystem) - (_: root'.outputs) - ]; in { - lib = exports; + lib = flakelite; __functor = _: mkFlake; } diff --git a/flake.lock b/flake.lock index fdd830d..4b72b38 100644 --- a/flake.lock +++ b/flake.lock @@ -2,16 +2,16 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1680665430, - "narHash": "sha256-MTVhTukwza1Jlq2gECITZPFnhROmylP2uv3O3cSqQCE=", + "lastModified": 1692447944, + "narHash": "sha256-fkJGNjEmTPvqBs215EQU4r9ivecV5Qge5cF/QDLVn3U=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "5233fd2ba76a3accb5aaa999c00509a11fd0793c", + "rev": "d680ded26da5cf104dd2735a51e88d2d8f487b4d", "type": "github" }, "original": { "id": "nixpkgs", - "ref": "nixos-22.11", + "ref": "nixos-unstable", "type": "indirect" } }, diff --git a/flake.nix b/flake.nix index d58dcef..5934dad 100644 --- a/flake.nix +++ b/flake.nix @@ -3,13 +3,10 @@ # SPDX-License-Identifier: MIT { - inputs.nixpkgs.url = "nixpkgs/nixos-22.11"; + inputs.nixpkgs.url = "nixpkgs/nixos-unstable"; outputs = inputs: let flakelite = import ./. inputs; in - flakelite ./. { - nixDir = ./.; - outputs = _: flakelite; - }; + flakelite ./. { outputs = flakelite; }; } diff --git a/packages/pre-commit-hook.nix b/packages/pre-commit-hook.nix deleted file mode 100644 index de05ced..0000000 --- a/packages/pre-commit-hook.nix +++ /dev/null @@ -1,15 +0,0 @@ -{ writeShellApplication -, coreutils -, nix -, git -, gnutar -}: -writeShellApplication { - name = "pre-commit"; - runtimeInputs = [ coreutils nix git gnutar ]; - text = '' - TREE=$(mktemp -d) - git archive "$(git write-tree)" | tar -xC "$TREE" - nix flake check "$TREE" - ''; -}