This guide outlines how to set up Git hooks to automatically run code linters and formatters before commits and pushes in your Android project. This setup uses:
- Spotless for code formatting.
- ktlint for Kotlin style checks.
- Detekt for static code analysis.
Your project should include:
- A
scripts
folder containingpre-commit.sh
,pre-push.sh
, andgit-hooks.gradle.kts
. - The
git-hooks.gradle.kts
script to automate copying and installing Git hooks.
`project-root/
β
βββ scripts/
β βββ pre-commit.sh
β βββ pre-push.sh
β βββ build/
β βββ git-hooks.gradle.kts
β
βββ build.gradle.kts`
Create the following scripts in the scripts
folder.
File: scripts/pre-commit.sh
#!/bin/sh
# Function to check the current branch
check_current_branch() {
echo "\nπ Checking the current git branch..."
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
if [ "$CURRENT_BRANCH" = "main" ] || [ "$CURRENT_BRANCH" = "develop" ]; then
echo "π Hold it right there! Committing directly to the '$CURRENT_BRANCH' branch? That's a big no-no!"
echo "π« Direct commits to '$CURRENT_BRANCH' are like trying to use a wrench to write codeβdoesn't work! π"
echo "\nABORTING COMMIT: You must navigate to a feature branch or create a new one to save the day! π¦ΈββοΈπ¦ΈββοΈ\n"
exit 1
else
echo "β
Fantastic! You're on the '$CURRENT_BRANCH' branch, which is perfect for commits. Let's keep this awesome momentum going! πβ¨"
fi
}
# Function to run ktlint checks
run_ktlint_checks() {
echo "\nπ Brace yourself! We're about to embark on a journey of code analysis and style checking with ktlint!"
./gradlew ktlintCheck --daemon > /tmp/ktlint-result
KT_EXIT_CODE=$?
if [ ${KT_EXIT_CODE} -ne 0 ]; then
cat /tmp/ktlint-result
rm /tmp/ktlint-result
echo "\n*********************************************************************************"
echo " π₯ Oh no! ktlint found style issues in the code! Time to fix those gremlins! π₯"
echo " π‘ Tip: You might need your Kotlin ninja skills to resolve these issues. π οΈ"
echo "*********************************************************************************\n"
exit ${KT_EXIT_CODE}
else
rm /tmp/ktlint-result
echo "π Bravo! Your Kotlin code has passed ktlint's rigorous style checks with flying colors! Keep rocking that clean code! ππ«"
fi
}
# Function to run Spotless checks
run_spotless_checks() {
echo "\nπ Spotless is now analyzing and formatting your code!"
./gradlew spotlessApply --daemon > /tmp/spotless-result
SPOTLESS_EXIT_CODE=$?
if [ ${SPOTLESS_EXIT_CODE} -ne 0 ]; then
cat /tmp/spotless-result
rm /tmp/spotless-result
echo "\n*********************************************************************************"
echo " π₯ Uh-oh! Spotless found formatting issues in the code! Time to tidy up! π₯"
echo " π‘ Tip: Check the reported issues and fix formatting errors. π οΈ"
echo "*********************************************************************************"
exit ${SPOTLESS_EXIT_CODE}
else
rm /tmp/spotless-result
echo "π Stellar job! Your code is pristine and has passed Spotless's formatting checks without a hitch! Keep shining bright! β¨π"
fi
}
# Function to run Detekt checks
run_detekt_checks() {
echo "\nπ Detekt is now analyzing your Kotlin code for potential issues!"
./gradlew detekt > /tmp/detekt-result
DETEKT_EXIT_CODE=$?
if [ ${DETEKT_EXIT_CODE} -ne 0 ]; then
cat /tmp/detekt-result
rm /tmp/detekt-result
echo "\n*********************************************************************************"
echo " π₯ Oh no! Detekt found issues in the code! Time to fix those issues! π₯"
echo " π‘ Tip: Review the Detekt report to resolve these issues. π οΈ"
echo "*********************************************************************************"
exit ${DETEKT_EXIT_CODE}
else
rm /tmp/detekt-result
echo "π Fantastic work! Your Kotlin code has sailed through Detekt's analysis with ease! Onward to greatness! ππ"
fi
}
# Function to print success message
print_success_message() {
GIT_USERNAME=$(git config user.name)
echo "\n *******************************************************************************"
echo "ππ Huzzah, $GIT_USERNAME! Your code has triumphed through the Style Checker Dragon unscathed! π"
echo "Your code shines brighter than a supernova and sparkles like a constellation of stars! β¨π"
echo "*******************************************************************************"
echo "\nππ Hold tight, $GIT_USERNAME! Your code is ready to commit and conquer new heights! πβ¨ Keep up the amazing work! πͺ\n"
}
# Main script execution
check_current_branch
run_ktlint_checks
run_spotless_checks
run_detekt_checks
print_success_message
exit 0
File: scripts/pre-push.sh
#!/bin/sh
# Function to check the current branch
check_current_branch() {
echo "\nπ Checking the current git branch..."
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
if [ "$CURRENT_BRANCH" = "main" ] || [ "$CURRENT_BRANCH" = "develop" ]; then
echo "π Hold it right there! Committing directly to the '$CURRENT_BRANCH' branch? That's a big no-no!"
echo "π« Direct commits to '$CURRENT_BRANCH' are like trying to use a wrench to write codeβdoesn't work! π"
echo "\nABORTING COMMIT: You must navigate to a feature branch or create a new one to save the day! π¦ΈββοΈπ¦ΈββοΈ\n"
exit 1
else
echo "β
Fantastic! You're on the '$CURRENT_BRANCH' branch, which is perfect for commits. Let's keep this awesome momentum going! πβ¨"
fi
}
# Function to run ktlint checks
run_ktlint_checks() {
echo "\nπ Brace yourself! We're about to embark on a journey of code analysis and style checking with ktlint!"
./gradlew ktlintCheck --daemon > /tmp/ktlint-result
KT_EXIT_CODE=$?
if [ ${KT_EXIT_CODE} -ne 0 ]; then
cat /tmp/ktlint-result
rm /tmp/ktlint-result
echo "\n*********************************************************************************"
echo " π₯ Oh no! ktlint found style issues in the code! Time to fix those gremlins! π₯"
echo " π‘ Tip: You might need your Kotlin ninja skills to resolve these issues. π οΈ"
echo "*********************************************************************************\n"
exit ${KT_EXIT_CODE}
else
rm /tmp/ktlint-result
echo "π Bravo! Your Kotlin code has passed ktlint's rigorous style checks with flying colors! Keep rocking that clean code! ππ«"
fi
}
# Function to run Spotless checks
run_spotless_checks() {
echo "\nπ Spotless is now analyzing and formatting your code!"
./gradlew spotlessApply --daemon > /tmp/spotless-result
SPOTLESS_EXIT_CODE=$?
if [ ${SPOTLESS_EXIT_CODE} -ne 0 ]; then
cat /tmp/spotless-result
rm /tmp/spotless-result
echo "\n*********************************************************************************"
echo " π₯ Uh-oh! Spotless found formatting issues in the code! Time to tidy up! π₯"
echo " π‘ Tip: Check the reported issues and fix formatting errors. π οΈ"
echo "*********************************************************************************"
exit ${SPOTLESS_EXIT_CODE}
else
rm /tmp/spotless-result
echo "π Stellar job! Your code is pristine and has passed Spotless's formatting checks without a hitch! Keep shining bright! β¨π"
fi
}
# Function to run Detekt checks
run_detekt_checks() {
echo "\nπ Detekt is now analyzing your Kotlin code for potential issues!"
./gradlew detekt > /tmp/detekt-result
DETEKT_EXIT_CODE=$?
if [ ${DETEKT_EXIT_CODE} -ne 0 ]; then
cat /tmp/detekt-result
rm /tmp/detekt-result
echo "\n*********************************************************************************"
echo " π₯ Oh no! Detekt found issues in the code! Time to fix those issues! π₯"
echo " π‘ Tip: Review the Detekt report to resolve these issues. π οΈ"
echo "*********************************************************************************"
exit ${DETEKT_EXIT_CODE}
else
rm /tmp/detekt-result
echo "π Fantastic work! Your Kotlin code has sailed through Detekt's analysis with ease! Onward to greatness! ππ"
fi
}
# Function to print success message
print_success_message() {
GIT_USERNAME=$(git config user.name)
echo "\n *******************************************************************************"
echo "ππ Huzzah, $GIT_USERNAME! Your code has triumphed through the Style Checker Dragon unscathed! π"
echo "Your code shines brighter than a supernova and sparkles like a constellation of stars! β¨π"
echo "*******************************************************************************"
echo "\nππ Get ready, $GIT_USERNAME! Your code is about to take flight into the repository cosmos! πβ¨ Fasten your seatbelt for a stellar push! ππ«\n"
}
# Main script execution
check_current_branch
run_ktlint_checks
run_spotless_checks
run_detekt_checks
print_success_message
exit 0
In your scripts/build/git-hooks.gradle.kts
, add tasks which copy these scripts to the .git/hooks
folder and make them executable.
File: scripts/build/git-hooks.gradle.kts
import java.util.Locale
// Define a function to check if the OS is Linux or MacOS
fun isLinuxOrMacOs(): Boolean {
val osName = System.getProperty("os.name").lowercase(Locale.getDefault())
return osName.contains("linux") || osName.contains("mac os") || osName.contains("macos")
}
// Define the copyGitHooks task
tasks.register<Copy>("copyGitHooks") {
description = "Copies the git hooks from /scripts to the .git/hooks folder."
from("$rootDir/scripts/") {
include("**/*.sh")
rename { it.removeSuffix(".sh") }
}
into("$rootDir/.git/hooks")
onlyIf { isLinuxOrMacOs() }
}
// Define the installGitHooks task
tasks.register<Exec>("installGitHooks") {
description = "Installs the pre-commit git hooks from the scripts directory."
group = "git hooks"
workingDir = rootDir
commandLine("chmod", "-R", "+x", ".git/hooks/")
dependsOn(tasks.named("copyGitHooks"))
onlyIf { isLinuxOrMacOs() }
doLast {
logger.info("Git hooks installed successfully.")
}
}
// Configure task dependencies after evaluation
afterEvaluate {
tasks.matching {
it.name in listOf("preBuild", "build", "assembleDebug", "assembleRelease", "installDebug", "installRelease", "clean")
}.configureEach {
dependsOn(tasks.named("installGitHooks"))
}
}
In your app's module level build.gradle.kts
file, ensure you apply the git-hooks.gradle.kts
script.
File: build.gradle.kts
// Apply the git hooks setup script
apply(from = "../scripts/build/git-hooks.gradle.kts")
- Create the Scripts: Create
pre-commit.sh
andpre-push.sh
in thescripts
directory with the contents provided above. - Configure Gradle: Create
git-hooks.gradle.kts
inscripts/build
with the provided content. - Update Main Build File: Ensure the main
build.gradle.kts
applies thegit-hooks.gradle.kts
script. - Sync Project: Sync your project with Gradle files to apply the changes.
- Run the app: This triggers on these events which install the hooks {listOf("preBuild", "build", "assembleDebug", "assembleRelease", "installDebug", "installRelease", "clean")}
- Verify Hooks: Ensure that the hooks are copied to
.git/hooks
and are executable.
- Attempt to commit or push changes. The hooks should automatically run the specified checks and formatters.
- Check the output for success messages or error details.
- The scripts and tasks are designed to work on Linux or MacOS. Adjustments might be needed for Windows.
- Ensure that you have
ktlint
,Spotless
, andDetekt
configured in yourbuild.gradle.kts
files.