Skip to content

Instantly share code, notes, and snippets.

@guilherme
Last active February 24, 2024 20:39
Show Gist options
  • Save guilherme/9604324 to your computer and use it in GitHub Desktop.
Save guilherme/9604324 to your computer and use it in GitHub Desktop.
Git pre-commit hook that detects if the developer forget to remove all the javascript console.log before commit.
#!/bin/sh
# Redirect output to stderr.
exec 1>&2
# enable user input
exec < /dev/tty
consoleregexp='console.log'
# CHECK
if test $(git diff --cached | grep $consoleregexp | wc -l) != 0
then
exec git diff --cached | grep -ne $consoleregexp
read -p "There are some occurrences of console.log at your modification. Are you sure want to continue? (y/n)" yn
echo $yn | grep ^[Yy]$
if [ $? -eq 0 ]
then
exit 0; #THE USER WANTS TO CONTINUE
else
exit 1; # THE USER DONT WANT TO CONTINUE SO ROLLBACK
fi
fi
@TheophileMot
Copy link

TheophileMot commented Feb 16, 2022

@TimCreasman Thanks, that's helpful. You can simplify a bit: .* means "any number of characters", so it is redundant to have .*.*. Also, the outer parentheses in filenameRegexp aren't being used, so they can be removed. Thus:

consoleRegexp='^[^-].*console.log'
filenameRegexp='^[^-].*console.log(\|^+++'

@TimCreasman
Copy link

@TheophileMot Thanks for the input! I updated the code with your changes 👍

@TheophileMot
Copy link

TheophileMot commented Feb 17, 2022

@TimCreasman Glad to help! I worked on this myself yesterday; it's starting to get away from the original topic, but other people might find this useful. I wanted to check for a few things (e.g., warn when committing directly to main), so I wrapped the console.log check in a function called no_console, and made another function called check_branch to see whether the current branch is master or main. Each of these calls another function, confirm, when there is a potential problem.

Another small fix: after the "y/n" prompt, I didn't like the duplication of the user input, so instead of the line if echo "$yn" | grep "^[Yy]$", which is a bit hacky, it now just checks the variable directly with if test "$yn" == "y" || test "$yn" == "Y".

I also added some fancy colours.

#!/bin/bash

# Redirect output to stderr.
exec 1>&2
# enable user input
exec < /dev/tty

LRED='\033[1;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # NO COLOUR

confirm () {  
  printf "${YELLOW}"
  read -rp "Continue with commit? (y/n) " yn
  printf "${NC}"
  if test "$yn" == "y" || test "$yn" == "Y"
  then
    printf "\n" # CONTINUE
  else    
    exit 1
  fi
}

check_branch () {
  if test "$(git branch --show-current)" == "master" || test "$(git branch --show-current)" == "main"
  then
    printf "You are on ${LRED}$(git branch --show-current)${NC}.\n"
    confirm
  fi
}

no_console () {
  consoleRegexp='^[^-].*console.log'
  filenameRegexp='^[^-].*console.log(\|^+++'

  if test "$(git diff --cached | grep -c "$consoleRegexp")" != 0
  then
    git diff --cached | grep -ne "$filenameRegexp" | grep -B1 "$consoleRegexp"
    printf "\nSome files include ${LRED}console.log${NC}.\n"
    confirm
  fi
}

### RUN THROUGH CHECKS AND STOP IF THERE IS A PROBLEM
check_branch
no_console

printf "${GREEN}Proceeding with commit...${NC}\n"

This gives a little framework that makes it easier to add further pre-commit checks.


Exercise for the reader: on success, the code as written now displays a "Proceeding with commit" message even when there was no problem. Some people might find this a nuisance. Add a variable to flag whenever the user decides to proceed despite a problem (it can go in the confirm function), and display the "Proceeding" message only when this flag is true.

@Kasik-D
Copy link

Kasik-D commented May 31, 2022

