// Common regex patterns
private val APP_VERSION_REGEX = """([^(]+) \((\d+)\)""".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()
val versionNames = mutableListOf()
// 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("""CFBundleVersion\s*(\d+)""".toRegex()
.find(iosInfoPlistContent)?.groupValues?.get(1)?.toIntOrNull())
versionNames.add("""CFBundleShortVersionString\s*([^<]+)""".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("""CFBundleVersion\s*$currentVersionCode""".toRegex(),
"""CFBundleVersion
$newVersionCode""")
.replace("""CFBundleShortVersionString\s*$currentVersionName""".toRegex(),
"""CFBundleShortVersionString
$newVersionName"""))
// Update shared version
sharedVersionXml.writeText(sharedVersionXmlContent
.replace("""$currentVersionName ($currentVersionCode)""",
"""$newVersionName ($newVersionCode)"""))
println("Successfully updated version to $newVersionName ($newVersionCode)")
}
}