Have you ever wondered if you could launch a git hook that triggers a certain script that could help you do some task?. Me too!. Well, not really, until I needed it 😀
In my case, I work with Xcode (iOS dev here, indeed) and it’s well known between us, iOS developers, that Xcode doesn’t provide a IDE with an autocorrect functionality, like others have. We do use a tool called “SwiftLint” wich triggers a lint in our project to enforce Swift style and conventions. And it also provides a “Autocorrect” command.
Problem is, that I wanted to autocorrect automatically, without me needing to run swiftlint autocorrect
everytime in terminal, since I tend to end up forgetting to do this kind of task manually.
When you’re working on a big project with multiple devs at the same time, you need to improve somehow the way at least how autocorrect is triggered. I started learning about git hooks, and they seem very handy to do this kind of work. Ok, so, I started to think about steps on what I wanted and how could I achieve it:
- Run swiftlint autocorrect only in staged files, so I wouldn’t add more warnings to the project.
- Since we use danger to pint point this missing lints in our PR’s, I wanted to skip this behaviour because it added a lot of noise and as human do, we’ll get used to the warnings and end up not fixing it :shruggs:
Both of the steps above ended up in this final idea:
When editing or creating files, run swiflint autocorrect after a commit is made over these files.
So, I ended creating the next files:
1.- The bootstrap file which will copy the hook file inside .git/hooks/pre-commit path. I’ve called this file “bootstrap_hook.sh”
#!/usr/bin/env bash
# Usage: scripts/autocorrect
set -eu
echo "Configuring pre-commit hook..."
file="../.git/hooks/pre-commit"
path=".git/hooks"
echo $(pwd)
rm -f $file
cp -f pre-commit $file
echo "Done coping pre-hook in $file"
2. And the code that will evaluate the files that are only in staging mode, so after you do a commit, these files will be autocorrected. If there’s anything to correct, then a new commit will be shown with these changes.
#!/bin/bash
echo "post-commit started"
# Run SwiftLint
START_DATE=$(date +"%s")
SWIFT_LINT=/usr/local/bin/swiftlint
# Run SwiftLint for given filename
run_swiftlint() {
local filename="${1}"
echo "File is: ${filename}"
echo "File with previous extension is: ${filename#*.}"
if [[ "${filename##*.}" == "swift" ]]; then
if [[ "${filename#*.}" != "generated.swift" ]]; then
echo "Autocorrecting..."
${SWIFT_LINT} autocorrect --path "${filename}"
${SWIFT_LINT} lint --path "${filename}"
fi
fi
}
if [[ -e "${SWIFT_LINT}" ]]; then
echo "SwiftLint version: $(${SWIFT_LINT} version)"
# Run only if not merging
if ! git rev-parse -q --verify MERGE_HEAD; then
# Run for both staged and unstaged files
echo "Enter merging"
git diff --name-only | while read filename; do run_swiftlint "${filename}"; done
git diff --cached --name-only | while read filename; do run_swiftlint "${filename}"; done
fi
else
echo "${SWIFT_LINT} is not installed. Please install it first from https://www.github.com/realm/SwiftLint"
exit 0
fi
END_DATE=$(date +"%s")
DIFF=$(($END_DATE - $START_DATE))
echo "SwiftLint took $(($DIFF / 60)) minutes and $(($DIFF % 60)) seconds to complete."
echo "post-commit finished"
Now, to use it, the only thing you have to do is to run the bootstrap file and let git do his magic:
To run it:
$ sh bootstrap_hook.sh
I have copied these files in my github account if you want to clone it, fork it or pull request it 😀 https://github.com/phynet/Pre-Hook-Swiftlint-Autocorrect