Skip to content

Commit

Permalink
Implement Google Play upload task
Browse files Browse the repository at this point in the history
  • Loading branch information
Alexander Ivanov authored and slmlt committed Apr 15, 2024
1 parent 14b1b33 commit de05342
Show file tree
Hide file tree
Showing 16 changed files with 1,493 additions and 7 deletions.
4 changes: 4 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ moshi = "1.15.0"
retrofit = "2.9.0"
junit = "4.13.2"
ksp = "1.9.23-1.0.19"
play-publish = "v3-rev20231115-2.0.0"
google-auth = "1.20.0"

[libraries]
agp = { module = "com.android.tools.build:gradle", version.ref = "agp" }
Expand All @@ -29,6 +31,8 @@ kotlin-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.
ktlint-plugin = { module = "org.jlleitschuh.gradle:ktlint-gradle", version.ref = "ktlint" }
detekt-plugin = { module = "io.gitlab.arturbosch.detekt:detekt-gradle-plugin", version.ref = "detekt" }
ksp-plugin = { module = "com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin", version.ref = "ksp" }
play-publish = { module = "com.google.apis:google-api-services-androidpublisher", version.ref = "play-publish" }
google-auth = { module = "com.google.auth:google-auth-library-oauth2-http", version.ref = "google-auth" }

