Automate All-The-Things with: Git Hooks

2 minute read

If you had the chance to work with some colleagues/friends on some projects, you already know that automate some boring tasks you have to do quite often is really important.

Everything started from a real conversation I had the past week:

Me: I updated the ad-hoc distribution profile. Please download it from the iOS Provisioning Portal and install it on your machine

My colleague: WAT?

This sounds like something just one of us should do and the others should JUST receive it without doing anything manually.

We use GIT to manager all our code base so all we want to do is JUST run git pull on our local repository. Which tool can we use to achieve this?

Git Hooks

Hooks are little scripts you can place in your project $GIT_DIR/hooks directory to trigger action at certain points.

This is exactly what we need! Each time we run the git pull command (or git merge) we should looking for changes related to provisioning profiles. If there are, install them all.

So let’s do it!

Security note: we keep all our iOS provisioning profiles related to a specific app inside the same repository. To be safe, we encrypt them all using git-encrypt. Take a look at it if you want to do the same!

To keep these hook files out of your app code, I created a git-hooks folder in the project root directory and save them there.

# git-hooks/install-profiles.sh
XCODE_PROFILES_PATH="$HOME/Library/MobileDevice/Provisioning Profiles"

prepare_folder()
{
  mkdir -p "${XCODE_PROFILES_PATH}"
  return 0
}

install_profiles()
{
  for file in ${1}/*.mobileprovision
  do
    [ -f "$file" ] || break
    
    UUID=`grep -aA1 UUID ${file}| grep -o "[-a-z0-9]\{36\}"`
    cp "${file}" "${XCODE_PROFILES_PATH}/${UUID}.mobileprovision"
    if [ $? -ne 0 ]; then
      echo "ERROR: Unable to copy mobileprovision file to its destination"
      return 1
    fi
  done
  return 0
}

prepare_folder
install_profiles "./Certificates/development"
install_profiles "./Certificates/distribution"

Now we need to run this script each time we do a git pull or git merge. So we prepared a post-merge.sh and post-checkout.sh scripts. Both are exactly the same

# git-hooks/post-merge.sh, git-hooks/post-checkout.sh
changed_files="$(git diff-tree -r --name-only --no-commit-id ORIG_HEAD HEAD)"
check_run()
{
  echo "$changed_files" | grep --quiet "$1" && eval "$2"
}
    
check_run "^Certificates/.*\.mobileprovision$" "$BASEDIR/install-profiles.sh"

Last thing to do is to link post-merge.sh (and post-checkout.sh scripts inside the project .git/hooks folder. A simple setup script will do this

# setup.sh
echo "Configuring git hooks..."

# Make scripts executable
chmod +x git-hooks/*.sh

# make a symbolic link with for the hooks
if [ ! -f .git/hooks/post-checkout ]; then
  ln git-hooks/post-checkout.sh .git/hooks/post-checkout
  echo "post-checkout hook created."
else
  echo "A post-checkout hook exists already."
fi

if [ ! -f .git/hooks/post-merge ]; then
  ln git-hooks/post-merge.sh .git/hooks/post-merge
  echo "post-merge hook created."
else
  echo "A post-merge hook exists already."
fi

And finally, run setup.sh. It’s safe to execute it multiple times but one is enough.

Now you are ready to roll! Each time you do a git checkout, git pull or git merge, these scripts will install each .mobileprovision files available.

You can do so much more of course, it’s up to you how much further to go.

Have fun with git hooks!

Alessandro