diff --git a/.gitignore b/.gitignore index 84747ae..db122e8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ .direnv/ .justfile -.pre-commit-config.yaml .prettierignore site/ +lefthook.yml diff --git a/flake.nix b/flake.nix index 88e54c1..6b08c71 100644 --- a/flake.nix +++ b/flake.nix @@ -44,9 +44,9 @@ tools = mkTools [ pkgs.cue pkgs.just + pkgs.lefthook pkgs.mkdocs pkgs.nixpkgs-fmt - pkgs.pre-commit pkgs.nodePackages.prettier ]; @@ -64,7 +64,7 @@ tasks = { check = [ "@${tools.nixpkgs-fmt.exe} --check flake.nix $(git ls-files '**/*.nix')" - "@${tools.prettier.exe} -c ." + "@${tools.prettier.exe} --check ." "@nix flake check" "@mkdocs build --strict && rm -rf site" ]; @@ -77,18 +77,26 @@ ]; }; }; - # Pre-commit configuration - "pre-commit.mkLocalConfig" = { - nixpkgs-fmt = { - entry = tools.nixpkgs-fmt.exe; - language = "system"; - files = "\\.nix"; + # Lefthook configuration + "lefthook.mkConfig" = { + pre-commit = { + commands = { + nixpkgs-fmt = { + run = "${tools.nixpkgs-fmt.exe} --check {staged_files}"; + glob = "*.nix"; + }; + prettier = { + run = "${tools.prettier.exe} --check {staged_files}"; + glob = "*.{yaml,yml,md}"; + }; + }; }; }; # Prettier "prettier.mkIgnoreConfig" = [ ".direnv" "tests" + "lefthook.yml" ]; }; in @@ -102,18 +110,15 @@ # Local tests checks = { just = pkgs.callPackage ./tests/just { inherit pkgs plugins; }; + lefthook = pkgs.callPackage ./tests/lefthook { inherit pkgs plugins; }; pre-commit = pkgs.callPackage ./tests/pre-commit { inherit pkgs plugins; }; prettier = pkgs.callPackage ./tests/prettier { inherit pkgs plugins; }; }; - # Local shell for development. - # The shell does not currently build on i686-linux machines due to a - # downstream dependency of pkgs.pre-commit. - # See: https://github.com/NixOS/nixpkgs/issues/174847 - # The shell also does not currently work on x86_64-darwin due to a + # The shell does not currently work on x86_64-darwin due to a # downstream dependency of mkdocs. # See: https://github.com/NixOS/nixpkgs/pull/171388 - devShells = nixpkgs.lib.optionalAttrs (!builtins.elem system [ "i686-linux" "x86_64-darwin" ]) { + devShells = nixpkgs.lib.optionalAttrs (!builtins.elem system [ "x86_64-darwin" ]) { default = pkgs.mkShell { shellHook = (lib.mkAll configurations).shellHook; packages = tools.all ++ deps; diff --git a/plugins/default.nix b/plugins/default.nix index ba8f8c4..e5b148c 100644 --- a/plugins/default.nix +++ b/plugins/default.nix @@ -4,6 +4,10 @@ */ just = import ./just { inherit pkgs lib; }; + /* https://github.com/evilmartians/lefthook + */ + lefthook = import ./lefthook { inherit pkgs lib; }; + /* https://github.com/pre-commit/pre-commit */ pre-commit = import ./pre-commit { inherit pkgs lib; }; diff --git a/plugins/lefthook/default.nix b/plugins/lefthook/default.nix new file mode 100644 index 0000000..19d32d0 --- /dev/null +++ b/plugins/lefthook/default.nix @@ -0,0 +1,6 @@ +{ pkgs, lib }: +{ + /* Creates a lefthook.yml file for configuring lefthook. + */ + mkConfig = import ./mkConfig.nix { inherit pkgs lib; }; +} diff --git a/plugins/lefthook/mkConfig.nix b/plugins/lefthook/mkConfig.nix new file mode 100644 index 0000000..06d5d00 --- /dev/null +++ b/plugins/lefthook/mkConfig.nix @@ -0,0 +1,33 @@ +{ pkgs, lib }: +data: +with pkgs.lib; +let + files = [ ./template.cue ]; + output = "lefthook.yml"; + lefthook = pkgs.lefthook; + + # Add an extra hook for adding required stages whenever the file changes + skip_attrs = [ + "colors" + "extends" + "skip_output" + "source_dir" + "source_dir_local" + ]; + stages = builtins.attrNames (builtins.removeAttrs data skip_attrs); + stagesStr = builtins.concatStringsSep " " stages; + shellHookExtra = '' + # Install configured hooks + for stage in ${stagesStr}; do + ${lefthook}/bin/lefthook add -a "$stage" + done + ''; + + # Generate the module + result = lib.mkTemplate { + inherit data files output shellHookExtra; + }; +in +{ + inherit (result) configFile shellHook; +} diff --git a/plugins/lefthook/template.cue b/plugins/lefthook/template.cue new file mode 100644 index 0000000..02424cb --- /dev/null +++ b/plugins/lefthook/template.cue @@ -0,0 +1,38 @@ +#Config: { + [string]: #Hook + colors?: bool | *true + extends?: [...string] + skip_output?: [...string] + source_dir?: string + source_dir_local?: string + ... +} + +#Hook: { + commands?: [string]: #Command + exclude_tags?: [...string] + parallel?: bool | *false + piped?: bool | *false + scripts?: [string]: #Script + ... +} + +#Command: { + exclude?: string + files?: string + glob?: string + root?: string + run: string + skip?: bool | [...string] + tags?: string + ... +} + +#Script: { + runner: string + ... +} + +{ + #Config +} \ No newline at end of file diff --git a/tests/lefthook/default.nix b/tests/lefthook/default.nix new file mode 100644 index 0000000..2cbef74 --- /dev/null +++ b/tests/lefthook/default.nix @@ -0,0 +1,36 @@ +{ pkgs, plugins }: +let + output = plugins.lefthook.mkConfig { + commit-msg = { + scripts = { + template_checker = { runner = "bash"; }; + }; + }; + pre-commit = { + commands = { + stylelint = { + tags = "frontend style"; + glob = "*.js"; + run = "yarn stylelint {staged_files}"; + }; + rubocop = { + tags = "backend style"; + glob = "*.rb"; + exclude = "application.rb|routes.rb"; + run = "bundle exec rubocop --force-exclusion {all_files}"; + }; + }; + scripts = { + "good_job.js" = { runner = "node"; }; + }; + }; + }; + + result = pkgs.runCommand "test.lefthook" + { } + '' + cmp "${./expected.yml}" "${output.configFile}" + touch $out + ''; +in +result diff --git a/tests/lefthook/expected.yml b/tests/lefthook/expected.yml new file mode 100644 index 0000000..4080fa1 --- /dev/null +++ b/tests/lefthook/expected.yml @@ -0,0 +1,18 @@ +commit-msg: + scripts: + template_checker: + runner: bash +pre-commit: + commands: + rubocop: + exclude: application.rb|routes.rb + glob: '*.rb' + run: bundle exec rubocop --force-exclusion {all_files} + tags: backend style + stylelint: + glob: '*.js' + run: yarn stylelint {staged_files} + tags: frontend style + scripts: + good_job.js: + runner: node