[plugins]
agp = { id = "com.android.application", version.ref = "agp" }
Expand Down
6 changes: 4 additions & 2 deletions plugin-build/plugin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ dependencies {
implementation(libs.retrofitMoshi)
implementation(libs.grgitCore)
implementation(libs.grgitGradle)
implementation(libs.play.publish)
implementation(libs.google.auth)

testImplementation(libs.junit)

Expand All @@ -31,7 +33,7 @@ gradlePlugin {
id = "ru.kode.android.build-publish"
displayName = "Configure project with Firebase App Distribution and changelogs"
implementationClass = "ru.kode.android.build.publish.plugin.BuildPublishPlugin"
version = project.version
version = "1.3.2"
description = "Android plugin to publish bundles and apks to Firebase App Distribution with changelogs"
tags.set(listOf("firebase", "publish", "changelog", "build"))
}
Expand All @@ -43,7 +45,7 @@ publishing {
create<MavenPublication>("maven") {
groupId = project.group.toString()
artifactId = "ru.kode.android.build-publish".removePrefix("$groupId.")
version = project.version.toString()
version = "1.3.2"

from(components["java"])
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import com.android.build.api.variant.ApplicationAndroidComponentsExtension
import com.android.build.api.variant.impl.VariantOutputImpl
import com.android.build.gradle.AppExtension
import com.android.build.gradle.AppPlugin
import com.android.build.gradle.internal.tasks.PackageBundleTask
import com.android.build.gradle.tasks.PackageAndroidArtifact
import com.android.builder.model.Version.ANDROID_GRADLE_PLUGIN_VERSION
import com.google.firebase.appdistribution.gradle.AppDistributionExtension
Expand All @@ -18,6 +19,7 @@ import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.file.RegularFile
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.logging.Logging
import org.gradle.api.provider.Provider
import org.gradle.api.provider.SetProperty
import org.gradle.api.tasks.StopExecutionException
Expand All @@ -33,11 +35,13 @@ import ru.kode.android.build.publish.plugin.extension.config.ChangelogConfig
import ru.kode.android.build.publish.plugin.extension.config.FirebaseAppDistributionConfig
import ru.kode.android.build.publish.plugin.extension.config.JiraConfig
import ru.kode.android.build.publish.plugin.extension.config.OutputConfig
import ru.kode.android.build.publish.plugin.extension.config.PlayConfig
import ru.kode.android.build.publish.plugin.extension.config.SlackConfig
import ru.kode.android.build.publish.plugin.extension.config.TelegramConfig
import ru.kode.android.build.publish.plugin.task.appcenter.AppCenterDistributionTask
import ru.kode.android.build.publish.plugin.task.changelog.GenerateChangelogTask
import ru.kode.android.build.publish.plugin.task.jira.JiraAutomationTask
import ru.kode.android.build.publish.plugin.task.play.PlayDistributionTask
import ru.kode.android.build.publish.plugin.task.slack.changelog.SendSlackChangelogTask
import ru.kode.android.build.publish.plugin.task.slack.distribution.SlackDistributionTask
import ru.kode.android.build.publish.plugin.task.tag.GetLastTagTask
Expand All @@ -47,6 +51,7 @@ import ru.kode.android.build.publish.plugin.util.capitalizedName
import java.io.File
import java.time.LocalDate
import java.time.format.DateTimeFormatter
import java.util.logging.Logger

internal const val SEND_SLACK_CHANGELOG_TASK_PREFIX = "sendSlackChangelog"
internal const val SEND_TELEGRAM_CHANGELOG_TASK_PREFIX = "sendTelegramChangelog"
Expand All @@ -59,6 +64,7 @@ internal const val DEFAULT_VERSION_CODE = 1
internal const val DEFAULT_BASE_FILE_NAME = "dev-build"
internal const val CHANGELOG_FILENAME = "changelog.txt"
internal const val APP_CENTER_DISTRIBUTION_UPLOAD_TASK_PREFIX = "appCenterDistributionUpload"
internal const val PLAY_DISTRIBUTION_UPLOAD_TASK_PREFIX = "playUpload"
internal const val SLACK_DISTRIBUTION_UPLOAD_TASK_PREFIX = "slackDistributionUpload"
internal const val JIRA_AUTOMATION_TASK = "jiraAutomation"
internal const val DEFAULT_CONTAINER_NAME = "default"
Expand Down Expand Up @@ -87,6 +93,10 @@ abstract class BuildPublishPlugin : Plugin<Project> {

androidExtension.onVariants(
callback = { variant ->
Logging.getLogger(this::class.java).info("FOUND VARIANT: $variant WITH OUTPUT NAME ${variant.outputs
.find { it is VariantOutputImpl && it.fullName == variant.name }
as? VariantOutputImpl}")

val output =
variant.outputs
.find { it is VariantOutputImpl && it.fullName == variant.name }
Expand Down Expand Up @@ -239,6 +249,21 @@ abstract class BuildPublishPlugin : Plugin<Project> {
)
tasks.registerAppCenterDistributionTask(params)
}
val playConfig =
with(buildPublishExtension.play) {
findByName(buildVariant.name) ?: findByName(DEFAULT_CONTAINER_NAME)
}
if (playConfig != null) {
val params =
PlayTaskParams(
config = playConfig,
buildVariant = buildVariant,
buildVariantOutputFileProvider = outputFileProvider,
tagBuildProvider = tagBuildProvider,
outputConfig = outputConfig,
)
tasks.registerPlayTask(params)
}
val jiraConfig =
with(buildPublishExtension.jira) {
findByName(buildVariant.name) ?: findByName(DEFAULT_CONTAINER_NAME)
Expand Down Expand Up @@ -461,6 +486,25 @@ abstract class BuildPublishPlugin : Plugin<Project> {
it.uploadStatusRequestDelayCoefficient.set(config.uploadStatusRequestDelayCoefficient)
}
}

private fun TaskContainer.registerPlayTask(
params: PlayTaskParams,
): TaskProvider<PlayDistributionTask> {
val buildVariant = params.buildVariant
val config = params.config

return register(
"$PLAY_DISTRIBUTION_UPLOAD_TASK_PREFIX${buildVariant.capitalizedName()}",
PlayDistributionTask::class.java,
) {
it.tagBuildFile.set(params.tagBuildProvider)
it.buildVariantOutputFile.set(params.buildVariantOutputFileProvider)
it.apiTokenFile.set(config.apiTokenFile)
it.appId.set(config.appId)
it.trackId.set(config.trackId)
it.updatePriority.set(config.updatePriority)
}
}
}

private fun Project.configurePlugins(
Expand Down Expand Up @@ -552,6 +596,15 @@ private data class AppCenterDistributionTaskParams(
val outputConfig: OutputConfig,
)


private data class PlayTaskParams(
val config: PlayConfig,
val buildVariant: BuildVariant,
val buildVariantOutputFileProvider: Provider<RegularFile>,
val tagBuildProvider: Provider<RegularFile>,
val outputConfig: OutputConfig,
)

private fun mapToVersionCode(tagBuildFile: RegularFile): Int {
val file = tagBuildFile.asFile
return if (file.exists()) {
Expand All @@ -573,8 +626,15 @@ private fun mapToOutputFileName(
val versionName = tagBuild.buildVariant
val versionCode = tagBuild.buildNumber
"$baseFileName-$versionName-vc$versionCode-$formattedDate.apk"
} else if (file.exists() && outputFileName.endsWith(".aab")) {
val tagBuild = fromJson(file)
val versionName = tagBuild.buildVariant
val versionCode = tagBuild.buildNumber
"$baseFileName-$versionName-vc$versionCode-$formattedDate.aab"
} else if (!file.exists() && outputFileName.endsWith(".apk")) {
"$baseFileName-$formattedDate.apk"
} else if (!file.exists() && outputFileName.endsWith(".aab")) {
"$baseFileName-$formattedDate.aab"
} else {
createDefaultOutputFileName(baseFileName, outputFileName)
}
Expand Down Expand Up @@ -604,9 +664,16 @@ private fun Project.mapToOutputFile(
buildVariant: BuildVariant,
fileName: String,
): Provider<RegularFile> {
return project.tasks.withType(PackageAndroidArtifact::class.java)
.firstOrNull { it.variantName == buildVariant.name }
?.outputDirectory
?.map { directory -> directory.file(fileName) }
?: throw GradleException("no output for variant ${buildVariant.name}")
return if (fileName.endsWith("aab")) {
project.tasks.withType(PackageBundleTask::class.java)
.firstOrNull { it.variantName == buildVariant.name }
?.bundleFile
?: throw GradleException("no output for variant ${buildVariant.name}")
} else {
project.tasks.withType(PackageAndroidArtifact::class.java)
.firstOrNull { it.variantName == buildVariant.name }
?.outputDirectory
?.map { directory -> directory.file(fileName) }
?: throw GradleException("no output for variant ${buildVariant.name}")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ import ru.kode.android.build.publish.plugin.extension.config.ChangelogConfig
import ru.kode.android.build.publish.plugin.extension.config.FirebaseAppDistributionConfig
import ru.kode.android.build.publish.plugin.extension.config.JiraConfig
import ru.kode.android.build.publish.plugin.extension.config.OutputConfig
import ru.kode.android.build.publish.plugin.extension.config.PlayConfig
import ru.kode.android.build.publish.plugin.extension.config.SlackConfig
import ru.kode.android.build.publish.plugin.extension.config.TelegramConfig
import ru.kode.android.build.publish.plugin.task.play.PlayDistributionTask
import javax.inject.Inject

const val EXTENSION_NAME = "buildPublish"
Expand All @@ -31,4 +33,6 @@ abstract class BuildPublishExtension
objectFactory.domainObjectContainer(FirebaseAppDistributionConfig::class.java)
val appCenterDistribution: NamedDomainObjectContainer<AppCenterDistributionConfig> =
objectFactory.domainObjectContainer(AppCenterDistributionConfig::class.java)
val play: NamedDomainObjectContainer<PlayConfig> =
objectFactory.domainObjectContainer(PlayConfig::class.java)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package ru.kode.android.build.publish.plugin.extension.config

import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.Property
import org.gradle.api.provider.SetProperty
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFile

interface PlayConfig {
val name: String

/**
* The path to file with token for Google Play project
*/
@get:InputFile
val apiTokenFile: RegularFileProperty

/**
* appId in Google Play
*/
@get:Input
val appId: Property<String>

/**
* Track name of target app. Defaults to "internal"
*/
@get:Input
val trackId: Property<String>

/**
* Test groups for app distribution
*
* For example: [android-testers]
*/
@get:Input
val updatePriority: Property<Int>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package ru.kode.android.build.publish.plugin.task.play

import org.gradle.api.DefaultTask
import org.gradle.api.GradleException
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.plugins.BasePlugin
import org.gradle.api.provider.Property
import org.gradle.api.provider.SetProperty
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.TaskAction
import org.gradle.api.tasks.options.Option
import org.gradle.workers.WorkQueue
import org.gradle.workers.WorkerExecutor
import ru.kode.android.build.publish.plugin.enity.mapper.fromJson
import ru.kode.android.build.publish.plugin.extension.config.MAX_REQUEST_COUNT
import ru.kode.android.build.publish.plugin.extension.config.MAX_REQUEST_DELAY_MS
import ru.kode.android.build.publish.plugin.task.appcenter.work.AppCenterUploadWork
import ru.kode.android.build.publish.plugin.task.play.work.PlayUploadWork
import ru.kode.android.build.publish.plugin.util.capitalized
import javax.inject.Inject

abstract class PlayDistributionTask
@Inject
constructor(
private val workerExecutor: WorkerExecutor,
) : DefaultTask() {
init {
description = "Task to send apk to Google Play"
group = BasePlugin.BUILD_GROUP
}

@get:InputFile
@get:Option(
option = "buildVariantOutputFile",
description = "Artifact output file (absolute path is expected)",
)
abstract val buildVariantOutputFile: RegularFileProperty

@get:InputFile
@get:Option(option = "tagBuildFile", description = "Json contains info about tag build")
abstract val tagBuildFile: RegularFileProperty

@get:InputFile
@get:Option(
option = "apiTokenFile",
description = "API token for google play console",
)
abstract val apiTokenFile: RegularFileProperty

@get:Input
@get:Option(
option = "appId",
description = "appId from Play Console",
)
abstract val appId: Property<String>


@get:Input
@get:Option(
option = "trackId",
description = "Track name of target app. Defaults to internal",
)
abstract val trackId: Property<String>

@get:Input
@get:Optional
@get:Option(
option = "updatePriority",
description = "Update priority (0..5)",
)
abstract val updatePriority: Property<Int>

@TaskAction
fun upload() {
val outputFile = buildVariantOutputFile.asFile.get()
if (outputFile.extension != "aab") throw GradleException("file ${outputFile.path} is not bundle")
val tag = fromJson(tagBuildFile.asFile.get())
val releaseName = "${tag.name}(${tag.buildVersion}.${tag.buildNumber})"
val apiTokenFile = apiTokenFile.asFile.get()
val workQueue: WorkQueue = workerExecutor.noIsolation()
val trackId = trackId.orNull ?: "internal"
val appId = appId.get()
val updatePriority = updatePriority.orNull ?: 0

workQueue.submit(PlayUploadWork::class.java) { parameters ->
parameters.appId.set(appId)
parameters.apiToken.set(apiTokenFile)
parameters.trackId.set(trackId)
parameters.updatePriority.set(updatePriority)
parameters.releaseName.set(releaseName)
parameters.outputFile.set(outputFile)
}
}
}
Loading

0 comments on commit de05342

Please sign in to comment.