diff --git a/.gitignore b/.gitignore index 6dde2da7ad0..f2abb861c48 100644 --- a/.gitignore +++ b/.gitignore @@ -95,8 +95,9 @@ lint/tmp/ # Autogenerated file with git hash information. app/src/main/assets/version.txt +app/src/main/assets/dependencies_version.json /intellij.gdsl # Editor temporary files *~ -\#*# \ No newline at end of file +\#*# diff --git a/app/src/main/kotlin/com/wire/android/ui/debug/DebugDataOptions.kt b/app/src/main/kotlin/com/wire/android/ui/debug/DebugDataOptions.kt index 667437dc59c..73ff7903f74 100644 --- a/app/src/main/kotlin/com/wire/android/ui/debug/DebugDataOptions.kt +++ b/app/src/main/kotlin/com/wire/android/ui/debug/DebugDataOptions.kt @@ -25,6 +25,7 @@ import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue @@ -52,6 +53,7 @@ import com.wire.android.ui.home.settings.SettingsItem import com.wire.android.ui.theme.wireColorScheme import com.wire.android.ui.theme.wireDimensions import com.wire.android.ui.theme.wireTypography +import com.wire.android.util.getDependenciesVersion import com.wire.android.util.getDeviceIdString import com.wire.android.util.getGitBuildId import com.wire.android.util.ui.PreviewMultipleThemes @@ -68,6 +70,9 @@ import com.wire.kalium.logic.sync.periodic.UpdateApiVersionsScheduler import com.wire.kalium.logic.sync.slow.RestartSlowSyncProcessForRecoveryUseCase import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.qualifiers.ApplicationContext +import kotlinx.collections.immutable.ImmutableMap +import kotlinx.collections.immutable.persistentMapOf +import kotlinx.collections.immutable.toImmutableMap import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import javax.inject.Inject @@ -84,7 +89,8 @@ data class DebugDataOptionsState( val commitish: String = "null", val certificate: String = "null", val showCertificate: Boolean = false, - val startGettingE2EICertificate: Boolean = false + val startGettingE2EICertificate: Boolean = false, + val dependencies: ImmutableMap = persistentMapOf() ) @Suppress("LongParameterList") @@ -97,7 +103,7 @@ class DebugDataOptionsViewModel private val updateApiVersions: UpdateApiVersionsScheduler, private val mlsKeyPackageCountUseCase: MLSKeyPackageCountUseCase, private val restartSlowSyncProcessForRecovery: RestartSlowSyncProcessForRecoveryUseCase, - private val disableEventProcessingUseCase: DisableEventProcessingUseCase, + private val disableEventProcessingUseCase: DisableEventProcessingUseCase ) : ViewModel() { var state by mutableStateOf( @@ -108,10 +114,28 @@ class DebugDataOptionsViewModel observeEncryptedProteusStorageState() observeMlsMetadata() checkIfCanTriggerManualMigration() - state = state.copy( - debugId = context.getDeviceIdString() ?: "null", - commitish = context.getGitBuildId() - ) + checkDependenciesVersion() + setGitHashAndDeviceId() + } + + private fun setGitHashAndDeviceId() { + viewModelScope.launch { + val deviceId = context.getDeviceIdString() ?: "null" + val gitBuildId = context.getGitBuildId() + state = state.copy( + debugId = deviceId, + commitish = gitBuildId + ) + } + } + + fun checkDependenciesVersion() { + viewModelScope.launch { + val dependencies = context.getDependenciesVersion().toImmutableMap() + state = state.copy( + dependencies = dependencies + ) + } } fun enableEncryptedProteusStorage(enabled: Boolean) { @@ -352,7 +376,8 @@ fun DebugDataOptionsContent( isEventProcessingEnabled = state.isEventProcessingDisabled, onDisableEventProcessingChange = onDisableEventProcessingChange, onRestartSlowSyncForRecovery = onRestartSlowSyncForRecovery, - onForceUpdateApiVersions = onForceUpdateApiVersions + onForceUpdateApiVersions = onForceUpdateApiVersions, + dependenciesMap = state.dependencies ) } @@ -520,7 +545,8 @@ private fun DebugToolsOptions( isEventProcessingEnabled: Boolean, onDisableEventProcessingChange: (Boolean) -> Unit, onRestartSlowSyncForRecovery: () -> Unit, - onForceUpdateApiVersions: () -> Unit + onForceUpdateApiVersions: () -> Unit, + dependenciesMap: ImmutableMap ) { FolderHeader(stringResource(R.string.label_debug_tools_title)) Column { @@ -568,6 +594,17 @@ private fun DebugToolsOptions( ) } ) + RowItemTemplate( + modifier = Modifier.wrapContentWidth(), + title = { + Text( + style = MaterialTheme.wireTypography.body01, + color = MaterialTheme.wireColorScheme.onBackground, + text = prettyPrintMap(dependenciesMap), + modifier = Modifier.padding(start = dimensions().spacing8x) + ) + } + ) } } @@ -599,6 +636,15 @@ private fun DisableEventProcessingSwitch( } ) } + +@Stable +private fun prettyPrintMap(map: ImmutableMap): String = StringBuilder().apply { + append("Dependencies:\n") + map.forEach { (key, value) -> + append("$key: $value\n") + } +}.toString() + //endregion @PreviewMultipleThemes @@ -624,6 +670,6 @@ fun PreviewOtherDebugOptions() { onManualMigrationPressed = {}, enrollE2EICertificate = {}, handleE2EIEnrollmentResult = {}, - dismissCertificateDialog = {}, + dismissCertificateDialog = {} ) } diff --git a/app/src/main/kotlin/com/wire/android/util/FileUtil.kt b/app/src/main/kotlin/com/wire/android/util/FileUtil.kt index cc24748afff..4a66f7c3b2e 100644 --- a/app/src/main/kotlin/com/wire/android/util/FileUtil.kt +++ b/app/src/main/kotlin/com/wire/android/util/FileUtil.kt @@ -58,6 +58,7 @@ import com.wire.kalium.logic.util.buildFileName import com.wire.kalium.logic.util.splitFileExtensionAndCopyCounter import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext +import kotlinx.serialization.json.Json import okio.Path import java.io.File import java.io.FileNotFoundException @@ -421,6 +422,14 @@ fun Context.getGitBuildId(): String = runCatching { } }.getOrDefault("") +suspend fun Context.getDependenciesVersion(): Map = withContext(Dispatchers.IO) { + assets.open("dependencies_version.json").use { inputStream -> + inputStream.bufferedReader().use { it.readText() } + }.let { + Json.decodeFromString(it) + } +} + fun Context.getProviderAuthority() = "$packageName.provider" @VisibleForTesting diff --git a/buildSrc/src/main/kotlin/DependenciesVersionTask.kt b/buildSrc/src/main/kotlin/DependenciesVersionTask.kt new file mode 100644 index 00000000000..831008b87c7 --- /dev/null +++ b/buildSrc/src/main/kotlin/DependenciesVersionTask.kt @@ -0,0 +1,100 @@ +import org.gradle.api.DefaultTask +import org.gradle.api.tasks.TaskAction +import java.io.File +import java.io.FileOutputStream + +/* + * 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/. + */ +open class DependenciesVersionTask : DefaultTask() { + + private val VERSION_FILE = "app/src/main/assets/dependencies_version.json" + + // map of toml file and list of dependencies to extract version + private val dependencies = mapOf( + "kalium/gradle/libs.versions.toml" to listOf( + "avs", + "core-crypto" + ) + ) + + init { + group = "release" + description = "Get dependencies version and write it to the application, if possible." + } + + @TaskAction + fun processGitBuildIdentifier() { + runCatching { + println("\uD83D\uDD27 Running dependencies version parser to build.") + mutableMapOf().apply { + for ((tomlFile, dependencies) in dependencies) { + val toml = File(tomlFile).readText() + val tables = parseToml(toml) + for (dependency in dependencies) { + val version = tables["versions"]?.get(dependency) + println("\uD83D\uDD27 $dependency version: $version") + put(dependency, version) + } + } + }.toJsonString() + .also { writeToFile(it) } + }.onFailure { + println("\uD83D\uDD27 Failed to extract dependencies version: ${it.stackTraceToString()}") + writeToFile("{}") + } + } + + fun parseToml(tomlContent: String): Map> { + val table = mutableMapOf>() + var currentTable = "" + + // Regular expression to match table headers and key-value pairs + val regex = Regex("""\[(.*?)\]|\s*([^\s=]+)\s*=\s*(".*?"|[^\r\n#]+)""") + + // Iterate over lines of the TOML content + tomlContent.lines().forEach { line -> + val matchResult = regex.find(line) + + // If it's a table header + if (line.startsWith("[")) { + currentTable = matchResult?.groups?.get(1)?.value ?: "" + table[currentTable] = mutableMapOf() + } + // If it's a key-value pair + else if (matchResult != null) { + val key = matchResult.groups[2]?.value?.trim('"') + val value = matchResult.groups[3]?.value?.trim('"') + + if (!key.isNullOrBlank() && !value.isNullOrBlank()) { + table[currentTable]?.put(key, value) + } + } + } + return table + } + + /** + * Write the given [text] to the [VERSION_FILE]. + */ + private fun writeToFile(text: String) { + FileOutputStream(File(project.rootDir, VERSION_FILE)).use { + it.write(text.toByteArray()) + } + println("\u2705 Successfully wrote $text to file.") + } +} diff --git a/buildSrc/src/main/kotlin/MapTojson.kt b/buildSrc/src/main/kotlin/MapTojson.kt new file mode 100644 index 00000000000..6d4cdb2fe20 --- /dev/null +++ b/buildSrc/src/main/kotlin/MapTojson.kt @@ -0,0 +1,31 @@ +/* + * 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 Map.toJsonString (): String { + if (this.isEmpty()) return "{}" + + return StringBuilder().apply { + append("{") + this@toJsonString.forEach { (key, value) -> + append("\"$key\":\"$value\",") + } + deleteCharAt(length - 1) + append("}") + + }.toString() +} diff --git a/buildSrc/src/main/kotlin/scripts/compilation.gradle.kts b/buildSrc/src/main/kotlin/scripts/compilation.gradle.kts index cd04c23c298..e370013fb66 100644 --- a/buildSrc/src/main/kotlin/scripts/compilation.gradle.kts +++ b/buildSrc/src/main/kotlin/scripts/compilation.gradle.kts @@ -18,6 +18,7 @@ package scripts +import DependenciesVersionTask import IncludeGitBuildTask plugins { @@ -29,8 +30,13 @@ project.tasks.register("includeGitBuildIdentifier", IncludeGitBuildTask::class) println("> Registering Task :includeGitBuildIdentifier") } +project.tasks.register("dependenciesVersionTask", DependenciesVersionTask::class) { + println("> Registering Task :dependenciesVersionTask") +} + project.afterEvaluate { project.tasks.matching { it.name.startsWith("bundle") || it.name.startsWith("assemble") }.configureEach { dependsOn("includeGitBuildIdentifier") + dependsOn("dependenciesVersionTask") } }