Hello, you are very smart people. Can you help how i add test npm run lint -s && run before this comment. If this command throw error don`t go check console log

@TheophileMot
Copy link

TheophileMot commented May 31, 2022

@Kasik-D Sure. A couple of helpful things to know: $? gives the exit code of whatever command was previously run (the standard is 0 for success, 1 or another number otherwise); also, there are ways to compare numbers in a script, like [[ $x -ne $y ]]. Here -ne stands for "Not Equal to".

So try this:

run_linter () {
  npm lint -s     # or npx eslint *.js, or whatever your lint command is
  if [[ $? -ne 0 ]]
  then
    printf "Oh no\n"
    exit 1
  fi
}

# ...

run_linter
no_console
whatever_other_stuff

@Kasik-D
Copy link

Kasik-D commented Jun 1, 2022

@TheophileMot Thank you, one more qustions what is this programming language ? Where i see instruction of this

@TheophileMot
Copy link

@Kasik-D You're welcome. This language is Bash shell scripting. Bash (or a similar shell, like zsh) is what the terminal runs on MacOS or Linux machines. On Windows, you would have to download it. Learning Bash is a life-long journey, but there are many excellent resources. This looks like a good place to start: https://linuxconfig.org/bash-scripting-tutorial-for-beginners. Have fun!

@Kasik-D
Copy link

Kasik-D commented Jun 1, 2022

@TheophileMot Thanks again

@TimCreasman
Copy link

I've been working on a more general solution that takes a text file of blocked phrases as input and will detect if any of these phrases are in your commit. I also incorporated the color changes @TheophileMot added and formatted some additional things.

If you have a file block-list.txt with the following:

console.log
// TODO
other thing

It will detect these phrases and print out the offending diff statement.

Also disclaimer: I only tested this on OSX, but anyway here's the code:

#!/bin/bash

# Redirect output to stderr.
exec 1>&2

# Block list file location: Update this to point to your own list
blockListFile=".husky/_/block-list.txt"

# Terminal colors and formats
lightRed='\033[1;31m'
green='\033[0;32m'
yellow='\033[1;33m'
clearFormat='\033[0m'
underline='\033[4m'

# Flags
errorFoundFlag=0

confirm () {
  printf '%b\n' "$yellow"
  read -rp "Commit anyway? (y/n) " yn </dev/tty # Enable user input.
  printf '%b' "$clearFormat"

  if echo "$yn" | grep "^[Yy]$"; then
    printf "%bProceeding with commit...%b\n" "$green" "$clearFormat" # The user wants to continue.
  else
    exit 1 # The user does not wish to continue, so rollback.
  fi
}

# Finds the section of the diff that has the blocked phrase
offendingDiff () {
  # Skip this method if there is no parameter passed in
  if [ -z "$1" ]; then
    return 0
  fi

  git diff --cached --color=always | tail -r | awk "/$1/{flag=1}/diff/{flag=0}flag" | tail -r
}

checkRegexp () {
  # Skip this method if there is no parameter passed in
  if [ -z "$1" ]; then
    return 0
  fi

  # Here we prepend the regex with [^-] which will ensure we won't be blocked committing changes that remove the blocked phrase
  regex="^[^-].*$1"

  if [ "$(git diff --cached | grep -c "$regex")" != 0 ]; then
    printf "%b\nError: you are attempting to commit the blocked phrase: %b%s:%b\n" "$lightRed" "$underline" "$1" "$clearFormat"
    # Highlights and adds error arrows to the printout
    offendingDiff "$1" | sed "s/$1/$(printf "%b$1" "$underline")/" | sed "/$1/ s/$/ $(printf "%b<<<<<<<%b" "$lightRed" "$clearFormat")/"
    errorFoundFlag=1
  fi
}

checkBlockList () {
  while read -r line
  do
    checkRegexp "$line"
  done < "$blockListFile"
  if [ "$errorFoundFlag" = 1 ]; then
    confirm
  fi
}

checkBlockList

@Maximauve
Copy link

@TimCreasman thanks for this script, I tested it on Windows / Linux and it seems that the -r argument on tail -r in your OffendingDiff is unkown.

I found the tac command that works just fine

git diff --cached --color=always | tac | awk "/$1/{flag=1}/diff/{flag=0}flag" | tac

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment