diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..17d3448 --- /dev/null +++ b/.envrc @@ -0,0 +1,8 @@ +eval "$(lorri direnv)" + +# Use system PKI +unset SSL_CERT_FILE +unset NIX_SSL_CERT_FILE + +# Install pre-commit hooks +eval "$shellHook" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e2db727 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ + +# Nix package written by the pre-commit integration +.pre-commit-hooks diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..b84b1c7 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,16 @@ +# DO NOT MODIFY +# This file was generated by nix-pre-commit-hooks +{ + "exclude": "(nix/sources.nix$)", + "repos": [ + { + "hooks": [ + { + "id": "nixpkgs-fmt" + } + ], + "repo": ".pre-commit-hooks/", + "rev": "master" + } + ] +} diff --git a/default.nix b/default.nix index 216ddef..2b66a9a 100644 --- a/default.nix +++ b/default.nix @@ -1,5 +1,4 @@ -{ lib ? - import ((import ./nix/sources.nix).nixpkgs + "/lib") +{ lib ? import ((import ./nix/sources.nix).nixpkgs + "/lib") , ... }: @@ -16,6 +15,8 @@ let ./modules/nixpkgs.nix ./modules/shell.nix ./modules/niv.nix + ./modules/pre-commit.nix + ./modules/activation.nix ]; in diff --git a/modules/activation.nix b/modules/activation.nix new file mode 100644 index 0000000..332c7ce --- /dev/null +++ b/modules/activation.nix @@ -0,0 +1,69 @@ +{ config, lib, ... }: + +let + + inherit (lib) + concatStringsSep + hasPrefix + literalExample + mkIf + mkOption + mkOptionType + mkOrder + types + ; + + cfg = config.activation; + + isOutsideStore = p: !(hasPrefix builtins.storeDir (toString p)); + +in +{ + options = { + + activation.hooks = mkOption { + type = types.listOf types.str; + description = '' + bash snippets to run on activation. + + This is quite distinct from a shell hook: + - the working directory is always the project root. + - variables are not propagated to the shell. + - activation hooks may be run separately or before most shell.hooks. + ''; + default = []; + }; + + activation.enableShellHook = mkOption { + type = types.bool; + description = '' + Whether to run the activation hooks whenever the project shell is opened. + ''; + default = true; + }; + + }; + + config = + mkIf cfg.enableShellHook { + + shell.extraAttrs.activationHook = + concatStringsSep "\n" cfg.hooks; + + shell.hooks = mkIf (isOutsideStore config.root) ( + mkOrder 300 [ + '' + # lorri runs the shellHook in the sandbox first, so skip if that's happening. + if ! [[ "$name" =~ lorri-.*-hack ]]; then + ( + echo 1>&2 project.nix: activating in ${lib.escapeShellArg config.root} + cd ${lib.escapeShellArg config.root} + runHook activationHook + ) + fi + '' + ] + ); + }; + +} diff --git a/modules/niv.nix b/modules/niv.nix index fe21c46..d5c2b90 100644 --- a/modules/niv.nix +++ b/modules/niv.nix @@ -1,4 +1,4 @@ -{ config, lib, pkgs, ... }: +{ config, lib, options, pkgs, ... }: let inherit (lib) mkIf mkOption types; @@ -38,9 +38,13 @@ in }; - config = mkIf cfg.enable { - shell.packages = [ - cfg.package - ]; - }; -} \ No newline at end of file + config = mkIf cfg.enable ( + { + shell.packages = [ + cfg.package + ]; + } // lib.optionalAttrs (options ? pre-commit.excludes) { + pre-commit.excludes = [ "nix/sources.nix$" ]; + } + ); +} diff --git a/modules/pre-commit.nix b/modules/pre-commit.nix new file mode 100644 index 0000000..1f2d64d --- /dev/null +++ b/modules/pre-commit.nix @@ -0,0 +1,38 @@ +{ config, lib, pkgs, ... }: + +let + + inherit (lib) + mkIf + mkOption + types + ; + + cfg = config.pre-commit; + +in +{ + options.pre-commit = { + + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable pre-commit integration. + + https://pre-commit.com/ + ''; + }; + + }; + + config = mkIf cfg.enable { + + shell.packages = [ cfg.package ]; + + activation.hooks = [ + config.pre-commit.installationScript + ]; + + }; +} diff --git a/modules/root.nix b/modules/root.nix index b97faab..2e7867e 100644 --- a/modules/root.nix +++ b/modules/root.nix @@ -13,15 +13,10 @@ in The root of the project. This must be defined as ../. in nix/project.nix. - - When reading the option, take care not to accidentally add it to - the store in its entirety. In particular, use - - config.root + "/somepath" - - and avoid interpolation, toString etc until you want to add paths - to the store. ''; + + # Strings are harder to accidentally add to the store. + apply = toString; }; }; -} \ No newline at end of file +} diff --git a/modules/shell.nix b/modules/shell.nix index 0e5648e..9b2c43a 100644 --- a/modules/shell.nix +++ b/modules/shell.nix @@ -19,9 +19,30 @@ in ''; }; - # TODO shellHook hooks + hooks = mkOption { + type = types.listOf types.str; + description = '' + bash snippets to run when entering the project's nix-shell. + ''; + default = []; + example = [ + '' + if ! type git >/dev/null; then + echo 1>&2 "git command not found! Please install git on your system or user profile"; + fi + '' + ]; + }; - # TODO environment variables + # TODO: can we specify merge functions in an extensible way? + extraAttrs = mkOption { + type = types.attrsOf types.str; + description = '' + Extra variables to set in the project's nix-shell. + ''; + default = {}; + example = { LANG = "en_US.UTF-8"; }; + }; shell = mkOption { type = types.package; @@ -32,8 +53,11 @@ in }; config = { - shell.shell = pkgs.mkShell { - nativeBuildInputs = cfg.packages; - }; + shell.shell = pkgs.mkShell ( + cfg.extraAttrs // { + nativeBuildInputs = cfg.packages; + shellHook = lib.concatStringsSep "\n" cfg.hooks; + } + ); }; } diff --git a/nix/ci.nix b/nix/ci.nix new file mode 100644 index 0000000..ffbfc10 --- /dev/null +++ b/nix/ci.nix @@ -0,0 +1,7 @@ +{ projectNix ? import ../default.nix {} +, project ? projectNix.evalProject { modules = [ ./project.nix ]; } +}: + +{ + pre-commit = project.config.pre-commit.run; +} diff --git a/nix/project.nix b/nix/project.nix index ddf53b8..30e4a78 100644 --- a/nix/project.nix +++ b/nix/project.nix @@ -1,4 +1,21 @@ -{ +/* + + This is a _project.nix configuration_ + for project.nix _itself_. + + */ +{ config, lib, pkgs, ... }: { + + imports = [ + ((import ./sources.nix).nix-pre-commit-hooks + "/nix/project-module.nix") + ]; + root = ../.; pinning.niv.enable = true; + pre-commit.enable = true; + pre-commit.tools.nixpkgs-fmt = lib.mkForce pkgs.nixpkgs-fmt; + pre-commit.hooks.nixpkgs-fmt.enable = true; + + # TODO: upstream the command line change, remove this + pre-commit.hooks.nixpkgs-fmt.entry = lib.mkForce "${config.pre-commit.tools.nixpkgs-fmt}/bin/nixpkgs-fmt"; } diff --git a/nix/sources.json b/nix/sources.json index 80981fe..742cdb1 100644 --- a/nix/sources.json +++ b/nix/sources.json @@ -1,4 +1,16 @@ { + "gitignore": { + "branch": "master", + "description": "Nix function for filtering local git sources", + "homepage": "", + "owner": "hercules-ci", + "repo": "gitignore", + "rev": "f9e996052b5af4032fe6150bba4a6fe4f7b9d698", + "sha256": "0jrh5ghisaqdd0vldbywags20m2cxpkbbk5jjjmwaw0gr8nhsafv", + "type": "tarball", + "url": "https://github.com/hercules-ci/gitignore/archive/f9e996052b5af4032fe6150bba4a6fe4f7b9d698.tar.gz", + "url_template": "https://github.com///archive/.tar.gz" + }, "niv": { "branch": "master", "description": "Easy dependency management for Nix projects", @@ -11,6 +23,18 @@ "url": "https://github.com/nmattia/niv/archive/efce82e4ba77a84835f9febefe3b2fe52a8c7ede.tar.gz", "url_template": "https://github.com///archive/.tar.gz" }, + "nix-pre-commit-hooks": { + "branch": "master", + "description": "Seamless integration of https://pre-commit.com git hooks with Nix.", + "homepage": "", + "owner": "hercules-ci", + "repo": "nix-pre-commit-hooks", + "rev": "1f5b70278150e17783c67ea8648d4295f29fb1b9", + "sha256": "1my4sz698iq7i0m68n3xz3vkynvq6crw17annj7hgngvhpq4ysrb", + "type": "tarball", + "url": "https://github.com/hercules-ci/nix-pre-commit-hooks/archive/1f5b70278150e17783c67ea8648d4295f29fb1b9.tar.gz", + "url_template": "https://github.com///archive/.tar.gz" + }, "nixpkgs": { "branch": "nixos-19.09", "description": "A read-only mirror of NixOS/nixpkgs tracking the released channels. Send issues and PRs to", diff --git a/shell.nix b/shell.nix index f758b22..9f885fd 100644 --- a/shell.nix +++ b/shell.nix @@ -1,7 +1,5 @@ -{ projectNix ? - import ./default.nix {} -, project ? - projectNix.evalProject { modules = [ ./nix/project.nix ]; } +{ projectNix ? import ./default.nix {} +, project ? projectNix.evalProject { modules = [ ./nix/project.nix ]; } }: project.config.shell.shell