Skip to content

自动版本管理

源:Semantic Versioning | Git Version Plugin

自动化管理应用版本号,集成 Git 信息,实现版本追踪和发布管理。

版本号概念

Android 版本号

versionCode

  • 整数类型
  • 用于版本比较
  • 每次发布递增
  • Google Play 要求

versionName

  • 字符串类型
  • 展示给用户
  • 遵循语义化版本

语义化版本

格式

<major>.<minor>.<patch>[-<label>][+<build>]

1.0.0         - 正式版本
1.0.0-alpha01 - Alpha 版本
1.0.0-beta01  - Beta 版本
1.0.0-rc01    - Release Candidate
1.0.1         - 补丁版本
1.1.0         - 次要版本
2.0.0         - 主要版本

规则

  • MAJOR:不兼容的 API 变更
  • MINOR:向后兼容的功能
  • PATCH:向后兼容的修复

基本版本管理

gradle.properties 配置

gradle.properties

properties
VERSION_NAME=1.0.0
VERSION_CODE=1

build.gradle.kts

kotlin
android {
    defaultConfig {
        versionCode = findProperty("VERSION_CODE").toString().toInt()
        versionName = findProperty("VERSION_NAME").toString()
    }
}

版本号自增

创建脚本

kotlin
// buildSrc/src/main/kotlin/VersionManager.kt
object VersionManager {
    fun getVersionCode(): Int {
        val props = java.util.Properties()
        file("version.properties").inputStream().use { props.load(it) }
        return props.getProperty("VERSION_CODE").toInt()
    }
    
    fun getVersionName(): String {
        val props = java.util.Properties()
        file("version.properties").inputStream().use { props.load(it) }
        return props.getProperty("VERSION_NAME")
    }
    
    fun incrementVersionCode() {
        val props = java.util.Properties()
        val file = file("version.properties")
        file.inputStream().use { props.load(it) }
        
        val versionCode = props.getProperty("VERSION_CODE").toInt() + 1
        props.setProperty("VERSION_CODE", versionCode.toString())
        
        file.outputStream().use { props.store(it, null) }
    }
}

使用

kotlin
android {
    defaultConfig {
        versionCode = VersionManager.getVersionCode()
        versionName = VersionManager.getVersionName()
    }
}

tasks.register("incrementVersion") {
    doLast {
        VersionManager.incrementVersionCode()
    }
}

Git 集成

使用 Git Tag

获取最新 Tag

kotlin
fun getGitTag(): String {
    val process = Runtime.getRuntime().exec("git describe --tags --always")
    val tag = process.inputStream.bufferedReader().readText().trim()
    return if (tag.startsWith("v")) tag.substring(1) else tag
}

android {
    defaultConfig {
        versionName = getGitTag()
    }
}

使用 Provider API(推荐):

kotlin
val gitTag = providers.exec {
    commandLine("git", "describe", "--tags", "--always")
}.standardOutput.asText.map { it.trim() }

android {
    defaultConfig {
        versionName = gitTag.get()
    }
}

Git Commit Hash

kotlin
val gitCommit = providers.exec {
    commandLine("git", "rev-parse", "--short", "HEAD")
}.standardOutput.asText.map { it.trim() }

android {
    defaultConfig {
        buildConfigField("String", "GIT_HASH", "\"${gitCommit.get()}\"")
    }
}

Git Branch

kotlin
val gitBranch = providers.exec {
    commandLine("git", "branch", "--show-current")
}.standardOutput.asText.map { it.trim() }

android {
    buildTypes {
        debug {
            versionNameSuffix = "-${gitBranch.get()}"
        }
    }
}

自动化版本号

基于 Git Commits

kotlin
fun getVersionCode(): Int {
    val process = Runtime.getRuntime().exec("git rev-list --count HEAD")
    return process.inputStream.bufferedReader().readText().trim().toInt()
}

android {
    defaultConfig {
        versionCode = getVersionCode()
    }
}

基于 Git Tags

kotlin
fun parseVersion(): Pair<Int, String> {
    val tag = providers.exec {
        commandLine("git", "describe", "--tags", "--abbrev=0")
    }.standardOutput.asText.get().trim()
    
    val versionName = tag.removePrefix("v")
    val parts = versionName.split(".")
    val versionCode = parts[0].toInt() * 10000 + parts[1].toInt() * 100 + parts[2].toInt()
    
    return versionCode to versionName
}

val (versionCode, versionName) = parseVersion()

