A tale of Sourcetree and Git hooks

posted by 6 years ago and updated 5 years ago

At Under the Coco Tree we constantly update the code of different projects. This summer we took a plunge into automating the process of testing and deployment. Yes it took time but it was worth implementing continues integration.

We starting using PHPCI but the tests and code quality scans would take place after the commit was done. We had to correct the warnings and errors and create a new commit.

This is a repetitive task and through time it takes a lot of time. We could have built a script to run every time but that would have to be manual. Since we use git, we thought, there must be a way to perform a series of commands before we commit without thinking about it.

Git Hooks

A duckduckgo search later and Git hooks came to the surface. We knew about Web Hooks, but hey this is new for us. Hooks are used to fire a certain scrip after an important action has occurred. Such as:

  • Commit
  • Push
  • Rebase
  • Merge

Our scripts can run pre or post the action. For example, we wanted to run our code quality control tests before we committed the change. So we had to use the pre-commit hook.

Where do these hooks hide?

Pleased you asked, setting a hook is not as hard as it might sound. The directory to look into is .git/hooks/. Git comes with a few hooks installed but they are all dormant by having a .sample extension. To start using any of them, simply copy or rename the file to the hook name required.

cp pre-commit.sample pre-commit

If you create one from scratch make sure to make it an executable. Remember that these hooks are not part of your repository and won’t be going upstream to your repo.

If you want to have them as part of your repo you need to add them into your repo and then have an installation script to copy them on a local environment.

How to set up a hook script

A hook will execute as long as the script running returns a clean exit code. This is how we set up our .git/hooks/pre-commit script.

source ~/.zshrc

# Run code quality and tests against the commit before we commit

# If a command fails, exit and return that error 
set -e

printf "\e[1;33mpre-commit:\e[0m: Php-cs-fixer\n"
vendor/bin/php-cs-fixer fix app --level=psr2
if [ $? -eq 0 ]
  printf "\e[1;32mSuccessful:\e[0m Php-cs-fixer."
  printf "\e[1;31mError:\e[0m Php-cs-fixer"
  exit 1

exit 0

We first set up the script to be a shell script. In our case Zsh shell.

Then we print some information about what is about to happen to the user, we then execute the command .

Then we check the exit code of that command if [ $? -eq 0 ].

$? returns the exit code of the last command.

If the exit code returned an error we stop the execution of our script by using exit 1

We have multiple commands running, at the end of the script we give a clean exit by using exit 0

In case you are wondering what this means "\e[1;31mError:\e[0m, yes it does say Error: but we are using colours to make it easier to read in the terminal. In this case we are using red. At the bottom of this article you can find a link that explains it in more detail.

The problem with Sourcetree

Sourcetree seems to run git under it’s own environment. This created a problem with our script. The famous command not found error. After some hacking around we used source ~/.zshrc on line 2 of our script to inject our environment.

We have commands that are globally installed for a user with composer and npm. These commands were not found but by injection our environment variables our script works both in the terminal and in Sourcetree.

Some other variations to consider depending on your shell of choice: source ~/.bash_profile or source ~/.bashrc

~Want to read more? Follow these links.~

Do you need help? Sometimes it is just easier to ask

In case you need that little extra push with your project. We are always happy to collaborate with new people in the industry.

Contact us for help