gradle/releases.gradle.kts (82 lines of code) (raw):
// Common regex patterns
private val APP_VERSION_REGEX = """<string name="app_version">([^(]+) \((\d+)\)</string>""".toRegex()
private val VERSION_HEADER_REGEX = """## Version (.*) \(([a-f0-9]+)\)""".toRegex()
private val COMMIT_HASH_REGEX = """([a-f0-9]+) -.*""".toRegex()
private val MERGE_PR_REGEX = """([a-f0-9]+) - Merge pull request #(\d+) from.*""".toRegex()
// Helper function to run git commands
private fun runGitCommand(vararg args: String): String {
val process = ProcessBuilder("git", *args)
.directory(projectDir)
.redirectOutput(ProcessBuilder.Redirect.PIPE)
.start()
val output = process.inputStream.bufferedReader().readText().trim()
process.waitFor()
return output
}
// Function to normalize content by ensuring there's only one empty line between sections
private fun normalizeContent(content: String): String {
return content.replace(Regex("\n{3,}"), "\n\n")
}
tasks.register("prepareRelease") {
group = "releases"
description = "Bumps versions and prepares release notes"
dependsOn("updateVersion", ":shared:exportLibraryDefinitions")
}
tasks.register("updateVersion") {
group = "releases"
description = "Updates app version code and name across all platforms"
doLast {
// Read current state
val versionCodes = mutableListOf<Int?>()
val versionNames = mutableListOf<String?>()
// Android version
val androidBuildGradle = file("androidApp/build.gradle.kts")
val androidBuildGradleContent = androidBuildGradle.readText()
versionCodes.add("""versionCode\s*=\s*(\d+)""".toRegex().find(androidBuildGradleContent)?.groupValues?.get(1)?.toIntOrNull())
versionNames.add("""versionName\s*=\s*"([^"]+)"""".toRegex().find(androidBuildGradleContent)?.groupValues?.get(1))
// iOS versions from project file
val iosProjectFile = file("iosApp/KotlinConf.xcodeproj/project.pbxproj")
val iosProjectContent = iosProjectFile.readText()
versionCodes.addAll("""CURRENT_PROJECT_VERSION\s*=\s*(\d+)""".toRegex().findAll(iosProjectContent)
.map { it.groupValues[1].toIntOrNull() }.toList())
versionNames.addAll("""MARKETING_VERSION\s*=\s*([^;]+)""".toRegex().findAll(iosProjectContent)
.map { it.groupValues[1].trim() }.toList())
// iOS versions from Info.plist
val iosInfoPlist = file("iosApp/iosApp/Info.plist")
val iosInfoPlistContent = iosInfoPlist.readText()
versionCodes.add("""<key>CFBundleVersion</key>\s*<string>(\d+)</string>""".toRegex()
.find(iosInfoPlistContent)?.groupValues?.get(1)?.toIntOrNull())
versionNames.add("""<key>CFBundleShortVersionString</key>\s*<string>([^<]+)</string>""".toRegex()
.find(iosInfoPlistContent)?.groupValues?.get(1))
// Shared version
val sharedVersionXml = file("shared/src/commonMain/composeResources/values/version.xml")
val sharedVersionXmlContent = sharedVersionXml.readText()
val sharedVersionMatch = APP_VERSION_REGEX.find(sharedVersionXmlContent)
versionCodes.add(sharedVersionMatch?.groupValues?.get(2)?.toIntOrNull())
versionNames.add(sharedVersionMatch?.groupValues?.get(1))
// Validate versions
if (versionCodes.distinct().size != 1 || versionNames.distinct().size != 1) {
throw GradleException("Version mismatch.\nVersion codes found: $versionCodes\nVersion names found: $versionNames")
}
val currentVersionCode = versionCodes.first() ?: throw GradleException("Invalid version code")
val currentVersionName = versionNames.first() ?: throw GradleException("Invalid version name")
println("Current version: $currentVersionName ($currentVersionCode)")
// Calculate new version
val newVersionCode = currentVersionCode + 1
val newVersionName = "${currentVersionName.substringBeforeLast('.')}.${currentVersionName.substringAfterLast('.').toInt() + 1}"
println("New version: $newVersionName ($newVersionCode)")
// Update Android version
androidBuildGradle.writeText(androidBuildGradleContent
.replace("""versionCode = $currentVersionCode""", """versionCode = $newVersionCode""")
.replace("""versionName = "$currentVersionName"""", """versionName = "$newVersionName""""))
// Update iOS project file
iosProjectFile.writeText(iosProjectContent
.replace("""CURRENT_PROJECT_VERSION = $currentVersionCode""", """CURRENT_PROJECT_VERSION = $newVersionCode""")
.replace("""MARKETING_VERSION = $currentVersionName""", """MARKETING_VERSION = $newVersionName"""))
// Update iOS Info.plist
iosInfoPlist.writeText(iosInfoPlistContent
.replace("""<key>CFBundleVersion</key>\s*<string>$currentVersionCode</string>""".toRegex(),
"""<key>CFBundleVersion</key>
<string>$newVersionCode</string>""")
.replace("""<key>CFBundleShortVersionString</key>\s*<string>$currentVersionName</string>""".toRegex(),
"""<key>CFBundleShortVersionString</key>
<string>$newVersionName</string>"""))
// Update shared version
sharedVersionXml.writeText(sharedVersionXmlContent
.replace("""<string name="app_version">$currentVersionName ($currentVersionCode)</string>""",
"""<string name="app_version">$newVersionName ($newVersionCode)</string>"""))
println("Successfully updated version to $newVersionName ($newVersionCode)")
}
}