pre-commit-black 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. #!/usr/bin/env bash
  2. # git pre-commit hook that runs a black stylecheck.
  3. # Based on pre-commit-clang-format.
  4. ##################################################################
  5. # SETTINGS
  6. # Set path to black binary.
  7. BLACK=`which black 2>/dev/null`
  8. BLACK_OPTIONS="-l 120"
  9. # Remove any older patches from previous commits. Set to true or false.
  10. DELETE_OLD_PATCHES=false
  11. # File types to parse.
  12. FILE_NAMES="SConstruct SCsub"
  13. FILE_EXTS=".py"
  14. # Use pygmentize instead of cat to parse diff with highlighting.
  15. # Install it with `pip install pygments` (Linux) or `easy_install Pygments` (Mac)
  16. PYGMENTIZE=`which pygmentize 2>/dev/null`
  17. if [ ! -z "$PYGMENTIZE" ]; then
  18. READER="pygmentize -l diff"
  19. else
  20. READER=cat
  21. fi
  22. # Path to zenity
  23. ZENITY=`which zenity 2>/dev/null`
  24. # Path to xmessage
  25. XMSG=`which xmessage 2>/dev/null`
  26. # Path to powershell (Windows only)
  27. PWSH=`which powershell 2>/dev/null`
  28. ##################################################################
  29. # There should be no need to change anything below this line.
  30. . "$(dirname -- "$0")/canonicalize_filename.sh"
  31. # exit on error
  32. set -e
  33. # check whether the given file matches any of the set extensions
  34. matches_name_or_extension() {
  35. local filename=$(basename "$1")
  36. local extension=".${filename##*.}"
  37. for name in $FILE_NAMES; do [[ "$name" == "$filename" ]] && return 0; done
  38. for ext in $FILE_EXTS; do [[ "$ext" == "$extension" ]] && return 0; done
  39. return 1
  40. }
  41. # necessary check for initial commit
  42. if git rev-parse --verify HEAD >/dev/null 2>&1 ; then
  43. against=HEAD
  44. else
  45. # Initial commit: diff against an empty tree object
  46. against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
  47. fi
  48. if [ ! -x "$BLACK" ] ; then
  49. if [ ! -t 1 ] ; then
  50. if [ -x "$ZENITY" ] ; then
  51. $ZENITY --error --title="Error" --text="Error: black executable not found."
  52. exit 1
  53. elif [ -x "$XMSG" ] ; then
  54. $XMSG -center -title "Error" "Error: black executable not found."
  55. exit 1
  56. elif [ \( \( "$OSTYPE" = "msys" \) -o \( "$OSTYPE" = "win32" \) \) -a \( -x "$PWSH" \) ]; then
  57. winmessage="$(canonicalize_filename "$(dirname -- "$0")/winmessage.ps1")"
  58. $PWSH -noprofile -executionpolicy bypass -file "$winmessage" -center -title "Error" --text "Error: black executable not found."
  59. exit 1
  60. fi
  61. fi
  62. printf "Error: black executable not found.\n"
  63. printf "Set the correct path in $(canonicalize_filename "$0").\n"
  64. exit 1
  65. fi
  66. # create a random filename to store our generated patch
  67. prefix="pre-commit-black"
  68. suffix="$(date +%s)"
  69. patch="/tmp/$prefix-$suffix.patch"
  70. # clean up any older black patches
  71. $DELETE_OLD_PATCHES && rm -f /tmp/$prefix*.patch
  72. # create one patch containing all changes to the files
  73. git diff-index --cached --diff-filter=ACMR --name-only $against -- | while read file;
  74. do
  75. # ignore thirdparty files
  76. if grep -q "thirdparty" <<< $file; then
  77. continue;
  78. fi
  79. # ignore file if not one of the names or extensions we handle
  80. if ! matches_name_or_extension "$file"; then
  81. continue;
  82. fi
  83. # format our file with black, create a patch with diff and append it to our $patch
  84. # The sed call is necessary to transform the patch from
  85. # --- $file timestamp
  86. # +++ $file timestamp
  87. # to both lines working on the same file and having a/ and b/ prefix.
  88. # Else it can not be applied with 'git apply'.
  89. "$BLACK" "$BLACK_OPTIONS" --diff "$file" | \
  90. sed -e "1s|--- |--- a/|" -e "2s|+++ |+++ b/|" >> "$patch"
  91. done
  92. # if no patch has been generated all is ok, clean up the file stub and exit
  93. if [ ! -s "$patch" ] ; then
  94. printf "Files in this commit comply with the black formatter rules.\n"
  95. rm -f "$patch"
  96. exit 0
  97. fi
  98. # a patch has been created, notify the user and exit
  99. printf "\nThe following differences were found between the code to commit "
  100. printf "and the black formatter rules:\n\n"
  101. if [ -t 1 ] ; then
  102. $READER "$patch"
  103. printf "\n"
  104. # Allows us to read user input below, assigns stdin to keyboard
  105. exec < /dev/tty
  106. terminal="1"
  107. else
  108. cat "$patch"
  109. printf "\n"
  110. # Allows non zero zenity/powershell output
  111. set +e
  112. terminal="0"
  113. fi
  114. while true; do
  115. if [ $terminal = "0" ] ; then
  116. if [ -x "$ZENITY" ] ; then
  117. choice=$($ZENITY --text-info --filename="$patch" --width=800 --height=600 --title="Do you want to apply that patch?" --ok-label="Apply" --cancel-label="Do not apply" --extra-button="Apply and stage")
  118. if [ "$?" = "0" ] ; then
  119. yn="Y"
  120. else
  121. if [ "$choice" = "Apply and stage" ] ; then
  122. yn="S"
  123. else
  124. yn="N"
  125. fi
  126. fi
  127. elif [ -x "$XMSG" ] ; then
  128. $XMSG -file "$patch" -buttons "Apply":100,"Apply and stage":200,"Do not apply":0 -center -default "Do not apply" -geometry 800x600 -title "Do you want to apply that patch?"
  129. choice=$?
  130. if [ "$choice" = "100" ] ; then
  131. yn="Y"
  132. elif [ "$choice" = "200" ] ; then
  133. yn="S"
  134. else
  135. yn="N"
  136. fi
  137. elif [ \( \( "$OSTYPE" = "msys" \) -o \( "$OSTYPE" = "win32" \) \) -a \( -x "$PWSH" \) ]; then
  138. winmessage="$(canonicalize_filename "$(dirname -- "$0")/winmessage.ps1")"
  139. $PWSH -noprofile -executionpolicy bypass -file "$winmessage" -file "$patch" -buttons "Apply":100,"Apply and stage":200,"Do not apply":0 -center -default "Do not apply" -geometry 800x600 -title "Do you want to apply that patch?"
  140. choice=$?
  141. if [ "$choice" = "100" ] ; then
  142. yn="Y"
  143. elif [ "$choice" = "200" ] ; then
  144. yn="S"
  145. else
  146. yn="N"
  147. fi
  148. else
  149. printf "Error: zenity, xmessage, or powershell executable not found.\n"
  150. exit 1
  151. fi
  152. else
  153. read -p "Do you want to apply that patch (Y - Apply, N - Do not apply, S - Apply and stage files)? [Y/N/S] " yn
  154. fi
  155. case $yn in
  156. [Yy] ) git apply $patch;
  157. printf "The patch was applied. You can now stage the changes and commit again.\n\n";
  158. break
  159. ;;
  160. [Nn] ) printf "\nYou can apply these changes with:\n git apply $patch\n";
  161. printf "(may need to be called from the root directory of your repository)\n";
  162. printf "Aborting commit. Apply changes and commit again or skip checking with";
  163. printf " --no-verify (not recommended).\n\n";
  164. break
  165. ;;
  166. [Ss] ) git apply $patch;
  167. git diff-index --cached --diff-filter=ACMR --name-only $against -- | while read file;
  168. do git add $file;
  169. done
  170. printf "The patch was applied and the changed files staged. You can now commit.\n\n";
  171. break
  172. ;;
  173. * ) echo "Please answer yes or no."
  174. ;;
  175. esac
  176. done
  177. exit 1 # we don't commit in any case