ZNackasha
ZNackasha

Reputation: 1089

Git server hooks, check if code is formated

I am trying to create a git server hook so that unformatted code would not be pushed. In my scenario I want to use clang format to check if the code is formatted correctly. if not the user will receive a message stating they need to format the code before pushing.

we are trying to implement a must format policy at work and this will simply be a safety net.

Upvotes: 5

Views: 4881

Answers (2)

coolaj86
coolaj86

Reputation: 77112

Summary

It took me quite a while to piece together from the sparse examples I was able to find, so I figure I'll share what I've come up with as well.

Basically here's what you need to do:

  • use an update hook (similar to pre-receive)
  • inspect the branch and commit
  • checkout the bare repo to a temp folder
  • run the formatter (Prettier, in this case) on the temp folder
  • exit non-zero if files are / would be changed
    • (either from the tool's output or a git status --work-tree=... --git-dir=...)

Example: Prettier + 'update' Hook

Although the pre-receive hook is much more googleable, I find that the update hook is both easier to use and more flexible. I'm using Prettier in this example, but I tried to write to be easy to adapt to any linter / vetter / formatter.

In my case my git user's home directory is /srv/git-repositories and the hooks for the project I've set up are at:

/srv/git-repositories/my-project.git/hooks/update

I've actually tested this, so I know that it works, although it's a bit watered down from what I'm really using.

That said, it covers the basics:

ref_name=$1
new_rev=$3

# only check branches, not tags or bare commits
if [ -z $(echo $ref_name | grep "refs/heads/") ]; then
  exit 0
fi

# don't check empty branches
if [ "$(expr "${new_rev}" : '0*$')" -ne 0 ]; then
  exit 0
fi

# Checkout a copy of the branch (but also changes HEAD)
my_work_tree=$(mktemp -d -t git-work-tree.XXXXXXXX) 2>/dev/null
git --work-tree="${my_work_tree}" --git-dir="." checkout $new_rev -f >/dev/null

# Do the formatter check
echo "Checking code formatting..."
pushd ${my_work_tree} >/dev/null
prettier './**/*.{js,css,html,json,md}' --list-different
my_status=$?
popd >/dev/null

# reset HEAD to master, and cleanup
git --work-tree="${my_work_tree}" --git-dir="." checkout master -f >/dev/null
rm -rf "${my_work_tree}"

# handle error, if any
if [ "0" != "$my_status" ]; then
  echo "Please format the files listed above and re-commit."
  echo "(and don't forget your .prettierrc, if you have one)"
  exit 1
fi

Although I typically use gitea for git hosting, I've tested this particular hook with a simple automated git deploy with ssh, and it should work just as well with GitLab, Gogs, and the like.

More Info

I offer a little more detail in the blog post I wrote on the matter:

Even more...

You may also benefit from some of these other resources:

Upvotes: 3

VonC
VonC

Reputation: 1328562

On the client side, you can look at wangkuiyi/7379a242f0d4089eaa75 which is Git pre-commit hook that invokes clang-format to reformat C/C++/Objective-C source code.

That gives you an idea on how to write a server-side pre-receive hook, which will do the same and compare the resulting content with the original content.
See more at "Git 'pre-receive' hook and 'git-clang-format' script to reliably reject pushes that violate code style conventions"

Upvotes: 3

Related Questions