diff --git a/apps/flipcash/app/build.gradle.kts b/apps/flipcash/app/build.gradle.kts index 7080336941..ba301ebe12 100644 --- a/apps/flipcash/app/build.gradle.kts +++ b/apps/flipcash/app/build.gradle.kts @@ -37,6 +37,14 @@ val bugsnagBuildId: String = providers.exec { commandLine("git", "rev-parse", "HEAD") }.standardOutput.asText.get().trim() +// Tracked changes only: CI writes google-services.json, local.properties and the signing key +// into the checkout, all gitignored, so a release build still reads as clean. Reading just the +// exit code keeps the configuration cache from invalidating on every edit to a dirty tree. +val gitTreeDirty: Boolean = providers.exec { + commandLine("git", "diff", "--quiet", "HEAD") + isIgnoreExitValue = true +}.result.get().exitValue != 0 + val contributorsSigningConfig = ContributorsSignatory(rootDir) val appNamespace = "${Gradle.flipcashNamespace}.app.android" @@ -61,6 +69,8 @@ android { testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" buildConfigField("String", "VERSION_NAME", "\"${Packaging.Flipcash.versionName}\"") + buildConfigField("String", "GIT_SHA", "\"$bugsnagBuildId\"") + buildConfigField("Boolean", "GIT_DIRTY", "$gitTreeDirty") buildConfigField("String", "MIXPANEL_API_KEY", "\"${tryReadProperty(rootProject.rootDir, "MIXPANEL_API_KEY")}\"") buildConfigField("Boolean", "NOTIFY_ERRORS", "false") diff --git a/apps/flipcash/app/src/main/kotlin/com/flipcash/app/inject/AppModule.kt b/apps/flipcash/app/src/main/kotlin/com/flipcash/app/inject/AppModule.kt index cbc65b893a..074c4f9f30 100644 --- a/apps/flipcash/app/src/main/kotlin/com/flipcash/app/inject/AppModule.kt +++ b/apps/flipcash/app/src/main/kotlin/com/flipcash/app/inject/AppModule.kt @@ -30,7 +30,9 @@ object AppModule { @Singleton fun providesAppVersion(): VersionInfo = VersionInfo( versionName = BuildConfig.VERSION_NAME, - versionCode = BuildConfig.VERSION_CODE + versionCode = BuildConfig.VERSION_CODE, + commitSha = BuildConfig.GIT_SHA, + isDirty = BuildConfig.GIT_DIRTY, ) @Provides diff --git a/apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/android/VersionInfo.kt b/apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/android/VersionInfo.kt index 0d9b16f3ed..69aedc9c9a 100644 --- a/apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/android/VersionInfo.kt +++ b/apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/android/VersionInfo.kt @@ -5,4 +5,24 @@ import javax.inject.Inject class VersionInfo @Inject constructor( val versionName: String = "", val versionCode: Int = 0, -) \ No newline at end of file + /** The full commit SHA the build was made from; empty when the build carries none. */ + val commitSha: String = "", + /** Whether the build was made with uncommitted changes to tracked files. */ + val isDirty: Boolean = false, +) { + /** + * The commit as the version footer shows it: the first [COMMIT_LABEL_LENGTH] characters of + * [commitSha], with `*` appended for a build with uncommitted changes. Null when the + * build carries no commit. The iOS footer uses the same format. + */ + val commitLabel: String? + get() { + if (commitSha.isBlank()) return null + val short = commitSha.trim().take(COMMIT_LABEL_LENGTH) + return if (isDirty) "$short*" else short + } + + companion object { + const val COMMIT_LABEL_LENGTH = 10 + } +} diff --git a/apps/flipcash/core/src/main/res/values/strings.xml b/apps/flipcash/core/src/main/res/values/strings.xml index c981d0eff9..6daf7f2aca 100644 --- a/apps/flipcash/core/src/main/res/values/strings.xml +++ b/apps/flipcash/core/src/main/res/values/strings.xml @@ -136,7 +136,9 @@ Something went wrong. The cash could not be collected - Version %1$s • Build %2$s%3$s + Version %1$s • Build %2$s\n%3$s%4$s + + Version %1$s • Build %2$s%3$s No Beta Features There are no beta features available right now. diff --git a/apps/flipcash/core/src/test/kotlin/com/flipcash/app/core/android/VersionInfoTest.kt b/apps/flipcash/core/src/test/kotlin/com/flipcash/app/core/android/VersionInfoTest.kt new file mode 100644 index 0000000000..ac705be3f6 --- /dev/null +++ b/apps/flipcash/core/src/test/kotlin/com/flipcash/app/core/android/VersionInfoTest.kt @@ -0,0 +1,40 @@ +package com.flipcash.app.core.android + +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNull + +class VersionInfoTest { + + private val sha = "2acfecffeb1c1e7b8d64b0e3c6b7a1f2d9e0c4a5" + + @Test + fun `the label is the first ten characters of the SHA`() { + assertEquals("2acfecffeb", VersionInfo(commitSha = sha).commitLabel) + } + + @Test + fun `a dirty build appends an asterisk`() { + assertEquals("2acfecffeb*", VersionInfo(commitSha = sha, isDirty = true).commitLabel) + } + + @Test + fun `a SHA shorter than ten characters is shown whole`() { + assertEquals("2acfecf", VersionInfo(commitSha = "2acfecf").commitLabel) + } + + @Test + fun `surrounding whitespace is not counted toward the ten characters`() { + assertEquals("2acfecffeb", VersionInfo(commitSha = " $sha\n").commitLabel) + } + + @Test + fun `an empty SHA has no label`() { + assertNull(VersionInfo().commitLabel) + } + + @Test + fun `a blank SHA has no label, even when dirty`() { + assertNull(VersionInfo(commitSha = " ", isDirty = true).commitLabel) + } +} diff --git a/apps/flipcash/features/menu/src/main/kotlin/com/flipcash/app/menu/internal/MenuScreenContent.kt b/apps/flipcash/features/menu/src/main/kotlin/com/flipcash/app/menu/internal/MenuScreenContent.kt index 598b803edc..0b5a479598 100644 --- a/apps/flipcash/features/menu/src/main/kotlin/com/flipcash/app/menu/internal/MenuScreenContent.kt +++ b/apps/flipcash/features/menu/src/main/kotlin/com/flipcash/app/menu/internal/MenuScreenContent.kt @@ -930,7 +930,10 @@ private fun ShareTile( } } -/** The "Version … • Build …" footer; its repeated tap toggles beta access (see the ViewModel). */ +/** + * The two-line version footer, "Version … • Build …" over " • ". Its repeated tap + * toggles beta access (see the ViewModel). + */ @Composable private fun VersionFooter( viewModel: MenuScreenViewModel, @@ -946,8 +949,16 @@ private fun VersionFooter( .noRippleClickable(enabled = enabled) { viewModel.dispatchEvent(Event.OnVersionInfoClicked) }, - text = stringResource( - R.string.subtitle_appVersionInfoFooter, + text = state.appVersionInfo.commitLabel?.let { commit -> + stringResource( + R.string.subtitle_appVersionInfoFooter, + state.appVersionInfo.versionName, + state.appVersionInfo.versionCode, + commit, + state.releaseTrack, + ) + } ?: stringResource( + R.string.subtitle_appVersionInfoFooterNoCommit, state.appVersionInfo.versionName, state.appVersionInfo.versionCode, state.releaseTrack,