Gradle脚本自动版本控制并在Android中包含提交哈希值

73

我需要编写一个Gradle脚本,以便在每次提交时自动对我的应用程序进行版本控制。 我还需要在应用程序中包含提交哈希作为测试人员的参考。

我不太清楚自动版本控制通常是如何工作的。 有人可以解释一下自动版本控制的过程吗?

6个回答

165

我曾经遇到过类似的问题,但不想修改versionName来包含git哈希值。我们想保持它像1.2.2那样,但仍然有可能在UI中显示git哈希值。

我从这里的其他答案修改了代码,使用buildConfigField任务来生成一个BuildConfig.GitHash值,该值可以在Java代码中引用。

将以下内容添加到模块的build.gradle文件的android部分之前:

def getGitHash = { ->
    def stdout = new ByteArrayOutputStream()
    exec {
        commandLine 'git', 'rev-parse', '--short', 'HEAD'
        standardOutput = stdout
    }
    return stdout.toString().trim()
}

然后在 build.gradle 文件中 android 部分的 defaultConfig 部分中添加以下行,即在 versionName 下面:

buildConfigField "String", "GitHash", "\"${getGitHash()}\""

这将在自动生成的BuildConfig.java文件中生成以下行:

// Fields from default config.
public static final String GitHash = "e61af97";

现在您可以在Java代码中使用BuildConfig.GitHash获取git哈希。


嗨,我遇到了这个错误:CreateProcess error=2,系统找不到指定的文件,有任何线索吗?我的代码库在GitLabs上。 - Haroon
@Haroon:请检查git的路径是否在你的PATH环境变量中。可以通过打开新的命令提示符/终端并输入“git --version”来检查。 - Paul
谢谢@Paul,你所写的正是我所做的,只是将git可用于环境变量。 - Haroon
问题在于,在进行 git 提交之后,整个应用程序都需要重新构建。 - Dmitry Ryadnenko

22

一个理想的解决方案是从项目的git状态中获取版本号。这样,版本控制不依赖于您记得要增加变量或更改gradle或配置文件中的任何文本。另一个优点是将版本名称和代码追溯到一个特定的代码状态。

http://ryanharter.com/blog/2013/07/30/automatic-versioning-with-git-and-gradle/中可以找到一个描述性的例子。

该思路是使用getVersionName函数获取git信息,并在gradle脚本中使用该函数:

/*
 * Gets the version name from the latest Git tag
 */
def getVersionName = { ->
    def stdout = new ByteArrayOutputStream()
    exec {
        commandLine 'git', 'describe', '--tags'
        standardOutput = stdout
    }
    return stdout.toString().trim()
}

kts:

val gitDescribe: String by lazy {
    val stdout = ByteArrayOutputStream()
    rootProject.exec {
        commandLine("git", "describe", "--tags")
        standardOutput = stdout
    }
    stdout.toString().trim()
    /* 'g' doesn't belong to the commit id and stands for 'git'
       v0.1.9-1-g3a259e0 -> v0.1.9-1-3a259e0
       if you like this to be removed then */
    //.replace("-g", "-") 
}

当然,你需要使用命令行 git(因为要执行命令 git describe --tags 以生成信息)。
另一种方法(也是基于从 git 获取版本信息)可以将该逻辑外部化为 Gradle 插件,如: 使用哪个取决于你想应用的版本控制策略。

2
非常好的想法,我会添加Kotlin的对应项以提高舒适度。 - elect

20

将以下代码添加到你的build.gradle文件中


def gitCommitHash = 'git rev-parse --verify --short HEAD'.execute().text.trim()
defaultConfig{
    ... otherConfigs
    buildConfigField("String", "GIT_HASH", "\"${gitCommitHash}\"")
}

现在您可以通过BuildConfig.GIT_HASH获取git哈希值。

玩得开心!


感谢这个回答的简洁明了。谢谢 :) - Victor Ude
感谢。最简短和最直接的答案,因此最适合我的情况。 - HX_unbanned

10
我为您创建了一个Gradle插件,可以帮助您完成这个任务。该项目的完整说明和指示请参见https://github.com/lessthanoptimal/gversion-plugin
要使用它,请将以下内容添加到您的build.gradle文件中。
plugins {
  id "com.peterabeles.gversion" version "1.2.4"
}

gversion {
  srcDir = "src/main/java/"
  classPackage = "com.your.package"
  className = "MyVersion"                   // optional. If not specified GVersion is used
  dateFormat   = "yyyy-MM-dd'T'HH:mm:ss'Z'" // optional. This is the default
  timeZone     = "UTC"                      // optional. UTC is default
}

现在,您只需要运行gradle任务“createVersionFile”来创建文件。您可能希望考虑将以下行添加到gradle项目中:project.compileJava.dependsOn(createVersionFile) 这将使它在Gradle构建项目时每次生成该文件。有关Android说明,请参见上面的网站。
下面是该文件的外观。
/**
 * Automatically generated file containing build version information.
 */
public class MyVersion {
    public static final String MAVEN_GROUP = "com.your";
    public static final String MAVEN_NAME = "project_name";
    public static final String VERSION = "1.0-SNAPSHOT";
    public static final int GIT_REVISION = 56;
    public static final String GIT_SHA = "a0e41dd1a068d184009227083fa6ae276ef1846a";
    public static final String BUILD_DATE = "2018-04-11T12:19:03Z";
    public static final long BUILD_UNIX_TIME = 1523449143116L;
}

您可能还希望将版本文件添加到您的.gitignore中,因为它是自动生成的,您不希望在git中出现。


太棒了!我使用反射来避免视觉警告和错误,但这只是我的个人偏好。如果这个工具箱还能自带git短版本号以及资源文件生成而不是类生成,那就更好了。 - edin-m

3

值得一提的是grgit - Groovy/Gradle Git,它可以帮助简化在Gradle脚本中提取信息(包括Git提交哈希)的过程。


这很有帮助,但是在一个单一代码库中使用此插件时出现了内存问题,并且传递依赖的 jgit 与另一个项目发生了冲突。 - 030

1
这是一个Kotlin DSL(build.gradle.kts)的解决方案,源自于Charlie Lee的答案
task<Exec>("MyTask") {
    doLast {
        commandLine("git")
            .args("rev-parse", "--verify", "--short", "HEAD")
            .workingDir(rootProject.projectDir)
    }
}

另一种方法是使用Java标准的ProcessBuilder API:
tasks.create("MyTask") {
    val command = "git rev-parse --verify --short HEAD"
    doLast {
        val process = ProcessBuilder()
            .command(command.split(" "))
            .directory(rootProject.projectDir)
            .redirectOutput(Redirect.INHERIT)
            .redirectError(Redirect.INHERIT)
            .start()
        process.waitFor(60, TimeUnit.SECONDS)
        val result = process.inputStream.bufferedReader().readText()
        println(result)
    }
}

更多信息请参见:

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接