Skip to content

Commit

Permalink
ci: add gradle task to generate app version [WPB-7289] 🍒 (#3358)
Browse files Browse the repository at this point in the history
Co-authored-by: Mohamad Jaara <[email protected]>
Co-authored-by: Yamil Medina <[email protected]>
  • Loading branch information
3 people authored Aug 30, 2024
1 parent c4eaec5 commit 74c545c
Show file tree
Hide file tree
Showing 12 changed files with 178 additions and 23 deletions.
13 changes: 11 additions & 2 deletions .github/workflows/build-prod-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ jobs:
echo $ENCODED_STRING | base64 -di > "${TMP_KEYSTORE_FILE_PATH}"/the.keystore
- name: Make gradlew executable
run: chmod +x ./gradlew
- name: Generate version file
run: ./gradlew generateVersionFile
- name: Upload version file as artifact
uses: actions/upload-artifact@v4
with:
name: Version File
path: app/version.txt
- name: build prod flavour APK
run:
./gradlew app:assembleProdCompatrelease
Expand Down Expand Up @@ -106,7 +113,9 @@ jobs:
github-token: ${{ secrets.GITHUB_TOKEN }}
build-flavour: prod
build-variant: compatrelease
- name: Attach APK to release
- name: Attach APK and version file to release
uses: softprops/[email protected]
with:
files: app/build/outputs/apk/prodCompatrelease/*.apk
files: |
app/build/outputs/apk/prodCompatrelease/*.apk
app/version.txt
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ app/src/main/assets/version.txt
app/src/main/assets/dependencies_version.json
/intellij.gdsl

app/version.txt
# Editor temporary files
*~
\#*#
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ plugins {
id(ScriptPlugins.compilation)
id(ScriptPlugins.testing)
id(libs.plugins.wire.kover.get().pluginId)
id(libs.plugins.wire.versionizer.get().pluginId)
}

repositories {
Expand Down
4 changes: 4 additions & 0 deletions build-logic/plugins/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,9 @@ gradlePlugin {
id = libs.plugins.wire.kover.get().pluginId
implementationClass = "KoverConventionPlugin"
}
register("appVersionPlugin") {
id = libs.plugins.wire.versionizer.get().pluginId
implementationClass = "AppVersionPlugin"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ class AndroidApplicationConventionPlugin : Plugin<Project> {
namespace = AndroidApp.id
configureKotlinAndroid(this)
defaultConfig {
AndroidApp.setRootDir(projectDir)

applicationId = AndroidApp.id
defaultConfig.targetSdk = AndroidSdk.target
versionCode = AndroidApp.versionCode
Expand Down
14 changes: 12 additions & 2 deletions build-logic/plugins/src/main/kotlin/AndroidCoordinates.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
import com.wire.android.gradle.version.Versionizer
import java.io.File

object AndroidSdk {
const val min = 26
Expand All @@ -26,7 +27,14 @@ object AndroidSdk {
object AndroidApp {
const val id = "com.wire.android"
const val versionName = "4.9.0"
val versionCode = Versionizer().versionCode
val versionCode by lazy {
Versionizer(_rootDir).versionCode
}

private lateinit var _rootDir: File
fun setRootDir(rootDir: File) {
this._rootDir = rootDir
}

/**
* The last 5 digits of the VersionCode. From 0 to 99_999.
Expand All @@ -43,5 +51,7 @@ object AndroidApp {
* unless they are build almost one year apart.
*/
@Suppress("MagicNumber")
val leastSignificantVersionCode = versionCode % 100_000
val leastSignificantVersionCode by lazy {
versionCode % 100_000
}
}
62 changes: 62 additions & 0 deletions build-logic/plugins/src/main/kotlin/AppVersionPlugin.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import com.wire.android.gradle.version.Versionizer
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.Task
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter

