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")