diff --git a/public/_data.json b/public/_data.json index 0281173..10343ac 100644 --- a/public/_data.json +++ b/public/_data.json @@ -19,6 +19,17 @@ "title": "Curriculum vitae" }, "posts": [ + { + "title": "A git pre-commit hook for iOS", + "date": "4th August, 2016", + "timestamp": 1470328683, + "tags": [ + "ios", + "git" + ], + "author": "Sami Samhuri", + "url": "/posts/2016/08/ios-git-pre-commit-hook" + }, { "title": "Tales of PRK Laser Eye Surgery", "date": "12th April, 2016", @@ -116,17 +127,6 @@ "author": "Sami Samhuri", "url": "/posts/2015/06/the-unofficial-guide-to-xcconfig-files", "link": "http://pewpewthespells.com/blog/xcconfig_guide.html?utm_campaign=iOS%2BDev%2BWeekly&utm_source=iOS_Dev_Weekly_Issue_200" - }, - { - "title": "→ GitHub Flow Like a Pro", - "date": "28th May, 2015", - "timestamp": 1432824147, - "tags": [ - - ], - "author": "Sami Samhuri", - "url": "/posts/2015/05/github-flow-like-a-pro", - "link": "http://haacked.com/archive/2014/07/28/github-flow-aliases/" } ] } \ No newline at end of file diff --git a/public/images/show-me-the-money.gif b/public/images/show-me-the-money.gif new file mode 100644 index 0000000..147e533 Binary files /dev/null and b/public/images/show-me-the-money.gif differ diff --git a/public/posts/2016/08/_data.json b/public/posts/2016/08/_data.json new file mode 100644 index 0000000..42c893f --- /dev/null +++ b/public/posts/2016/08/_data.json @@ -0,0 +1,15 @@ +{ + "ios-git-pre-commit-hook": { + "id": "ios-git-pre-commit-hook", + "author": "Sami Samhuri", + "title": "A git pre-commit hook for iOS", + "date": "4th August, 2016", + "timestamp": 1470328683, + "link": null, + "url": "/posts/2016/08/ios-git-pre-commit-hook", + "tags": [ + "ios", + "git" + ] + } +} \ No newline at end of file diff --git a/public/posts/2016/08/index.ejs b/public/posts/2016/08/index.ejs new file mode 100644 index 0000000..72c8d6b --- /dev/null +++ b/public/posts/2016/08/index.ejs @@ -0,0 +1 @@ +<%- partial('../../_month') %> diff --git a/public/posts/2016/08/ios-git-pre-commit-hook.md b/public/posts/2016/08/ios-git-pre-commit-hook.md new file mode 100644 index 0000000..f93d9ae --- /dev/null +++ b/public/posts/2016/08/ios-git-pre-commit-hook.md @@ -0,0 +1,129 @@ +[Krzysztof Zabłocki][kztwitter] wrote [a nice article on using a git pre-commit hook to catch mistakes in iOS projects][link] before you push those mistakes out to the whole team/world. It's a great idea! But the shell script has some problems, so let's fix those. + +If you don't care what I did, why, or how, then you can just [see the updated script][gist]. + +[kztwitter]: https://twitter.com/merowing_ +[link]: http://merowing.info/2016/08/setting-up-pre-commit-hook-for-ios/ +[gist]: https://gist.github.com/samsonjs/3c24c0c7b333f209bc5fcab0d8390c01 + +## Repeated code + +The diff command is repeated. This is any easy win: + + diff-index() { + git diff-index -p -M --cached HEAD -- "$@" + } + + if diff-index '*Tests.swift' | ... + +You get the idea. + +## Portability + +One problem is that the bootstrap script uses an absolute path when creating a symlink to the pre-commit script. That's no good because then your pre-commit hook breaks if you move your project somewhere else. + +That's easily fixed by using a relative path to your pre-commit hook, like so: + + ln -s ../../scripts/pre-commit.sh .git/hooks/pre-commit + +Ah, this is more flexible! Of course if you ever move the script itself then it's on you to update the symlink and bootstrap.sh, but that was already the case anyway. + +## Show me the errors + +Ok great so this script tells me there are errors. Well, script, what exactly _are_ those errors? + +

