From dc3c678835ffe27293e98060f182a44ea8baebc2 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 26 Sep 2019 00:19:37 +0200 Subject: [PATCH 1/7] Use the module system, delegate activation to project.nix core New - Selectively enable tools (#2) - Make choice of tool versions overridable - User-definable hooks - Input validation at the Nix level Fixes - Stop installing files all over the place when doing nix-shell ../some-project to borrow a tool - Lorri support Cleanup - Get rid of installation code --- .pre-commit-config.yaml | 24 ++- modules/hooks.nix | 83 +++++++++++ modules/pre-commit.nix | 319 ++++++++++++++++++++++++++++++++++++++++ nix/packages.nix | 12 +- nix/run.nix | 142 +++++------------- nix/sources.json | 24 +++ 6 files changed, 488 insertions(+), 116 deletions(-) create mode 100644 modules/hooks.nix create mode 100644 modules/pre-commit.nix diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 21bba7ec..e1f54097 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,18 @@ -repos: - - repo: .pre-commit-hooks/ - rev: master - hooks: - - id: shellcheck - - id: canonix +# DO NOT MODIFY +# This file was generated by project.nix +{ + "repos": [ + { + "hooks": [ + { + "id": "canonix" + }, + { + "id": "shellcheck" + } + ], + "repo": ".pre-commit-hooks/", + "rev": "master" + } + ] +} diff --git a/modules/hooks.nix b/modules/hooks.nix new file mode 100644 index 00000000..f90786bb --- /dev/null +++ b/modules/hooks.nix @@ -0,0 +1,83 @@ +{ config, lib, pkgs, ... }: +let + inherit (config.pre-commit) tools; +in { + config.pre-commit.hooks = + { + hlint = + { + name = "hlint"; + description = + "HLint gives suggestions on how to improve your source code."; + entry = "${tools.hlint}/bin/hlint"; + files = "\\.l?hs$"; + }; + ormolu = + { + name = "ormolu"; + description = "Haskell code prettifier."; + entry = "${tools.ormolu}/bin/ormolu --mode inplace"; + files = "\\.l?hs$"; + }; + hindent = + { + name = "hindent"; + description = "Haskell code prettifier."; + entry = "${tools.hindent}/bin/hindent"; + files = "\\.l?hs$"; + }; + cabal-fmt = + { + name = "cabal-fmt"; + description = "Format Cabal files"; + entry = "${tools.cabal-fmt}/bin/cabal-fmt --inplace"; + files = "\\.cabal$"; + }; + canonix = + { + name = "canonix"; + description = "Nix code prettifier."; + entry = "${tools.canonix}/bin/canonix"; + files = "\\.nix$"; + }; + nixfmt = + { + name = "nixfmt"; + description = "Nix code prettifier."; + entry = "${tools.nixfmt}/bin/nixfmt"; + files = "\\.nix$"; + }; + nixpkgs-fmt = + { + name = "nixpkgs-fmt"; + description = "Nix code prettifier."; + entry = "${tools.nixpkgs-fmt}/bin/nixpkgs-fmt -i"; + files = "\\.nix$"; + }; + elm-format = + { + name = "elm-format"; + description = "Format Elm files"; + entry = + "${tools.elm-format}/bin/elm-format --yes --elm-version=0.19"; + files = "\\.elm$"; + }; + shellcheck = + { + name = "shellcheck"; + description = "Format shell files"; + types = + [ + "bash" + ]; + entry = "${tools.shellcheck}/bin/shellcheck"; + }; + terraform-format = + { + name = "terraform-format"; + description = "Format terraform (.tf) files"; + entry = "${tools.terraform-fmt}/bin/terraform-fmt"; + files = "\\.tf$"; + }; + }; +} diff --git a/modules/pre-commit.nix b/modules/pre-commit.nix new file mode 100644 index 00000000..b8df26c8 --- /dev/null +++ b/modules/pre-commit.nix @@ -0,0 +1,319 @@ +{ config, lib, pkgs, ... }: + +let + + inherit (lib) + attrNames + concatStringsSep + filterAttrs + literalExample + mapAttrsToList + mkIf + mkOption + types + ; + + inherit (pkgs) runCommand writeText git; + + cfg = config.pre-commit; + + hookType = + types.submodule ( + { config, name, ... }: + { + options = + { + enable = + mkOption { + type = types.bool; + description = "Whether to enable this pre-commit hook."; + default = false; + }; + raw = + mkOption { + type = types.attrsOf types.unspecified; + description = + '' + Raw fields of a pre-commit hook. This is mostly for internal use but + exposed in case you need to work around something. + + Default: taken from the other hook options. + ''; + }; + name = + mkOption { + type = types.str; + default = name; + defaultText = literalExample "internal name, same as id"; + description = + '' + The name of the hook - shown during hook execution. + ''; + }; + entry = + mkOption { + type = types.str; + description = + '' + The entry point - the executable to run. entry can also contain arguments that will not be overridden such as entry: autopep8 -i. + ''; + }; + language = + mkOption { + type = types.str; + description = + '' + The language of the hook - tells pre-commit how to install the hook. + ''; + default = "system"; + }; + files = + mkOption { + type = types.str; + description = + '' + The pattern of files to run on. + ''; + default = ""; + }; + types = + mkOption { + type = types.listOf types.str; + description = + '' + List of file types to run on. See Filtering files with types (https://pre-commit.com/#plugins). + ''; + default = [ "file" ]; + }; + description = + mkOption { + type = types.str; + description = + '' + Description of the hook. used for metadata purposes only. + ''; + default = ""; + }; + excludes = + mkOption { + type = types.listOf types.str; + description = + '' + Exclude files that were matched by these patterns. + ''; + default = []; + }; + }; + config = + { + raw = + { + inherit (config) name entry language files types; + id = name; + exclude = + if config.excludes == [] then "^$" else + "(${concatStringsSep "|" config.excludes})"; + }; + }; + } + ); + + enabledHooks = filterAttrs ( id: value: value.enable ) cfg.hooks; + processedHooks = + mapAttrsToList ( id: value: value.raw // { inherit id; } ) enabledHooks; + + precommitConfig = + { + repos = + [ + { + repo = ".pre-commit-hooks/"; + rev = "master"; + hooks = + mapAttrsToList ( id: _value: { inherit id; } ) enabledHooks; + } + ]; + }; + + hooksFile = + writeText "pre-commit-hooks.json" ( builtins.toJSON processedHooks ); + configFile = + writeText "pre-commit-config.json" ( builtins.toJSON precommitConfig ); + + hooks = + runCommand "pre-commit-hooks-dir" { buildInputs = [ git ]; } '' + HOME=$PWD + mkdir -p $out + ln -s ${hooksFile} $out/.pre-commit-hooks.yaml + cd $out + git config --global user.email "you@example.com" + git config --global user.name "Your Name" + git init + git add . + git commit -m "init" + ''; + + run = + runCommand "pre-commit-run" { buildInputs = [ git ]; } '' + set +e + HOME=$PWD + cp --no-preserve=mode -R ${cfg.rootSrc} src + unlink src/.pre-commit-hooks || true + ln -fs ${hooks} src/.pre-commit-hooks + cd src + rm -rf src/.git + git init + git add . + git config --global user.email "you@example.com" + git config --global user.name "Your Name" + git commit -m "init" + echo "Running: $ pre-commit run --all-files" + ${cfg.package}/bin/pre-commit run --all-files + exitcode=$? + git --no-pager diff --color + touch $out + [ $? -eq 0 ] && exit $exitcode + ''; + + srcStr = toString ( config.root.origSrc or config.root ); + + # TODO: provide a default pin that the user may override + inherit (import (import ../nix/sources.nix).gitignore { inherit lib; }) + gitignoreSource + ; +in { + options.pre-commit = + { + + enable = + mkOption { + type = types.bool; + default = false; + description = + '' + Whether to enable pre-commit integration. + + https://pre-commit.com/ + ''; + }; + + enableAutoInstall = + mkOption { + type = types.bool; + default = true; + description = + '' + Whether to auto install pre-commit when invoking nix-shell in the + project root. + ''; + }; + + package = + mkOption { + type = types.package; + description = + '' + The pre-commit package to use. + ''; + default = pkgs.pre-commit; + defaultText = + literalExample '' + pkgs.pre-commit + ''; + }; + + tools = + mkOption { + type = types.attrsOf types.package; + description = + '' + Tool set from which nix-pre-commit will pick binaries. + + nix-pre-commit comes with its own set of packages for this purpose. + ''; + }; + + hooks = + mkOption { + type = types.attrsOf hookType; + description = + '' + The hook definitions. + ''; + default = {}; + }; + + run = + mkOption { + type = types.package; + description = + '' + A derivation that tests whether the pre-commit hooks run cleanly on + the entire project. + ''; + readOnly = true; + default = run; + }; + + rootSrc = + mkOption { + type = types.package; + description = + '' + The source of the project to be checked. + ''; + defaultText = literalExample ''gitignoreSource config.root''; + default = gitignoreSource config.root; + }; + + }; + + config = + mkIf cfg.enable { + + shell.packages = [ cfg.package ]; + + activation.hooks = + mkIf cfg.enableAutoInstall [ + '' + export PATH=$PATH:${cfg.package}/bin + if ! type -t git >/dev/null; then + # This happens in pure shells, including lorri + echo 1>&2 "WARNING: nix-pre-commit-hooks: git command not found; skipping installation." + else + ( + # We use srcStr to protect against installing pre-commit hooks + # in the wrong places such as for example ./. when invoking + # nix-shell ../../other-project/shell.nix + cd ${lib.escapeShellArg srcStr} && { + # Avoid filesystem churn. We may be watched! + # This prevents lorri from looping after every interactive shell command. + if readlink .pre-commit-hooks >/dev/null \ + && [[ $(readlink .pre-commit-hooks) == ${hooks} ]]; then + echo 1>&2 "nix-pre-commit-hooks: hooks up to date" + else + echo 1>&2 "nix-pre-commit-hooks: updating" ${lib.escapeShellArg srcStr} + + [ -L .pre-commit-hooks ] && unlink .pre-commit-hooks + ln -s ${hooks} .pre-commit-hooks + + # This can't be a symlink because its path is not constant, + # thus can not be committed and is invisible to pre-commit. + unlink .pre-commit-config.yaml + { echo '# DO NOT MODIFY'; + echo '# This file was generated by project.nix'; + ${pkgs.jq}/bin/jq . <${configFile} + } >.pre-commit-config.yaml + + pre-commit install + # this is needed as the hook repo configuration is cached + pre-commit clean + fi + } + ) + fi + '' + ]; + + }; +} diff --git a/nix/packages.nix b/nix/packages.nix index 45f18ee2..a939a55b 100644 --- a/nix/packages.nix +++ b/nix/packages.nix @@ -14,6 +14,14 @@ in tools // rec { inherit niv; inherit (gitAndTools) pre-commit; - run = import ./run.nix { inherit tools pre-commit runCommand writeText writeScript git; }; - pre-commit-check = run { src = ../.; }; + run = callPackage ./run.nix { inherit tools; }; + + # A pre-commit-check for nix-pre-commit itself + pre-commit-check = run { + src = ../.; + hooks = { + shellcheck.enable = true; + canonix.enable = true; + }; + }; } diff --git a/nix/run.nix b/nix/run.nix index 3bbe2df2..7e32f048 100644 --- a/nix/run.nix +++ b/nix/run.nix @@ -1,116 +1,42 @@ -{ tools, pre-commit, git, runCommand, writeText, writeScript }: +{ pkgs, tools, pre-commit, git, runCommand, writeText, writeScript, lib }: { src -, hooks ? null +, hooks ? {} }: let - hooksYaml = - writeText "pre-commit-hooks" '' - - id: hlint - name: hlint - description: HLint gives suggestions on how to improve your source code. - entry: ${tools.hlint}/bin/hlint - language: system - files: '\.l?hs$' - - id: ormolu - name: ormolu - description: Haskell code prettifier. - entry: ${tools.ormolu}/bin/ormolu --mode inplace - language: script - files: '\.l?hs$' - - id: hindent - name: hindent - description: Haskell code prettifier. - entry: ${tools.hindent}/bin/hindent - language: script - files: '\.l?hs$' - - id: cabal-fmt - name: cabal-fmt - description: Format Cabal files - entry: ${tools.cabal-fmt}/bin/cabal-fmt --inplace - language: script - files: '\.cabal$' - - id: canonix - name: canonix - description: Nix code prettifier. - entry: ${tools.canonix}/bin/canonix - language: system - files: '\.nix$' - - id: nixfmt - name: nixfmt - description: Nix code prettifier. - entry: ${tools.nixfmt}/bin/nixfmt - language: script - files: '\.nix$' - - id: nixpkgs-fmt - name: nixpkgs-fmt - description: Nix code prettifier. - entry: ${tools.nixpkgs-fmt}/bin/nixpkgs-fmt -i - language: script - files: '\.nix$' - - id: elm-format - name: elm-format - description: Format Elm files - entry: ${tools.elm-format}/bin/elm-format --yes --elm-version=0.19 - language: script - files: \.elm$ - - id: shellcheck - name: shellcheck - description: Format shell files - types: [bash] - entry: ${tools.shellcheck}/bin/shellcheck - language: system - - id: terraform-format - name: terraform-format - description: Format terraform (.tf) files - entry: ${tools.terraform-fmt}/bin/terraform-fmt - files: '\.tf$' - language: script - ''; + sources = import ./sources.nix; - hooks = - runCommand "pre-commit-hooks-dir" { buildInputs = [ git ]; } '' - HOME=$PWD - mkdir -p $out - ln -s ${hooksYaml} $out/.pre-commit-hooks.yaml - cd $out - git config --global user.email "you@example.com" - git config --global user.name "Your Name" - git init - git add . - git commit -m "init" - ''; + # TODO upstream this and add tests to support this use case + project-nix-core = + map ( f: sources."project.nix" + ("/" + f) ) [ + "modules/root.nix" + "modules/activation.nix" + "modules/shell.nix" + "modules/nixpkgs.nix" + ]; + + project = + lib.evalModules { + modules = + project-nix-core ++ [ + { + root = src; + nixpkgs.pkgs = pkgs; + pre-commit.tools = lib.mkDefault tools; + pre-commit.enable = true; + pre-commit.hooks = hooks; + } + ../modules/pre-commit.nix + ../modules/hooks.nix + ]; + }; + inherit (project.config.shell.shell) shellHook activationHook; - run = - runCommand "pre-commit-run" { buildInputs = [ git ]; } '' - set +e - HOME=$PWD - cp --no-preserve=mode -R ${src} src - unlink src/.pre-commit-hooks || true - ln -fs ${hooks} src/.pre-commit-hooks - cd src - rm -rf src/.git - git init - git add . - git config --global user.email "you@example.com" - git config --global user.name "Your Name" - git commit -m "init" - echo "Running: $ pre-commit run --all-files" - ${pre-commit}/bin/pre-commit run --all-files - exitcode=$? - git diff - touch $out - [ $? -eq 0 ] && exit $exitcode - ''; in - run // { - shellHook = '' - [ -L .pre-commit-hooks ] && unlink .pre-commit-hooks - ln -s ${hooks} .pre-commit-hooks - export PATH=$PATH:${pre-commit}/bin - pre-commit install - # this is needed as the hook repo configuration is cached - pre-commit clean - ''; -} + project.config.pre-commit.run // { + shellHook = '' + activationHook=${lib.escapeShellArg activationHook} + ${shellHook} + ''; + } diff --git a/nix/sources.json b/nix/sources.json index 2c99ae69..fd7c401c 100644 --- a/nix/sources.json +++ b/nix/sources.json @@ -23,6 +23,18 @@ "url": "https://github.com/hercules-ci/canonix/archive/a3369542aef56366d3432e01e3e73374a310ca4a.tar.gz", "url_template": "https://github.com///archive/.tar.gz" }, + "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" + }, "haskell.nix": { "branch": "master", "description": "Alternative Haskell Infrastructure for Nixpkgs", @@ -106,5 +118,17 @@ "type": "tarball", "url": "https://github.com/tweag/ormolu/archive/a7076c0f83e5c06ea9067b71171859fa2ba8afd9.tar.gz", "url_template": "https://github.com///archive/.tar.gz" + }, + "project.nix": { + "branch": "pre-commit", + "description": "A configuration manager for your projects", + "homepage": null, + "owner": "hercules-ci", + "repo": "project.nix", + "rev": "ec17ac79904a296f358961ffcff7a13948c2b92e", + "sha256": "1gkb9k995ly53d5sxn5hr4lyzh57sq25j3i2cw8zva92g8x69gb5", + "type": "tarball", + "url": "https://github.com/hercules-ci/project.nix/archive/ec17ac79904a296f358961ffcff7a13948c2b92e.tar.gz", + "url_template": "https://github.com///archive/.tar.gz" } } From 2739673f91fdc507d2f111ca8bed4b1ab51a60b6 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 26 Sep 2019 00:56:20 +0200 Subject: [PATCH 2/7] Update README.md for module system change --- README.md | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 32e8c003..f3d4a122 100644 --- a/README.md +++ b/README.md @@ -17,39 +17,32 @@ The goal is to manage these hooks with Nix and solve the following: # Installation & Usage -1. Create `.pre-commit-config.yaml` with hooks you want to run in your git repository: - ```yaml - repos: - - repo: .pre-commit-hooks/ - rev: master - hooks: - - id: ormolu - - id: shellcheck - - id: elm-format - ``` - -2. (optional) Use binary caches to avoid compilation: +1. (optional) Use binary caches to avoid compilation: ```bash $ nix-env -iA cachix -f https://cachix.org/api/v1/install $ cachix use hercules-ci ``` -3. Integrate hooks to be built as part of `default.nix`: +2. Integrate hooks to be built as part of `default.nix`: ```nix let - inherit (import (builtins.fetchTarball "https://github.com/hercules-ci/gitignore/tarball/master" {})) gitignoreSource; nix-pre-commit-hooks = import (builtins.fetchTarball "https://github.com/hercules-ci/nix-pre-commit-hooks/tarball/master"); in { pre-commit-check = nix-pre-commit-hooks.run { - src = gitignoreSource ./.; + src = ./.; + hooks = { + elm-format.enable = true; + ormolu.enable = true; + shellcheck.enable = true; + }; }; } ``` Run `$ nix-build -A pre-commit-check` to perform the checks as a Nix derivation. -2. Integrate hooks to prepare environment as part of `shell.nix`: +3. Integrate hooks to prepare environment as part of `shell.nix`: ```nix (import {}).mkShell { inherit ((import ./. {}).pre-commit-check) shellHook; From 669b2f3da6283a09d88b8b9c41b64f5fe8001d78 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 1 Oct 2019 23:21:03 +0200 Subject: [PATCH 3/7] Invert project.nix dependency, simplify installation script --- .pre-commit-config.yaml | 2 +- modules/pre-commit.nix | 80 ++++++++++++++++++++--------------------- nix/project-module.nix | 11 ++++++ nix/run.nix | 45 ++++++++++++----------- nix/sources.json | 12 ------- 5 files changed, 72 insertions(+), 78 deletions(-) create mode 100644 nix/project-module.nix diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e1f54097..93eb75a6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,5 +1,5 @@ # DO NOT MODIFY -# This file was generated by project.nix +# This file was generated by nix-pre-commit-hooks { "repos": [ { diff --git a/modules/pre-commit.nix b/modules/pre-commit.nix index b8df26c8..8a72caaa 100644 --- a/modules/pre-commit.nix +++ b/modules/pre-commit.nix @@ -175,8 +175,6 @@ let [ $? -eq 0 ] && exit $exitcode ''; - srcStr = toString ( config.root.origSrc or config.root ); - # TODO: provide a default pin that the user may override inherit (import (import ../nix/sources.nix).gitignore { inherit lib; }) gitignoreSource @@ -255,6 +253,16 @@ in { default = run; }; + installationScript = + mkOption { + type = types.str; + description = + '' + A bash snippet that installs nix-pre-commit in the current directory + ''; + readOnly = true; + }; + rootSrc = mkOption { type = types.package; @@ -269,51 +277,39 @@ in { }; config = - mkIf cfg.enable { - - shell.packages = [ cfg.package ]; + { - activation.hooks = - mkIf cfg.enableAutoInstall [ - '' - export PATH=$PATH:${cfg.package}/bin - if ! type -t git >/dev/null; then - # This happens in pure shells, including lorri - echo 1>&2 "WARNING: nix-pre-commit-hooks: git command not found; skipping installation." + pre-commit.installationScript = + '' + export PATH=$PATH:${cfg.package}/bin + if ! type -t git >/dev/null; then + # This happens in pure shells, including lorri + echo 1>&2 "WARNING: nix-pre-commit-hooks: git command not found; skipping installation." + else + # Avoid filesystem churn. We may be watched! + # This prevents lorri from looping after every interactive shell command. + if readlink .pre-commit-hooks >/dev/null \ + && [[ $(readlink .pre-commit-hooks) == ${hooks} ]]; then + echo 1>&2 "nix-pre-commit-hooks: hooks up to date" else - ( - # We use srcStr to protect against installing pre-commit hooks - # in the wrong places such as for example ./. when invoking - # nix-shell ../../other-project/shell.nix - cd ${lib.escapeShellArg srcStr} && { - # Avoid filesystem churn. We may be watched! - # This prevents lorri from looping after every interactive shell command. - if readlink .pre-commit-hooks >/dev/null \ - && [[ $(readlink .pre-commit-hooks) == ${hooks} ]]; then - echo 1>&2 "nix-pre-commit-hooks: hooks up to date" - else - echo 1>&2 "nix-pre-commit-hooks: updating" ${lib.escapeShellArg srcStr} + echo 1>&2 "nix-pre-commit-hooks: updating $PWD" - [ -L .pre-commit-hooks ] && unlink .pre-commit-hooks - ln -s ${hooks} .pre-commit-hooks + [ -L .pre-commit-hooks ] && unlink .pre-commit-hooks + ln -s ${hooks} .pre-commit-hooks - # This can't be a symlink because its path is not constant, - # thus can not be committed and is invisible to pre-commit. - unlink .pre-commit-config.yaml - { echo '# DO NOT MODIFY'; - echo '# This file was generated by project.nix'; - ${pkgs.jq}/bin/jq . <${configFile} - } >.pre-commit-config.yaml + # This can't be a symlink because its path is not constant, + # thus can not be committed and is invisible to pre-commit. + unlink .pre-commit-config.yaml + { echo '# DO NOT MODIFY'; + echo '# This file was generated by nix-pre-commit-hooks'; + ${pkgs.jq}/bin/jq . <${configFile} + } >.pre-commit-config.yaml - pre-commit install - # this is needed as the hook repo configuration is cached - pre-commit clean - fi - } - ) + pre-commit install + # this is needed as the hook repo configuration is cached + pre-commit clean fi - '' - ]; - + fi + ''; }; } diff --git a/nix/project-module.nix b/nix/project-module.nix new file mode 100644 index 00000000..4055367a --- /dev/null +++ b/nix/project-module.nix @@ -0,0 +1,11 @@ +/* + This module is picked up by project.nix but is also used internally + to find these imported modules. + */ +{ + imports = + [ + ../modules/pre-commit.nix + ../modules/hooks.nix + ]; +} diff --git a/nix/run.nix b/nix/run.nix index 7e32f048..3bc29860 100644 --- a/nix/run.nix +++ b/nix/run.nix @@ -7,36 +7,35 @@ let sources = import ./sources.nix; - # TODO upstream this and add tests to support this use case - project-nix-core = - map ( f: sources."project.nix" + ("/" + f) ) [ - "modules/root.nix" - "modules/activation.nix" - "modules/shell.nix" - "modules/nixpkgs.nix" - ]; - project = lib.evalModules { modules = - project-nix-core ++ [ + [ + ./project-module.nix { - root = src; - nixpkgs.pkgs = pkgs; - pre-commit.tools = lib.mkDefault tools; - pre-commit.enable = true; - pre-commit.hooks = hooks; + options = + { + root = + lib.mkOption { + description = "Internal option"; + default = src; + internal = true; + readOnly = true; + type = lib.types.unspecified; + }; + }; + config = + { + _module.args.pkgs = pkgs; + pre-commit.hooks = hooks; + pre-commit.tools = lib.mkDefault tools; + }; } - ../modules/pre-commit.nix - ../modules/hooks.nix - ]; + ]; }; - inherit (project.config.shell.shell) shellHook activationHook; + inherit (project.config.pre-commit) installationScript; in project.config.pre-commit.run // { - shellHook = '' - activationHook=${lib.escapeShellArg activationHook} - ${shellHook} - ''; + shellHook = installationScript; } diff --git a/nix/sources.json b/nix/sources.json index fd7c401c..e7b8c79a 100644 --- a/nix/sources.json +++ b/nix/sources.json @@ -118,17 +118,5 @@ "type": "tarball", "url": "https://github.com/tweag/ormolu/archive/a7076c0f83e5c06ea9067b71171859fa2ba8afd9.tar.gz", "url_template": "https://github.com///archive/.tar.gz" - }, - "project.nix": { - "branch": "pre-commit", - "description": "A configuration manager for your projects", - "homepage": null, - "owner": "hercules-ci", - "repo": "project.nix", - "rev": "ec17ac79904a296f358961ffcff7a13948c2b92e", - "sha256": "1gkb9k995ly53d5sxn5hr4lyzh57sq25j3i2cw8zva92g8x69gb5", - "type": "tarball", - "url": "https://github.com/hercules-ci/project.nix/archive/ec17ac79904a296f358961ffcff7a13948c2b92e.tar.gz", - "url_template": "https://github.com///archive/.tar.gz" } } From 7a3985167c26a4188d8921ae2212be56e21b359b Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 2 Oct 2019 00:17:31 +0200 Subject: [PATCH 4/7] Remove redundant enable options --- modules/pre-commit.nix | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/modules/pre-commit.nix b/modules/pre-commit.nix index 8a72caaa..2b2c1216 100644 --- a/modules/pre-commit.nix +++ b/modules/pre-commit.nix @@ -183,29 +183,6 @@ in { options.pre-commit = { - enable = - mkOption { - type = types.bool; - default = false; - description = - '' - Whether to enable pre-commit integration. - - https://pre-commit.com/ - ''; - }; - - enableAutoInstall = - mkOption { - type = types.bool; - default = true; - description = - '' - Whether to auto install pre-commit when invoking nix-shell in the - project root. - ''; - }; - package = mkOption { type = types.package; From b9957b52b488fc23fbc0479b6567cbef214a11cb Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 2 Oct 2019 01:02:52 +0200 Subject: [PATCH 5/7] Extract nix/tools.nix --- modules/pre-commit.nix | 5 +++++ nix/packages.nix | 12 ++---------- nix/tools.nix | 10 ++++++++++ 3 files changed, 17 insertions(+), 10 deletions(-) create mode 100644 nix/tools.nix diff --git a/modules/pre-commit.nix b/modules/pre-commit.nix index 2b2c1216..846aabd1 100644 --- a/modules/pre-commit.nix +++ b/modules/pre-commit.nix @@ -206,6 +206,11 @@ in { nix-pre-commit comes with its own set of packages for this purpose. ''; + # This default is for when the module is the entry point rather than + # /default.nix. /default.nix will override this for efficiency. + default = (import ../nix {}).callPackage ../nix/tools.nix {}; + defaultText = + literalExample ''nix-pre-commit-hooks-pkgs.callPackage tools-dot-nix {}''; }; hooks = diff --git a/nix/packages.nix b/nix/packages.nix index a939a55b..b7a7af2f 100644 --- a/nix/packages.nix +++ b/nix/packages.nix @@ -1,15 +1,7 @@ -{ hlint, shellcheck, ormolu, hindent, cabal-fmt, canonix, elmPackages, niv -, gitAndTools, runCommand, writeText, writeScript, git, nixpkgs-fmt, nixfmt -, callPackage -}: +{ niv, gitAndTools, callPackage }: let - tools = - { - inherit hlint shellcheck ormolu hindent cabal-fmt canonix nixpkgs-fmt nixfmt; - inherit (elmPackages) elm-format; - terraform-fmt = callPackage ./terraform-fmt {}; - }; + tools = callPackage ./tools.nix {}; in tools // rec { inherit niv; diff --git a/nix/tools.nix b/nix/tools.nix new file mode 100644 index 00000000..fd16db96 --- /dev/null +++ b/nix/tools.nix @@ -0,0 +1,10 @@ +{ hlint, shellcheck, ormolu, hindent, cabal-fmt, canonix, elmPackages, niv +, gitAndTools, runCommand, writeText, writeScript, git, nixpkgs-fmt, nixfmt +, callPackage +}: + +{ + inherit hlint shellcheck ormolu hindent cabal-fmt canonix nixpkgs-fmt nixfmt; + inherit (elmPackages) elm-format; + terraform-fmt = callPackage ./terraform-fmt {}; +} From 03e3814200fca27ad7ef241f76a8e90d54874baf Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 2 Oct 2019 01:06:59 +0200 Subject: [PATCH 6/7] Set up project-module.nix as a proper entrypoint --- modules/all-modules.nix | 7 +++++++ nix/project-module.nix | 5 +++-- nix/run.nix | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 modules/all-modules.nix diff --git a/modules/all-modules.nix b/modules/all-modules.nix new file mode 100644 index 00000000..9a72a78c --- /dev/null +++ b/modules/all-modules.nix @@ -0,0 +1,7 @@ +{ + imports = + [ + ./pre-commit.nix + ./hooks.nix + ]; +} diff --git a/nix/project-module.nix b/nix/project-module.nix index 4055367a..ec43244f 100644 --- a/nix/project-module.nix +++ b/nix/project-module.nix @@ -5,7 +5,8 @@ { imports = [ - ../modules/pre-commit.nix - ../modules/hooks.nix + ../modules/all-modules.nix ]; + + # TODO: move project.nix/modules/pre-commit.nix here } diff --git a/nix/run.nix b/nix/run.nix index 3bc29860..fb328e50 100644 --- a/nix/run.nix +++ b/nix/run.nix @@ -11,7 +11,7 @@ let lib.evalModules { modules = [ - ./project-module.nix + ../modules/all-modules.nix { options = { From bd07c08450731fa42206d42709e3244526b05f3a Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 2 Oct 2019 10:16:33 +0200 Subject: [PATCH 7/7] Add changelog, update readme --- CHANGELOG.md | 34 ++++++++++++++++++++++++++++++++++ README.md | 3 +++ 2 files changed, 37 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..67d291ed --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,34 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is loosely based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) +and is a rolling release. + +## 2019-10-02 + +### Added + +- The `run` derivation now uses [gitignore](https://github.com/hercules-ci/gitignore#readme) + +- Custom hooks can now be added + +### Changed + +- Hooks configuration is now module-based (using the module system, like NixOS). + `.pre-commit-config.yaml` is now obsolete with `nix-pre-commit-hooks`. Translate it to the `hooks` argument. For example: + + ``` + pre-commit-check = nix-pre-commit-hooks.run { + src = ./.; + hooks = { + elm-format.enable = true; + ormolu.enable = true; + shellcheck.enable = true; + }; + }; + ``` + +### Fixed + +- Some small improvements to the installation script (`shellHook`) diff --git a/README.md b/README.md index f3d4a122..321ecb82 100644 --- a/README.md +++ b/README.md @@ -102,6 +102,9 @@ eval "$shellHook" Everyone is encouraged to add new hooks. + +Have a look at the [existing hooks](modules/hooks.nix) and the [options](modules/pre-commit.nix). + There's no guarantee the hook will be accepted, but the general guidelines are: - Nix closure of the tool should be small e.g. `< 50MB`