/*
* Wire
* Copyright (C) 2024 Wire Swiss GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
class AppVersionPlugin : Plugin<Project> {
override fun apply(target: Project) {
with(target) {
project.tasks.register("generateVersionFile", Task::class.java) {
// clean the repo from a probably existing version.txt
if (file("$projectDir/version.txt").exists()) {
println("deleting existing version.txt file for safety reasons")
file("$projectDir/version.txt").delete()
}

val currentTime = LocalDateTime.now()
val versnisor = Versionizer(projectDir, currentTime)
val versionCode = versnisor.versionCode
val versionName = "${AndroidApp.versionName}-${AndroidApp.leastSignificantVersionCode}"
val buildTime = currentTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) ?: error("Failed to get build time")
val appName = "com.wire"
// git commit hash code
val gitRevision = "git rev-parse --short HEAD".execute().text.trim()
println("VersionCode: $versionCode")
println("VersionName: $versionName")
println("Revision: $gitRevision")
println("Buildtime: $buildTime")
println("Application-name: $appName")

// output the data to a file in app/version.txt
file("$projectDir/version.txt").writeText(
"""
|VersionCode: $versionCode
|VersionName: $versionName
|Revision: $gitRevision
|Buildtime: $buildTime
|Application-name: $appName
""".trimMargin()
)
}
}
}
}
34 changes: 34 additions & 0 deletions build-logic/plugins/src/main/kotlin/Extention.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import java.io.ByteArrayOutputStream

/*
* Wire
* Copyright (C) 2024 Wire Swiss GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/

fun String.execute(): Process {
val process = ProcessBuilder(this.split(" "))
.redirectErrorStream(true)
.start()
process.waitFor()
return process
}

val Process.text: String
get() {
val output = ByteArrayOutputStream()
inputStream.copyTo(output)
return output.toString()
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
*/
package com.wire.android.gradle.version

import java.io.File
import java.time.Duration
import java.time.LocalDateTime

