From f3c425e68ab351ef2ca53470bd9fc9183f42ecdd Mon Sep 17 00:00:00 2001 From: Carter Jernigan Date: Tue, 19 Oct 2021 09:24:45 -0400 Subject: [PATCH] [#31] Add injection of build info Provides infrastructure to help us track exactly what build of the app is running. This provides the ability to implement a number of features in the future - Displaying build version in an About screen - Automatically versioning with Git commit count as an incrementing version number - Injection of secrets, API keys, or other behaviors into the build so that the app can leverage them at runtime. The secrets can be securely stored as environment variables (e.g. on CI), so they don't need to be committed to the repository. --- build-info-lib/build.gradle.kts | 78 +++++++++++++++++++ buildSrc/build.gradle.kts | 19 +++++ .../main/kotlin/co/electriccoin/zcash/Git.kt | 22 ++++++ docs/Architecture.md | 1 + gradle.properties | 1 + settings.gradle.kts | 1 + 6 files changed, 122 insertions(+) create mode 100644 build-info-lib/build.gradle.kts create mode 100644 buildSrc/build.gradle.kts create mode 100644 buildSrc/src/main/kotlin/co/electriccoin/zcash/Git.kt diff --git a/build-info-lib/build.gradle.kts b/build-info-lib/build.gradle.kts new file mode 100644 index 00000000..d9ef59bb --- /dev/null +++ b/build-info-lib/build.gradle.kts @@ -0,0 +1,78 @@ +import java.text.SimpleDateFormat +import java.util.* + +plugins { + kotlin("multiplatform") + id("zcash.kotlin-multiplatform-build-conventions") +} + +// Injects build information +// Note timestamp is not currently injected because it effectively disables the cache since it +// changes with every build +val generateBuildConfigTask = tasks.create("buildConfig") { + val generatedDir = "$buildDir/generated" + + val gitInfo = co.electriccoin.zcash.Git.newInfo(parent!!.projectDir) + //val buildTimestamp = newIso8601Timestamp() + + inputs.property("gitSha", gitInfo.sha) + inputs.property("gitCommitCount", gitInfo.commitCount) + //inputs.property("buildTimestamp", buildTimestamp) + + outputs.dir(File(generatedDir)) + + doLast { + val outputFile = File("$generatedDir/co/electriccoin/zcash/build/BuildConfig.kt") + outputFile.parentFile.mkdirs() + + // To add timestamp, add this to the output below + // import kotlinx.datetime.Instant + // import kotlinx.datetime.toInstant + // val buildTimestamp: Instant = "$buildTimestamp".toInstant() + outputFile.writeText( + """ +// Generated file +package co.electriccoin.zcash.build + +const val gitSha: String = "${gitInfo.sha}" +const val gitCommitCount: Int = ${gitInfo.commitCount} +""".trimIndent() + ) + } +} + +kotlin { + jvm() + sourceSets { + getByName("commonMain") { + dependencies { + kotlin.srcDir(generateBuildConfigTask) + //api(libs.kotlinx.datetime) + } + } + getByName("commonTest") { + dependencies { + implementation(kotlin("test")) + } + } + getByName("jvmMain") { + dependencies { + } + } + getByName("jvmTest") { + dependencies { + implementation(kotlin("test")) + } + } + } +} + +/** + * @return Current ISO 8601 timestamp. + */ +fun newIso8601Timestamp(): String { + val formatter = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ").apply { + timeZone = TimeZone.getTimeZone("UTC") + } + return formatter.format(Date()) +} diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts new file mode 100644 index 00000000..5b57c46e --- /dev/null +++ b/buildSrc/build.gradle.kts @@ -0,0 +1,19 @@ +import org.jetbrains.kotlin.konan.properties.loadProperties + +plugins { + `java-gradle-plugin` + `kotlin-dsl` +} + +repositories { + mavenCentral() +} + +dependencies { + val rootProperties = getRootProperties() + + implementation("org.eclipse.jgit:org.eclipse.jgit:${rootProperties.getProperty("JGIT_VERSION")}") +} + +// A slightly gross way to use the root gradle.properties as the single source of truth for version numbers +fun getRootProperties() = loadProperties(File(project.projectDir.parentFile, "gradle.properties").path) \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/co/electriccoin/zcash/Git.kt b/buildSrc/src/main/kotlin/co/electriccoin/zcash/Git.kt new file mode 100644 index 00000000..68099a80 --- /dev/null +++ b/buildSrc/src/main/kotlin/co/electriccoin/zcash/Git.kt @@ -0,0 +1,22 @@ +package co.electriccoin.zcash + +import org.eclipse.jgit.api.Git +import org.eclipse.jgit.lib.ObjectId +import java.io.File + +object Git { + // Get the info for the current branch + private const val HEAD = "HEAD" + + fun newInfo(workingDirectory: File): GitInfo { + val git = Git.open(workingDirectory) + val repository = git.repository + + val head: ObjectId = repository.resolve(HEAD) + val count = git.log().call().count() + + return GitInfo(ObjectId.toString(head), count) + } +} + +data class GitInfo(val sha: String, val commitCount: Int) diff --git a/docs/Architecture.md b/docs/Architecture.md index 93369dab..1da50cd5 100644 --- a/docs/Architecture.md +++ b/docs/Architecture.md @@ -23,6 +23,7 @@ The main entrypoints of the application are: The logical components of the app are implemented as a number of Gradle modules. * app — Compiles all of the modules together into the final application. This module contains minimal actual code. Note that the Java package structure for this module is under `cash.z.ecc.app` while the Android package name is `cash.z.ecc`. + * build-info-lib — Collects information from the build environment (e.g. Git SHA, Git commit count) and compiles them into the application. Can also be used for injection of API keys or other secrets. * ui-lib — User interface that the user interacts with. This contains 99% of the UI code, along with localizations, icons, and other assets. * preference * preference-api-lib — Multiplatform interfaces for key-value storage of preferences diff --git a/gradle.properties b/gradle.properties index 6a30015f..b13c24a0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -49,6 +49,7 @@ ANDROID_GRADLE_PLUGIN_VERSION=7.0.3 DETEKT_VERSION=1.18.1 GRADLE_VERSIONS_PLUGIN_VERSION=0.38.0 KTLINT_VERSION=0.42.1 +JGIT_VERSION=5.12.0.202106070339-r ANDROIDX_ACTIVITY_VERSION=1.3.1 ANDROIDX_ANNOTATION_VERSION=1.2.0 diff --git a/settings.gradle.kts b/settings.gradle.kts index fa022500..2479567e 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -121,6 +121,7 @@ rootProject.name = "zcash-android-app" includeBuild("build-conventions") include("app") +include("build-info-lib") include("preference-api-lib") include("preference-impl-android-lib") include("test-lib")