Show me the money! –Cuba Gooding Jr. in Jerry Maguire

+ +First ignore the fact I'm talking to a shell script. I don't get out much. Anyway... now we need to pull out the regular expressions and globs so we can reuse them to show what the actual errors are if we find any. + + test_pattern='\b(fdescribe|fit|fcontext|xdescribe|xit|xcontext)\b' + test_glob=('*Tests.swift' '*Specs.swift') + if diff-index $test_glob | grep '^+' | egrep "$test_pattern" >/dev/null 2>&1 + ... + +and + + misplaced_pattern='misplaced="YES"' + misplaced_glob=('*.xib' '*.storyboard') + if diff-index $misplaced_glob | grep '^+' | egrep "$misplaced_pattern" >/dev/null 2>&1 + ... + +You may notice that I snuck in `*Specs.swift` as well. Let's not be choosy about file naming. + +Then we need to show where the errors are by using `git grep`, with an `|| true` at the end because the whole script fails if any single command fails, and `git grep` regularly exits with non-zero status (I didn't look into why that is). + + echo "COMMIT REJECTED for fdescribe/fit/fcontext/xdescribe/xit/xcontext." >&2 + echo "Remove focused and disabled tests before committing." >&2 + git grep -E "$test_pattern" $test_glob || true >&2 + echo '----' >&2 + +end for misplaced views: + + echo "COMMIT REJECTED for misplaced views. Correct them before committing." >&2 + git grep -E "$misplaced_pattern" $misplaced_glob || true >&2 + echo '----' >&2 + +## Fix all the things, at once + +The third problem is that if there are any focused or disabled tests you won't be told about any misplaced views until you try to commit again. I want to see all the errors on my first attempt to commit, and then fix them all in one fell swoop. + +The first step is to exit at the end using a code in a variable that is set to 1 when errors are found, so we always run through both branches even when the first has errors. + +Up top: + + failed=0 + +In the middle, where we detect errors: + + failed=1 + +And at the bottom: + + exit $failed + +That's all there is to it. If we don't exit early then all the code runs. + +## General Unixy goodness + +Error output should be directed to stderr, not stdout. I littered a bunch of `>&2`s around to rectify that situation. + +## Final countdown + +Those were all the obvious improvements in my mind and now I'm using this modified version in my project. If you come up with any more nice additions or changes please share! [Fork this gist][gist]. + +Here's the whole thing put together: + + #!/usr/bin/env bash + # + # Based on http://merowing.info/2016/08/setting-up-pre-commit-hook-for-ios/ + + set -eu + + diff-index() { + git diff-index -p -M --cached HEAD -- "$@" + } + + failed=0 + + test_pattern='\b(fdescribe|fit|fcontext|xdescribe|xit|xcontext)\b' + test_glob=('*Tests.swift' '*Specs.swift') + if diff-index $test_glob | grep '^+' | egrep "$test_pattern" >/dev/null 2>&1 + then + echo "COMMIT REJECTED for fdescribe/fit/fcontext/xdescribe/xit/xcontext." >&2 + echo "Remove focused and disabled tests before committing." >&2 + git grep -E "$test_pattern" $test_glob || true >&2 + echo '----' >&2 + failed=1 + fi + + misplaced_pattern='misplaced="YES"' + misplaced_glob=('*.xib' '*.storyboard') + if diff-index $misplaced_glob | grep '^+' | egrep "$misplaced_pattern" >/dev/null 2>&1 + then + echo "COMMIT REJECTED for misplaced views. Correct them before committing." >&2 + git grep -E "$misplaced_pattern" $misplaced_glob || true >&2 + echo '----' >&2 + failed=1 + fi + + exit $failed