Blog

Local Continuous Integration Setup With Git Post-Commit Hook Script

I have lots unit tests, but I don’t have a Continuous Integration server setup, and I sometimes forget my tests are there.

I know. Bad me. I was up late last night getting some failing unit tests to pass again, after forgetting I even had unit tests. Ugh. This would have been much easier if I knew I’d broken a test when I broke it; as it was, I had to go back and try to remember what I was working on when they broke!

So, to stop that happening in the future, I fiddled around with my local repository and whipped up a script that automatically runs tests in the background, on a separate temporary cloned version of the repository.

If build or tests fail, I get a nice little Notification Center message which I can click to see a report and build log. Then I can fix it and amend the commit as necessary.

It’s a script that’s invoked by a Post-Commit git hook, and it’s run in the background using nohup so it doesn’t make me wait and mess with my workflow. It just all happens transparently in the background.

Here’s how I did it.

I opened up the .git/hooks folder of my workspace repo, and created a new post-commit file, executable, with the following:

#!/bin/bash
SCRIPT_DIR=$(dirname "$0")
SCRIPT="$SCRIPT_DIR/run-unit-tests.sh"
 
# Stop any currently-running versions
pkill -f "$SCRIPT"
 
# Launch script into background
nohup "$SCRIPT_DIR/run-unit-tests.sh" &>/tmp/run-unit-tests-hook &

This calls another script called run-unit-tests.sh in the same folder.

Then I created run-unit-tests.sh, with the following:

#!/bin/sh
# Run post-commit unit tests
#
#   Requires:
#       git
#       xcodebuild
#       terminal-notifier
#       xcpretty
 
SCHEME="Loopy Masterpiece"
WORKSPACE="Loopy Masterpiece.xcworkspace"
PATH="/usr/local/bin:$PATH"
REPOSITORY_ROOT="$PWD"
TEMP_FOLDER_PREFIX="/tmp/run-unit-tests"
TEMP_FOLDER="$TEMP_FOLDER_PREFIX-$$"
PLATFORM="platform=iOS Simulator,name=iPhone 7"
 
unset GIT_WORK_TREE
unset GIT_DIR
 
# Clean up old versions
rm -rf $TEMP_FOLDER_PREFIX*
 
terminal-notifier -title 'CI Post-Hook Script' -message 'Running tests'
 
# Clone repository
git clone --depth 1 "file://$REPOSITORY_ROOT" "$TEMP_FOLDER"
if ! cd "$TEMP_FOLDER"; then
    terminal-notifier -title 'CI Post-Hook Script' -message 'Project clone failed'
    exit 1
fi
 
# Run build and tests, and evaluate result
if ! (xcodebuild -workspace "$WORKSPACE" -scheme "$SCHEME" -destination "$PLATFORM" build test 2>&1 | tee xcodebuild.log | xcpretty --no-color --report html &> xcodebuild-xcpretty.log) || grep -q '\*\* BUILD FAILED \*\*' xcodebuild.log; then
    terminal-notifier -title 'CI Post-Hook Script' -message 'Build failed' -execute "open $TEMP_FOLDER/xcodebuild-xcpretty.log; sleep 5; rm -rf $TEMP_FOLDER"
 
elif grep -q '\*\* TEST FAILED \*\*' xcodebuild.log; then
    terminal-notifier -title 'CI Post-Hook Script' -message 'Test failed' -execute "open $TEMP_FOLDER/build/reports/tests.html; open $TEMP_FOLDER/xcodebuild-xcpretty.log; sleep 5; rm -rf $TEMP_FOLDER"
 
elif grep -v "building for iOS simulator, but linking against dylib" xcodebuild.log | grep -v "directory not found for option" | grep -qi 'warning:'; then
    terminal-notifier -title 'CI Post-Hook Script' -message 'Warnings in build' -execute "open $TEMP_FOLDER/xcodebuild-xcpretty.log; sleep 5; rm -rf $TEMP_FOLDER"
 
else
    terminal-notifier -title 'CI Post-Hook Script' -message 'Tests completed successfully'
    rm -rf "$TEMP_FOLDER"
fi

Obviously, if you were to use this yourself you’d replace the values for SCHEME and WORKSPACE.

I made sure both scripts were executable, installed xcpretty and terminal-notifier on my machine, and voila: Continuous Integration is mine, and can be yours for the cheap cheap price of free.

, , . Bookmark the permalink. Both comments and trackbacks are currently closed.