Android Gradle 配置 Git Hooks 踩坑记录

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。

相关推荐
Flynt5 小时前
Claude Code 103 秒删掉 4.8 万个文件之后,我把自己的仓库"删"了一遍:git 能救的比你想的少
git·ai编程·claude
csdn2015_7 小时前
git 可以修改已经push上去的commit说明吗
git
MinggeQingchun1 天前
Git - 令牌登录
git
xhy_07071 天前
Git 合并冲突怎么解决?用 AI 处理冲突的流程、Prompt 和 4 个易错点
人工智能·git·安全·prompt·ai编程·代码复审
Zhou1411361 天前
Git_02_GitLab协作与CI_CD
git·ci/cd·gitlab
Zhou1411361 天前
Git_03_GitFlow分支工作流
git
Flynt1 天前
把 bug tracker 塞进 git 仓库,同步和冲突怎么解决?我把 git-bug 拆开实测了一遍
分布式·git·开源
louisgeek2 天前
Git submodule 子模块
git
imDwAaY2 天前
6篇文章讲清楚Git:Git 实用技巧:暂存现场、挑选提交与定位 Bug (5/6)
git·后端