diff --git a/gradle-plugin/CHANGELOG.md b/gradle-plugin/CHANGELOG.md index 29711ad3..bedf3eaf 100644 --- a/gradle-plugin/CHANGELOG.md +++ b/gradle-plugin/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +- Add build distribution support to Gradle plugin. [#281](https://github.com/EmergeTools/emerge-android/pull/281) + ## 4.0.2 - 2024-10-22 - Remove `previousSha` from PR diff --git a/gradle-plugin/README.md b/gradle-plugin/README.md index 472792d2..92c3e640 100644 --- a/gradle-plugin/README.md +++ b/gradle-plugin/README.md @@ -253,9 +253,10 @@ emerge { // The build variants Reaper is enabled for. // When Reaper is enabled the application bytecode will be instrumented to support Reaper. enabledVariants.set(listOf("release", "releaseVariant2")) - // The key used to identify Reaper reports for your organization. Emerge recommends setting this as an environment variable - // Note: This key is not the same as the API key used for uploading to Emerge - you can find this - publishableApiKey.set(System.getenv("REAPER_API_TOKEN")) + // The key used to identify Reaper reports for your organization. Emerge recommends setting this as an environment variable. + // This key can be found at https://www.emergetools.com/settings?tab=feature-configuration&cards=reaper_enabled + // Note: This key is not the same as the API token used for uploading to Emerge. + publishableApiKey.set(System.getenv("REAPER_API_KEY")) // Optional, defaults to 'release' tag.set("release") @@ -273,6 +274,51 @@ emerge { | `enabledVariants` | `List` | `emptyList()` | The build variants Reaper is enabled for. | | `tag` | `String` | `release` | The build tag to use for grouping builds in the Emerge dashboard. | +### Distribution + +The `distribution` extension allows you to configure build distribution specific fields. + +See the [build distribution](https://docs.emergetools.com/docs/distribution-setup-android) docs for more information. + +#### Tasks + +| Task | Description | +|----------------------------------------|------------------------------------------------------------------------------------------------| +| `emergeDistributionPreflight{Variant}` | Run a preflight check to validate if distribution is properly set up for the specific variant. | + +#### Configuration + +The `distribution` extension allows you to configure Distribution-specific fields. + +```kotlin +emerge { + // .. + + distribution { + // The build variants Distribution is enabled for. + enabledVariants.set(listOf("release", "staging")) + // The key used to authenticate downloads for future updates. + // Emerge recommends setting this as an environment variable. + // This key can be found at https://www.emergetools.com/settings?tab=feature-configuration&cards=distribution_enabled + // Note: This key is not the same as the API token used for uploading to Emerge. + apiKey.set(System.getenv("DISTRIBUTION_API_KEY")) + + // Optional, defaults to 'release' + tag.set("release") + // Alternatively, use `setFromVariant()` to set the tag from the Android build variant name + tag.setFromVariant() + } +} +``` + +##### Fields + +| Field | Type | Default | Description | +|---------------------|----------------|---------------|-----------------------------------------------------------------------------| +| `apiKey` | `String` | | This key is used to authenticate update downloads. | +| `enabledVariants` | `List` | `emptyList()` | The build variants Distribution is enabled for. | +| `tag` | `String` | `release` | The build tag to use for determining which builds are possible updates. | + ### Performance #### Tasks @@ -391,15 +437,30 @@ emerge { // The build variants Reaper is enabled for. // When Reaper is enabled the application bytecode will be instrumented to support Reaper. enabledVariants.set(listOf("release", "releaseVariant2")) - // The key used to identify Reaper reports for your organization. Emerge recommends setting this as an environment variable - // Note: This key is not the same as the API key used for uploading to Emerge - you can find this - publishableApiKey.set(System.getenv("REAPER_API_TOKEN")) + // The key used to identify Reaper reports for your organization. Emerge recommends setting this as an environment variable. + // This key can be found at https://www.emergetools.com/settings?tab=feature-configuration&cards=reaper_enabled + // Note: This key is not the same as the API token used for uploading to Emerge. + publishableApiKey.set(System.getenv("REAPER_API_KEY")) // Optional, defaults to 'release' tag.set("release") // Alternatively, use `setFromVariant()` to set the tag from the Android build variant name tag.setFromVariant() + } + distribution { + // The build variants Distribution is enabled for. + enabledVariants.set(listOf("release", "staging")) + // The key used to authenticate downloads for future updates. + // Emerge recommends setting this as an environment variable. + // This key can be found at https://www.emergetools.com/settings?tab=feature-configuration&cards=distribution_enabled + // Note: This key is not the same as the API token used for uploading to Emerge. + apiKey.set(System.getenv("DISTRIBUTION_API_KEY")) + + // Optional, defaults to 'release' + tag.set("release") + // Alternatively, use `setFromVariant()` to set the tag from the Android build variant name + tag.setFromVariant() } performance { diff --git a/gradle-plugin/plugin/src/main/kotlin/com/emergetools/android/gradle/EmergePlugin.kt b/gradle-plugin/plugin/src/main/kotlin/com/emergetools/android/gradle/EmergePlugin.kt index bcb74b7f..d449c297 100644 --- a/gradle-plugin/plugin/src/main/kotlin/com/emergetools/android/gradle/EmergePlugin.kt +++ b/gradle-plugin/plugin/src/main/kotlin/com/emergetools/android/gradle/EmergePlugin.kt @@ -9,6 +9,7 @@ import com.android.build.api.variant.ApplicationVariant import com.android.build.api.variant.TestAndroidComponentsExtension import com.android.build.gradle.internal.dsl.BaseAppModuleExtension import com.emergetools.android.gradle.instrumentation.reaper.ReaperClassLoadClassVisitorFactory +import com.emergetools.android.gradle.tasks.distribution.registerDistributionTasks import com.emergetools.android.gradle.tasks.internal.SaveExtensionConfigTask import com.emergetools.android.gradle.tasks.perf.registerGeneratePerfProjectTask import com.emergetools.android.gradle.tasks.perf.registerPerformanceTasks @@ -96,9 +97,9 @@ class EmergePlugin : Plugin { registerSizeTasks(appProject, emergeExtension, variant) } - // Always register the Reaper initialization task even if Reaper is disabled since users use - // it to help get Reaper setup for the first time. + // Reaper and distribution handle the enabled checks themselves: registerReaperTasks(appProject, emergeExtension, variant) + registerDistributionTasks(appProject, emergeExtension, variant) registerReaperTransform( project = appProject, @@ -290,6 +291,17 @@ class EmergePlugin : Plugin { ) addItem("tag (optional): ${extension.reaperOptions.tag.orEmpty()}", reaperHeading) + val distributionHeading = addHeading("distribution") + addItem( + "enabledVariants: ${extension.distributionOptions.enabledVariants.getOrElse(emptyList())}", + distributionHeading + ) + addItem( + "apiKey: ${if (extension.distributionOptions.apiKey.isPresent) "*****" else "MISSING"}", + distributionHeading + ) + addItem("tag (optional): ${extension.distributionOptions.tag.orEmpty()}", distributionHeading) + val performanceHeading = addHeading("performance") addItem("projectPath: ${extension.perfOptions.projectPath.orEmpty()}", performanceHeading) addItem("tag (optional): ${extension.perfOptions.tag.orEmpty()}", performanceHeading) diff --git a/gradle-plugin/plugin/src/main/kotlin/com/emergetools/android/gradle/EmergePluginExtension.kt b/gradle-plugin/plugin/src/main/kotlin/com/emergetools/android/gradle/EmergePluginExtension.kt index 8c2ca34c..fce5f469 100644 --- a/gradle-plugin/plugin/src/main/kotlin/com/emergetools/android/gradle/EmergePluginExtension.kt +++ b/gradle-plugin/plugin/src/main/kotlin/com/emergetools/android/gradle/EmergePluginExtension.kt @@ -58,6 +58,13 @@ abstract class EmergePluginExtension @Inject constructor(objects: ObjectFactory) action.execute(reaperOptions) } + @get:Nested + abstract val distributionOptions: DistributionOptions + + fun distribution(action: Action) { + action.execute(distributionOptions) + } + @get:Nested abstract val vcsOptions: VCSOptions @@ -183,3 +190,9 @@ abstract class ReaperOptions : ProductOptions() { abstract val publishableApiKey: Property } + +abstract class DistributionOptions : ProductOptions() { + abstract val enabledVariants: ListProperty + + abstract val apiKey: Property +} diff --git a/gradle-plugin/plugin/src/main/kotlin/com/emergetools/android/gradle/tasks/distribution/DistributionPreflight.kt b/gradle-plugin/plugin/src/main/kotlin/com/emergetools/android/gradle/tasks/distribution/DistributionPreflight.kt new file mode 100644 index 00000000..d52c3169 --- /dev/null +++ b/gradle-plugin/plugin/src/main/kotlin/com/emergetools/android/gradle/tasks/distribution/DistributionPreflight.kt @@ -0,0 +1,72 @@ +package com.emergetools.android.gradle.tasks.distribution + +import com.emergetools.android.gradle.tasks.base.BasePreflightTask +import com.emergetools.android.gradle.util.preflight.Preflight +import com.emergetools.android.gradle.util.preflight.PreflightFailure +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Optional +import org.gradle.api.tasks.TaskAction + +abstract class DistributionPreflight : BasePreflightTask() { + + @get:Input + @get:Optional + abstract val hasEmergeApiToken: Property + + @get:Input + @get:Optional + abstract val distributionApiKey: Property + + @get:Input + @get:Optional + abstract val distributionTag: Property + + @get:Input + @get:Optional + abstract val distributionEnabled: Property + + @get:Input + abstract val variantName: Property + + @get:Input + abstract val hasDistributionImplementationDependency: Property + + @TaskAction + fun execute() { + val preflight = Preflight("Reaper preflight check") + + val hasEmergeApiToken = hasEmergeApiToken.getOrElse(false) + preflight.add("Emerge API token set") { + if (!hasEmergeApiToken) { + throw PreflightFailure("Emerge API token not set. See https://docs.emergetools.com/docs/uploading-basics#obtain-an-api-key") + } + } + + val variantName = variantName.get() + preflight.add("enabled for variant: $variantName") { + if (!distributionEnabled.getOrElse(false)) { + throw PreflightFailure("Distribution not enabled for variant $variantName. Make sure \"${variantName}\" is included in `distribution.enabledVariants`") + } + } + + preflight.add("apiKey set") { + val key = distributionApiKey.orNull + if (key == null) { + throw PreflightFailure("apiKey not set. See https://docs.emergetools.com/docs/distribution-setup-android#configure-the-sdk") + } + if (key == "") { + throw PreflightFailure("apiKey must not be empty. See https://docs.emergetools.com/docs/distribution-setup-android#configure-the-sdk") + } + } + + preflight.add("Runtime SDK added") { + if (!hasDistributionImplementationDependency.getOrElse(false)) { + throw PreflightFailure("Distribution runtime SDK missing as an implementation dependency. See https://docs.emergetools.com/docs/distribution-setup-android#install-the-sdk") + } + } + + preflight.addSubPreflight(buildVcsPreflight()) + preflight.logOutput(logger) + } +} diff --git a/gradle-plugin/plugin/src/main/kotlin/com/emergetools/android/gradle/tasks/distribution/Register.kt b/gradle-plugin/plugin/src/main/kotlin/com/emergetools/android/gradle/tasks/distribution/Register.kt new file mode 100644 index 00000000..e69de29b diff --git a/gradle-plugin/plugin/src/main/kotlin/com/emergetools/android/gradle/tasks/reaper/Register.kt b/gradle-plugin/plugin/src/main/kotlin/com/emergetools/android/gradle/tasks/reaper/Register.kt index 0b415f21..3ed253df 100644 --- a/gradle-plugin/plugin/src/main/kotlin/com/emergetools/android/gradle/tasks/reaper/Register.kt +++ b/gradle-plugin/plugin/src/main/kotlin/com/emergetools/android/gradle/tasks/reaper/Register.kt @@ -10,9 +10,9 @@ import com.emergetools.android.gradle.util.capitalize import com.emergetools.android.gradle.util.hasDependency import org.gradle.api.Project -const val EMERGE_REAPER_TASK_GROUP = "Emerge reaper" -const val REAPER_DEP_GROUP = "com.emergetools.reaper" -const val REAPER_DEP_NAME = "reaper" +private const val EMERGE_REAPER_TASK_GROUP = "Emerge reaper" +private const val REAPER_DEP_GROUP = "com.emergetools.reaper" +private const val REAPER_DEP_NAME = "reaper" fun registerReaperTasks( appProject: Project, @@ -20,7 +20,7 @@ fun registerReaperTasks( variant: Variant, ) { appProject.logger.debug( - "Registering reaper tasks for variant ${variant.name} in project ${appProject.path}" + "Registering Reaper tasks for variant ${variant.name} in project ${appProject.path}" ) registerReaperPreflightTask(appProject, extension, variant) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d5028623..003f5635 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -24,11 +24,11 @@ runtime-android = "1.7.3" foundation-layout-android = "1.7.3" # internal -emerge-gradle-plugin = "4.0.2" +emerge-gradle-plugin = "4.0.3" emerge-performance = "2.1.2" emerge-reaper = "1.0.0-rc03" emerge-snapshots = "1.3.0-rc02" -emerge-distribution = "0.0.1" +emerge-distribution = "0.0.2" [plugins] android-application = { id = "com.android.application", version.ref = "agp" }