Android Gradle 配置 Git Hooks
1. Git Hooks是什么
Git Hooks
是一些自定义脚本,它们与特定git命令关联,如:git commit
、git push
等,允许我们在这些操作前/后调用。
通常为了规范化协作开发,我们会对提交代码做一系列的检查,比如:静态代码扫描、unit test检查、规范commit message格式等。
本文将演示,如何基于gradle脚本透明地配置pre-push
,使得developer push代码时执行静态代码检查和unit test
2. 配置 Git Hooks
2.1 准备pre-push脚本
sh
#!/usr/bin/env bash
function runDetekt() {
echo "Running detekt..."
OUTPUT="/tmp/detekt-$(date +%s)"
./gradlew ./gradlew detekt --auto-correct > $OUTPUT
EXIT_CODE=$?
if [ $EXIT_CODE -ne 0 ]; then
cat $OUTPUT
rm $OUTPUT
echo "***********************************************"
echo " detekt failed "
echo " Please fix the above issues before pushing "
echo "***********************************************"
exit $EXIT_CODE
fi
rm $OUTPUT
}
function runUnitTest() {
echo "Running unit test..."
OUTPUT="/tmp/unit-test-$(date +%s)"
./gradlew app:testDebugUnitTest > $OUTPUT
EXIT_CODE=$?
if [ $EXIT_CODE -ne 0 ]; then
cat $OUTPUT
rm $OUTPUT
echo "***********************************************"
echo " unit test failed "
echo " Please fix the above issues before pushing "
echo "***********************************************"
exit $EXIT_CODE
fi
rm $OUTPUT
}
runDetekt
runUnitTest
2.2 将pre-push拷贝到.git/hooks/
项目文件结构如下:
perl
my-project/
.git/
app/
hooks/
pre-push
...
kotlin
// app/build.kts
project.afterEvaluate {
val fromHooksDir = File(rootProject.projectDir, "hooks")
val toHooksDir = File(rootProject.projectDir, ".git/hooks")
fromHooksDir.listFiles().forEach { file ->
val target = File(toHooksDir, file.name)
file.copyTo(target, overwrite = true)
}
}
run/sync项目的时候,就会把pre-push hook配置好
2.3 权限问题
sh
➜ git push
hint: The '.git/hooks/pre-push' hook was ignored because it's not set as executable.
hint: You can disable this warning with `git config advice.ignoredHook false`.
2.4 通过脚本设置执行权限
我们可以通过chmod +x ${file path}
来修改权限
kotlin
// app/build.gradle.kts
project.afterEvaluate {
val fromHooksDir = File(rootProject.projectDir, "hooks")
val toHooksDir = File(rootProject.projectDir, ".git/hooks")
fromHooksDir.listFiles().forEach { file ->
val target = File(toHooksDir, file.name)
file.copyTo(target, overwrite = true)
// 修改权限
val command = "chmod +x ${target.path}"
Runtime.getRuntime().exec(command).inputStream.use {
String(it.readBytes()).trim()
}
}
}
sync项目后,便可以将pre-push配置好,且有执行权限。
但是,当我们运行项目的时候,会报错:
sh
Configuration cache problems found in this build.
1 problem was found storing the configuration cache.
- Build file 'app/build.gradle.kts': external process started 'chmod +x /Users/xxx/my-project/.git/hooks/pre-push'
See https://docs.gradle.org/8.4/userguide/configuration_cache.html#config_cache:requirements:external_processes
afterEvaluated
不允许开启新进程了,所以chmod +x
操作不能放在这里
2.5 新增installGitHooksTask执行
kotlin
// app/build.gradle.kts
project.afterEvaluate {
// 找个依附的task,这里用了preDebugBuild
tasks.findByPath("preDebugBuild")?.dependsOn("installGitHooksTask")
}
tasks.register("installGitHooksTask") {
doLast {
val fromHooksDir = File(rootProject.projectDir, "hooks")
val toHooksDir = File(rootProject.projectDir, ".git/hooks")
fromHooksDir.listFiles().forEach { file ->
val target = File(toHooksDir, file.name)
file.copyTo(target, overwrite = true)
val command = "chmod +x ${target.path}"
Runtime.getRuntime().exec(command).inputStream.use {
String(it.readBytes()).trim()
}
println("git hook '${file.name}' installed")
}
}
}
还是会报错,
sh
Configuration cache problems found in this build.
1 problem was found storing the configuration cache.
- Task `:app:installGitHooksTask` of type `org.gradle.api.DefaultTask`: cannot serialize Gradle script object references as these are not supported with the configuration cache.
See https://docs.gradle.org/8.4/userguide/configuration_cache.html#config_cache:requirements:disallowed_types
在task doLast块中不能依赖project,最终调整如下:
kotlin
// app/build.gradle.kts
project.afterEvaluate {
// 找个依附的task,这里用了preDebugBuild
tasks.findByPath("preDebugBuild")?.dependsOn("installGitHooksTask")
}
tasks.register("installGitHooksTask") {
val fromHooksDir = File(rootProject.projectDir, "hooks")
val toHooksDir = File(rootProject.projectDir, ".git/hooks")
doLast {
fromHooksDir.listFiles().forEach { file ->
val target = File(toHooksDir, file.name)
file.copyTo(target, overwrite = true)
val command = "chmod +x ${target.path}"
Runtime.getRuntime().exec(command).inputStream.use {
String(it.readBytes()).trim()
}
println("git hook '${file.name}' installed")
}
}
}
3. 总结
本文通过一个案例,演示了基于gradle实现git hook自动配置,大家可以根据实际情况,定制适合自身团队的git hook。