Created
October 4, 2021 09:13
-
-
Save rbanffy/fccfcf8f5566d4d1e6c214efb15839a5 to your computer and use it in GitHub Desktop.
A better pre-commit hook
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# This script is meant to be symlinked into your project's .git/hooks | |
# or global ~/.githooks directory within projects you are working | |
# with. | |
# Note: linters should be configured by project-local config files, so | |
# we can avoid changing this script. It's fine to change it, but, | |
# remember if you do that, you're on your own. Python and JavaScript linters | |
# will ideally run from project-specific folders so they are aware of the | |
# context the files should be analyzed with. | |
# Set up the environment so that we exit (-e) immediately as soon as a | |
# command exits with a non-zero return value and (-o pipefail) any | |
# pipeline with internal non-zero returns will cause the script to | |
# error out. Do not set -u yet. | |
set -eo pipefail | |
# ANSI sequences, if we are running in a terminal. If not, leave empty. | |
if [ -t 1 ]; then | |
YELLOW="\033[33m" | |
RESET="\033[0m" | |
else | |
YELLOW="" | |
RESET="" | |
fi | |
# If there is a branch named "main", we assume it's the | |
# parent. Otherwise we assume the parent is "master". | |
if git branch --list | grep '^main$'; then | |
# There is a branch called main, so let's use it. | |
PARENT_BRANCH="main" | |
else | |
# We didn't find a main, so we assume base is master. | |
PARENT_BRANCH="master" | |
fi | |
echo "Using $PARENT_BRANCH as parent branch" | |
# Any unset (-u) variable from now on causes an error. We didn't do | |
# this before because we may want to manually override the parent | |
# branch from the command line. This override logic needs to be | |
# implemented | |
set -u | |
function lint_shell() { | |
# Reformat and ling a shell file. Since we do the same for added and | |
# modified files, we ignore $2. | |
shfmt -i 4 -w "$1" | |
shellcheck "$1" | |
} | |
function lint_python() { | |
# The two paths exist so that we can leave already existing files | |
# undisturbed while we enforce much stricter standards for new | |
# ones. | |
if [[ "$2" == "A" ]]; then # A indicates an added file. | |
# If this is a new file, we check and reformat the file more | |
# rigorously than we do existing files. | |
isort -faas "$1" | |
black -v "$1" | |
flake8 "$1" | |
else | |
# If the file is not being added in this branch, we just run a | |
# Flake 8 with default behavior. | |
flake8 "$1" | |
fi | |
} | |
function lint_go() { | |
# Lint a Go file. Since we do the same for added and mofified | |
# files, we ignore $2. | |
go fmt "$1" | |
} | |
function lint_markdown() { | |
# Lint a markdown file. Since we do the same for added and | |
# mofified files, we ignore $2. | |
lint-md "$1" | |
} | |
function lint_web() { | |
if [[ "$2" == "A" ]]; then # A indicates an added file. | |
# If this is a new file, we check and reformat the file more | |
# rigorously than we do existing files. | |
prettier --write "$1" | |
else | |
# If the file is not being added in this branch, we just check | |
# it with prettier. | |
prettier --check "$1" | |
fi | |
} | |
function lint_tf() { | |
# Always reformat Terraform files. | |
terraform fmt -check=true "$1" | |
} | |
function show_unprocessed() { | |
# Show a message in case a file wasn't processed. | |
echo -e "${YELLOW}File $1 ($2) was not processed.${RESET}" | |
} | |
function lint_file() { | |
# $1: the file we want to lint | |
# $2: "A" for added, any other value for modified | |
# Get the file extension. | |
extension=${1##*\.} | |
case $extension in | |
"sh") | |
lint_shell "$1" "$2" | |
;; | |
"py") | |
lint_python "$1" "$2" | |
;; | |
"go") | |
lint_go "$1" "$2" | |
;; | |
"md") | |
lint_markdown "$1" "$2" | |
;; | |
"js" | "css" | "html" | "ts" | "tsx") | |
lint_web "$1" "$2" | |
;; | |
"tf" | "tfvars") | |
lint_tf "$1" "$2" | |
;; | |
*) | |
# Extension is not a known one, but we can use file magic | |
type=$(file --mime-type -b "$1") | |
case $type in | |
"text/x-shellscript") | |
lint_shell "$1" "$2" | |
;; | |
*) | |
show_unprocessed "$1" "$type" | |
;; | |
esac | |
;; | |
esac | |
} | |
# Iterate over the files added on this branch (we computer the | |
# difference between the current branch and the parent branch we | |
# identified. | |
for file in $(git diff --name-only --diff-filter=Ad "$PARENT_BRANCH"); do | |
lint_file "$file" "A" | |
done | |
# Iterate over the files modified on this commit | |
for file in $(git diff --name-only --diff-filter=Md HEAD); do | |
lint_file "$file" "M" | |
done |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment