以前看到一些自动化版本号打包的文章。如果您的项目是用 Git 管理的,并且恰巧又是使用 Gradle 编译(应该绝大部分都是这样的了吧?),本文试图找到一种更加优雅的自动版本管理方法。
1 背景
我们都知道,Android 应用的版本管理是依赖 AndroidManifest.xml 中的两个属性:
android:versionCode
- :版本号,是一个大于 0 的整数,相当于 Build Number,随着版本的更新,这个必须是递增的。大的版本号,覆盖更新小的版本号;
android:versionName
- :版本名,是一个字符串,例如
"1.2.0"
- ,这个是给人看的版本名,系统并不关心这个值,但是合理的版本名,对后期的维护和 bug 修复也非常重要。
在使用了 Android Studio 或者 Gradle 编译以后,我们通常是在 build.gradle 里面定义这两个值,如下:
android {
...
defaultConfig {
...
versionCode 1
versionName "1.0"
}
}
2 自动版本号
6 tips to speed up your Gradle build
def cmd = 'git rev-list HEAD --count'
def gitVersion = cmd.execute().text.trim().toInteger()
android {
defaultConfig {
versionCode gitVersion
}
}
git rev-list HEAD --count
,表示获取当前分支的 commit 数量。
@zhangls 提醒。原文直接使用了 6 tips to speed up your Gradle build 中的命令 git rev-list HEAD --first-parent --count,也就是带了 --first-parent 选项。发现使用此选项并不合理,比如 这篇文章 中提到的,first-parent
这是一个绝妙的方案。因为在项目开发中,我们的往 Git 库中提交的 Commit 的数量应该是只增不减的(当然,在极少的情况下有例外),而且对应 Commit 的数量直接对应代码当前的版本状态,只要你做了代码修改,版本号就应该增加。有些解决方案中,每次 Build 就会增加一次版本号,个人感觉并不合适,如果是相同的代码,发布出去版本号应该保持一致,而不在于你编译多少次。
2^31-1
,也就是 21 亿多,Android 源码中,改动最活跃的 framework/base 所有分支到目前为止也就 20 万多个 commit,所以完全够用了。
3 自动版本名
versionCode,现在我们看怎么自动化versionName。
git describe
,它的功能就是获取从当期 commit 到距离它最近的 tag 的描述。默认都是 annoted tag,如果要指所有的类型的 tag 的话,就加 --tags
参数。
git-describe。举例一个简单的例子,假如你的当前代码状态如下:
--A--B-...-C-->
| |
v1.0 v1.1
git describe
的结果是:v1.1
,如果是如下的情况:
--A--B-...-C--D-->
| |
v1.0 v1.1
git describe
的结果是:v1.1-1-gXXXXXX
,其中 1 表示当前代码距离最近的 tag v1.1
一个 commit,最新的 commit 的 id 是 XXXXXX
。describe
def cmd = 'git describe --tags'
def version = cmd.execute().text.trim()
android {
defaultConfig {
versionName version
}
}
v1.1-1-gXXXXXX
,这里也可以稍微做一些修改,使版本号更好看,如下:
def pattern = "-(\\d+)-g"
def matcher = version =~ pattern
if (matcher) {
version = version.substring(0, matcher.start()) + "." + matcher[0][1]
} else {
version = version + ".0"
}
v1.0.0
和 v1.1.1
4 优化
前面的那篇文章中说了,为了尽可能减少 gradle 脚本的运算,提高开发速度,我们可以把这样的自动版本的计算放到 release 编译中去。最后的写法如下:
def gitVersionCode() {
def cmd = 'git rev-list HEAD --first-parent --count'
cmd.execute().text.trim().toInteger()
}
def gitVersionTag() {
def cmd = 'git describe --tags'
def version = cmd.execute().text.trim()
def pattern = "-(\\d+)-g"
def matcher = version =~ pattern
if (matcher) {
version = version.substring(0, matcher.start()) + "." + matcher[0][1]
} else {
version = version + ".0"
}
return version
}
android {
compileSdkVersion 23
buildToolsVersion "23.0.2"
defaultConfig {
applicationId "com.race604.example"
minSdkVersion 15
targetSdkVersion 23
versionCode 1
versionName '1.0'
}
buildTypes {
debug {
// 为了不和 release 版本冲突
applicationIdSuffix ".debug"
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
applicationVariants.all { variant ->
if (variant.buildType.name.equals('release')) {
variant.mergedFlavor.versionCode = gitVersionCode()
variant.mergedFlavor.versionName = gitVersionTag()
}
}
}
至此,结合 git 和 gradle 我们就实现了自动版本号。