diff --git a/.pre-commit.sh b/.pre-commit.sh new file mode 100755 index 0000000..430220a --- /dev/null +++ b/.pre-commit.sh @@ -0,0 +1,113 @@ +#!/usr/bin/env bash +# pre-commit hook for degette project. +# +# Run formatters first, then checkers. +# Formatters which changed a file must set the flag 'formatter_updated'. + +exit_code=0 +formatter_updated=0 +checker_dirty=0 + +printf "Stash all unstaged changes.\n" +git stash save --keep-index &> /dev/null +STAGED_PYTHON_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep ".py$") + +# Formatter: black + +printf "> black ... " + +if type black &>/dev/null; then + if [ -z "$STAGED_PYTHON_FILES" ]; then + printf "OK\n" + else + BLACK_OUTPUT="/tmp/gc-black-output.log" + touch $BLACK_OUTPUT + + if ! echo "$STAGED_PYTHON_FILES" | xargs -d'\n' black --check &>$BLACK_OUTPUT; then + echo "$STAGED_PYTHON_FILES" | xargs -d'\n' black &>$BLACK_OUTPUT + tail -1 $BLACK_OUTPUT + formatter_updated=1 + else + printf "OK\n" + fi + fi +else + printf "SKIP: program not found\n" + printf "HINT: Install black with 'pip3 install black' (black requires Python>=3.6)\n" +fi + +# Formatter: isort + +printf "> isort ... " + +if type isort &>/dev/null; then + if [ -z "$STAGED_PYTHON_FILES" ]; then + printf "OK\n" + else + ISORT_OUTPUT="/tmp/gc-isort-output.log" + touch $ISORT_OUTPUT + + if ! echo "$STAGED_PYTHON_FILES" | xargs -d'\n' isort --check-only &>$ISORT_OUTPUT; then + echo "$STAGED_PYTHON_FILES" | xargs -d'\n' isort &>$ISORT_OUTPUT + printf "Reformatted.\n" + formatter_updated=1 + else + printf "OK\n" + fi + fi +else + printf "SKIP: program not found\n" + printf "HINT: Install isort with 'pip install isort'\n" +fi + +# Checker: flake8 + +printf "> flake8 ... " + +if type flake8 &>/dev/null; then + if [ -z "$STAGED_PYTHON_FILES" ]; then + printf "OK\n" + else + FLAKE8_OUTPUT="/tmp/gc-flake8-output.log" + touch $FLAKE8_OUTPUT + + # fix black incompatibility, sometimes flake8 is not PEP8-compliant + if ! echo "$STAGED_PYTHON_FILES" | xargs -d'\n' flake8 --ignore=E203,W503 &>$FLAKE8_OUTPUT; then + printf "FAIL\n" + cat $FLAKE8_OUTPUT + checker_dirty=1 + else + printf "OK\n" + fi + fi +else + printf "SKIP: program not found\n" + printf "HINT: Install flake8 with 'pip install flake8'\n" +fi + +# End + +if [ $checker_dirty -ne 0 ] +then + printf ">>> Checker(s) detect(s) issue(s)\n" + printf " You can still commit and push :)\n" + printf " Be warned that our CI may cause you more trouble.\n" +fi + +# In some cases, black makes some changes that are reverted by isort: +# the second condition checks whether some changes have been made at the end of the day. +# Indeed, when this case applies, one cannot commit using this hook. +if [ $formatter_updated -ne 0 ] +then + printf ">>> Working tree updated by formatter(s)\n" + printf " Add changes to staging area and retry.\n" + exit_code=1 +fi + +printf "\n" + +printf "Unstash all unstaged changes.\n" +git stash pop &> /dev/null + +exit $exit_code + diff --git a/requirements-dev.txt b/requirements-dev.txt index 4bffbf0..a6cb3e1 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,4 +1,6 @@ -r requirements.txt django-debug-toolbar ipython -black \ No newline at end of file +black +isort +flake8