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.