diff --git a/nix/tools/devTools.nix b/nix/tools/devTools.nix index 8b90f1fcb..808bebeae 100644 --- a/nix/tools/devTools.nix +++ b/nix/tools/devTools.nix @@ -4,6 +4,8 @@ , checkedShellScript , devCabalOptions , entr +, git +, gnugrep , graphviz , haskellPackages , hsie @@ -65,23 +67,161 @@ let name = "postgrest-check"; docs = '' - Run most checks that will also run on CI. + Run most checks that will also run on CI, but only against the + latest PostgreSQL version. - This currently excludes the memory tests, as those are particularly - expensive. + This currently excludes the memory and spec-idempotence tests, + as those are particularly expensive. ''; inRootDir = true; } '' - ${withTools.withPgAll} ${tests}/bin/postgrest-test-spec - ${withTools.withPgAll} ${tests}/bin/postgrest-test-querycost + ${tests}/bin/postgrest-test-spec + ${tests}/bin/postgrest-test-querycost ${tests}/bin/postgrest-test-doctests - ${tests}/bin/postgrest-test-spec-idempotence ${tests}/bin/postgrest-test-io ${style}/bin/postgrest-lint ${style}/bin/postgrest-style-check ''; + gitHooks = + let + name = "postgrest-git-hooks"; + in + checkedShellScript + { + inherit name; + docs = + '' + Enable or disable git pre-commit and pre-push hooks. + + Basic is faster and will only run: + - pre-commit: postgrest-style + - pre-push: postgrest-lint + + Full takes a lot more time and will run: + - pre-commit: postgrest-style && postgrest-lint + - pre-push: postgrest-check + + Changes made by postgrest-style will be staged automatically. + + Example usage: + postgrest-git-hooks disable + postgrest-git-hooks enable basic + postgrest-git-hooks enable full + + The "run" operation and "--hook" argument are only used internally. + ''; + args = + [ + "ARG_POSITIONAL_SINGLE([operation], [Operation])" + "ARG_TYPE_GROUP_SET([OPERATION], [OPERATION], [operation], [disable,enable,run])" + "ARG_POSITIONAL_SINGLE([mode], [Mode], [basic])" + "ARG_TYPE_GROUP_SET([MODE], [MODE], [mode], [basic,full])" + "ARG_OPTIONAL_SINGLE([hook], , [Hook], [pre-commit])" + "ARG_TYPE_GROUP_SET([HOOK], [HOOK], [hook], [pre-commit,pre-push])" + ]; + positionalCompletion = + '' + if test "$prev" == "${name}"; then + COMPREPLY=( $(compgen -W "enable disable" -- "$cur") ) + elif test "$prev" == "enable" || test "$prev" == "disable"; then + COMPREPLY=( $(compgen -W "basic full" -- "$cur") ) + fi + ''; + inRootDir = true; + } + '' + if [ run != "$_arg_operation" ]; then + # Remove all hooks first and ignore failures because the file might be missing. + # This assumes that we're only adding lines that include "postgrest-git-hooks" + # to the hook file. + sed -i -e '/postgrest-git-hooks/d' .git/hooks/pre-{commit,push} 2> /dev/null || true + + if [ disabled != "$_arg_mode" ]; then + # The nix-shell && + nix-shell || pattern makes sure we can run the hook + # in a pure nix-shell, where nix-shell itself is not available, too. + + # The $(nix-shell --run "command -v ...") pattern ensures we only need to enable + # the hooks once and still run the latest of our hook scripts, even when we + # update them in the repo. + + echo 'command -v nix-shell > /dev/null || postgrest-git-hooks --hook=pre-commit run' "$_arg_mode" \ + >> .git/hooks/pre-commit + # shellcheck disable=SC2016 + echo 'command -v nix-shell > /dev/null && $(nix-shell --quiet -Q --run "command -v postgrest-git-hooks") --hook=pre-commit run' "$_arg_mode" \ + >> .git/hooks/pre-commit + chmod +x .git/hooks/pre-commit + + echo 'command -v nix-shell > /dev/null || postgrest-git-hooks --hook=pre-push run' "$_arg_mode" \ + >> .git/hooks/pre-push + # shellcheck disable=SC2016 + echo 'command -v nix-shell > /dev/null && $(nix-shell --quiet -Q --run "command -v postgrest-git-hooks") --hook=pre-push run' "$_arg_mode" \ + >> .git/hooks/pre-push + chmod +x .git/hooks/pre-push + fi + else + # When run from a git hook, the GIT_ environment variables conflict with our withGit helper. + # The following unsets all GIT_ variables. + unset "''${!GIT_@}" + + case "$_arg_mode" in + basic) + case "$_arg_hook" in + pre-commit) + # To be able to automatically add only changes from postgrest-style to the staging area, + # we need to run postgrest-style twice. Otherwise we'd risk merge conflicts when popping + # the stash afterwards. + ${style}/bin/postgrest-style + + stash="postgrest-git-hooks-$RANDOM" + ${git}/bin/git stash push --include-untracked --keep-index -m "$stash" + if [ "$(git stash list --grep $stash)" ]; then + # Only create the stash pop trap, if we actually created a stash. + # Otherwise stash pop will cause havoc. + trap '${git}/bin/git stash pop $(git stash list --format=format:%gD --grep "$stash" -n1)' EXIT + fi + + ${style}/bin/postgrest-style + ${git}/bin/git add . + ;; + pre-push) + # Create a clean working tree without any uncomitted changes. + ${withTools.withGit} HEAD ${style}/bin/postgrest-lint + ;; + esac + ;; + full) + case "$_arg_hook" in + pre-commit) + # To be able to automatically add only changes from postgrest-style to the staging area, + # we need to run postgrest-style twice. Otherwise we'd risk merge conflicts when popping + # the stash afterwards. + ${style}/bin/postgrest-style + + stash="postgrest-git-hooks-$RANDOM" + ${git}/bin/git stash push --include-untracked --keep-index -m "$stash" + if [ "$(git stash list --grep $stash)" ]; then + # Only create the stash pop trap, if we actually created a stash. + # Otherwise stash pop will cause havoc. + trap '${git}/bin/git stash pop $(git stash list --format=format:%gD --grep "$stash" -n1)' EXIT + fi + + ${style}/bin/postgrest-style + ${git}/bin/git add . + + ${style}/bin/postgrest-lint + ;; + pre-push) + # Create a clean working tree without any uncomitted changes. + ${withTools.withGit} HEAD ${check} + ;; + esac + ;; + esac + fi + ''; + dumpMinimalImports = checkedShellScript { @@ -147,6 +287,7 @@ buildToolbox watch pushCachix check + gitHooks dumpMinimalImports hsieMinimalImports hsieGraphModules diff --git a/nix/tools/style.nix b/nix/tools/style.nix index 13adc1b9b..7b55c9385 100644 --- a/nix/tools/style.nix +++ b/nix/tools/style.nix @@ -44,6 +44,8 @@ let '' ${style} + trap "echo postgrest-style-check failed. Run postgrest-style to fix issues automatically." ERR + ${git}/bin/git diff-index --exit-code HEAD -- '*.hs' '*.lhs' '*.nix' '';