android {
    defaultConfig {
        this.versionCode = versionCode
        this.versionName = versionName
    }
}

插件方案

Git Version Plugin

添加插件

kotlin
plugins {
    id("com.gladed.androidgitversion") version "0.4.14"
}

androidGitVersion {
    codeFormat = "MNNPPPP"  // Major, Minor, Patch
    format = "%tag%"
}

android {
    defaultConfig {
        versionCode = androidGitVersion.code()
        versionName = androidGitVersion.name()
    }
}

Semantic Versioning Plugin

kotlin
plugins {
    id("net.nemerosa.versioning") version "3.0.0"
}

versioning {
    versionPrefix = "v"
}

android {
    defaultConfig {
        versionName = versioning.info.display
    }
}

BuildConfig 集成

添加构建信息

kotlin
val gitCommit = providers.exec {
    commandLine("git", "rev-parse", "--short", "HEAD")
}.standardOutput.asText.map { it.trim() }

val buildTime = providers.provider {
    java.time.Instant.now().toString()
}

android {
    defaultConfig {
        buildConfigField("String", "GIT_COMMIT", "\"${gitCommit.get()}\"")
        buildConfigField("String", "BUILD_TIME", "\"${buildTime.get()}\"")
    }
}

使用

kotlin
val versionInfo = """
    Version: ${BuildConfig.VERSION_NAME}
    Build: ${BuildConfig.VERSION_CODE}
    Commit: ${BuildConfig.GIT_COMMIT}
    Time: ${BuildConfig.BUILD_TIME}
""".trimIndent()

多环境版本

Flavor 版本

kotlin
android {
    flavorDimensions += "environment"
    
    productFlavors {
        create("dev") {
            dimension = "environment"
            versionNameSuffix = "-dev"
        }
        
        create("staging") {
            dimension = "environment"
            versionNameSuffix = "-staging"
        }
        
        create("prod") {
            dimension = "environment"
        }
    }
}

Build Type 版本

kotlin
android {
    buildTypes {
        debug {
            versionNameSuffix = "-debug"
            applicationIdSuffix = ".debug"
        }
        
        release {
            // 正式版本
        }
    }
}

实战案例

案例1:完整版本管理系统

kotlin
// buildSrc/src/main/kotlin/VersionConfig.kt
object VersionConfig {
    private val gitTag = providers.exec {
        commandLine("git", "describe", "--tags", "--abbrev=0", "--match", "v*")
    }.standardOutput.asText.map { it.trim().removePrefix("v") }
    
    private val gitCommitCount = providers.exec {
        commandLine("git", "rev-list", "--count", "HEAD")
    }.standardOutput.asText.map { it.trim().toInt() }
    
    private val gitCommitHash = providers.exec {
        commandLine("git", "rev-parse", "--short", "HEAD")
    }.standardOutput.asText.map { it.trim() }
    
    fun versionCode(): Int = gitCommitCount.get()
    
    fun versionName(): String {
        val tag = gitTag.getOrElse("0.0.0")
        val hash = gitCommitHash.get()
        return "$tag ($hash)"
    }
    
    fun buildTime(): String = java.time.Instant.now().toString()
}

使用

kotlin
android {
    defaultConfig {
        versionCode = VersionConfig.versionCode()
        versionName = VersionConfig.versionName()
        
        buildConfigField("String", "BUILD_TIME", "\"${VersionConfig.buildTime()}\"")
    }
}

案例2:CI 版本号

kotlin
val isCI = providers.environmentVariable("CI")
    .map { it.toBoolean() }
    .getOrElse(false)

val buildNumber = providers.environmentVariable("BUILD_NUMBER")
    .orElse("0")

android {
    defaultConfig {
        versionCode = if (isCI) {
            buildNumber.get().toInt()
        } else {
            1
        }
        
        versionNameSuffix = if (isCI) "-ci${buildNumber.get()}" else "-local"
    }
}

最佳实践

使用 Git Tags

  • 标记发布版本
  • 自动生成版本号
  • 追踪历史版本

Provider API

kotlin
// ✅ 推荐
val version = providers.exec { ... }.standardOutput.asText

// ❌ 避免
val version = "git ...".execute().text

版本号规范

  • versionCode:自动递增
  • versionName:语义化版本
  • BuildConfig:记录构建信息

环境区分

kotlin
debug {
    versionNameSuffix = "-debug"
}

CI/CD 集成

  • 使用环境变量
  • 自动化版本号
  • 标记发布版本