Expand All @@ -27,7 +28,10 @@ import java.time.LocalDateTime
* This has been built to match the current Groovy implementation:
* https://github.com/wireapp/wire-android/blob/594497477325d77c1d203dbcaab79fb14b511530/app/build.gradle#L467
*/
class Versionizer(private val localDateTime: LocalDateTime = LocalDateTime.now()) {
class Versionizer(
private val projectDir: File,
private val localDateTime: LocalDateTime = LocalDateTime.now(),
) {

// get version from app/version.txt otherwise use the current date
val versionCode = readFromInternalFile() ?: generateVersionCode()
Expand All @@ -36,16 +40,21 @@ class Versionizer(private val localDateTime: LocalDateTime = LocalDateTime.now()
// VersionCode: $$VERCODE$$\n
// the file is added by CI tp sync build version between store and fdroid
private fun readFromInternalFile(): Int? {
val file = java.io.File("app/version.txt")
val file = File("$projectDir/version.txt")
println("looking for version file")
if (file.exists()) {
println("Reading version from file")
val lines = file.readLines()
val versionCode = lines.find { it.startsWith("VersionCode:") }?.substringAfter(":")?.trim()
return versionCode?.toIntOrNull()
println("Version code: $versionCode from file")
return versionCode?.toInt()
}
println("No version file found")
return null
}

private fun generateVersionCode(): Int {
println("Generating version code with date: $localDateTime")
return if (localDateTime <= V2_DATE_OFFSET) {
val duration = Duration.between(V1_DATE_OFFSET, localDateTime)
(duration.seconds / V1_SECONDS_PER_BUMP).toInt()
Expand Down
48 changes: 33 additions & 15 deletions build-logic/plugins/src/test/kotlin/VersionizerTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import org.amshove.kluent.shouldBeLessThan
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import java.io.File
import java.time.LocalDateTime

@RunWith(JUnit4::class)
Expand All @@ -32,23 +33,26 @@ class VersionizerTest {
@Test
fun `given version generator when I generate two versions AT THE SAME TIME I should get the same version number`() {
val dateTime = LocalDateTime.now()

Versionizer(dateTime).versionCode shouldBeEqualTo Versionizer(dateTime).versionCode
val projectRoot = File("")
Versionizer(projectRoot, dateTime).versionCode shouldBeEqualTo Versionizer(projectRoot, dateTime).versionCode
}


@Test
fun `given version generator when I generate a version I should get the same version number on the current Android project`() {
val dateTime = LocalDateTime.of(2021, 6, 23, 13, 54, 28)
val projectRoot = File("")

Versionizer(dateTime).versionCode shouldBeEqualTo 548966
Versionizer(projectRoot, dateTime).versionCode shouldBeEqualTo 548966
}

@Test
fun `given before than 21 of June 2024 Build, then bump every 10 seconds`() {
val oldDate = LocalDateTime.of(2024, 6, 20, 0, 0)
val oldVersionCode = Versionizer(oldDate).versionCode
val newVersionCode = Versionizer(oldDate.plusSeconds(10)).versionCode
val projectRoot = File("")

val oldVersionCode = Versionizer(projectRoot, oldDate).versionCode
val newVersionCode = Versionizer(projectRoot, oldDate.plusSeconds(10)).versionCode

assertVersionCodeAreProperlyIncremented(oldVersionCode, newVersionCode)
assertEquals(1, newVersionCode - oldVersionCode)
Expand All @@ -57,8 +61,10 @@ class VersionizerTest {
@Test
fun `given after 21 of June 2024 Build, then bump every 5 minutes`() {
val oldDate = LocalDateTime.of(2024, 6, 22, 14, 0)
val oldVersionCode = Versionizer(oldDate).versionCode
val newVersionCode = Versionizer(oldDate.plusMinutes(5)).versionCode
val projectRoot = File("")

val oldVersionCode = Versionizer(projectRoot, oldDate).versionCode
val newVersionCode = Versionizer(projectRoot, oldDate.plusMinutes(5)).versionCode

println("Version number: $newVersionCode")
assertVersionCodeAreProperlyIncremented(oldVersionCode, newVersionCode)
Expand All @@ -67,38 +73,50 @@ class VersionizerTest {

@Test
fun `given version generator when I generate a new version THE NEXT DAY then I should get an incremented version number`() {
val oldVersionCode = Versionizer().versionCode
val newVersionCode = Versionizer(LocalDateTime.now().plusDays(1)).versionCode
val projectRoot = File("")
val oldVersionCode = Versionizer(projectRoot).versionCode
val newVersionCode = Versionizer(projectRoot, LocalDateTime.now().plusDays(1)).versionCode

assertVersionCodeAreProperlyIncremented(oldVersionCode, newVersionCode)
}

@Test
fun `given version generator when I generate a new version IN ONE YEAR then I should get an incremented version number`() {
val oldVersionCode = Versionizer().versionCode
val newVersionCode = Versionizer(LocalDateTime.now().plusYears(1)).versionCode
val projectRoot = File("")
val oldVersionCode = Versionizer(projectRoot).versionCode
val newVersionCode = Versionizer(projectRoot, LocalDateTime.now().plusYears(1)).versionCode

assertVersionCodeAreProperlyIncremented(oldVersionCode, newVersionCode)
}

@Test
fun `given version generator when I generate a new version IN ONE and TWO YEARS then I should get an incremented version number`() {
val now = LocalDateTime.now()
val oldVersionCode = Versionizer(now.plusYears(1)).versionCode
val newVersionCode = Versionizer(now.plusYears(2)).versionCode
val projectRoot = File("")
val oldVersionCode = Versionizer(projectRoot, now.plusYears(1)).versionCode
val newVersionCode = Versionizer(projectRoot, now.plusYears(2)).versionCode

assertVersionCodeAreProperlyIncremented(oldVersionCode, newVersionCode)
}

@Test
fun `given version generator when I generate a new version IN TEN YEARS then I should get an incremented version number`() {
val oldVersionCode = Versionizer().versionCode
val newVersionCode = Versionizer(LocalDateTime.now().plusYears(10)).versionCode
val projectRoot = File("")
val oldVersionCode = Versionizer(projectRoot).versionCode
val newVersionCode = Versionizer(projectRoot, LocalDateTime.now().plusYears(10)).versionCode

// This will break 655 years from now.
assertVersionCodeAreProperlyIncremented(oldVersionCode, newVersionCode)
}

@Test
fun `given root project have version txt file, then use the version code from the file`() {
val projectRoot = File("src/test/resources")
val versionCode = Versionizer(projectRoot).versionCode

versionCode shouldBeEqualTo 100018802
}

private fun assertVersionCodeAreProperlyIncremented(oldVersionCode: Int, newVersionCode: Int) {
oldVersionCode shouldBeGreaterThan 0
newVersionCode shouldBeGreaterThan 0
Expand Down
5 changes: 5 additions & 0 deletions build-logic/plugins/src/test/resources/version.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
VersionCode: 100018802
VersionName: 4.8.2-18802
Revision: e53642019
Buildtime: 2024-08-21 19:32:06
Application-name: com.wire
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ wire-android-application = { id = "com.wire.android.application" }
wire-android-library = { id = "com.wire.android.library" }
wire-hilt = { id = "com.wire.android.hilt" }
wire-kover = { id = "com.wire.android.kover" }

wire-versionizer = { id = "com.wire.android.versionizer" }
[bundles]
#android = ["appCompat", "activityCompose", "work", "composeMaterial", "coroutinesAndroid", "ktor", "ktor-okHttp"]

Expand Down

0 comments on commit 74c545c

Please sign in to comment.