Skip to content

Commit

Permalink
Fix broken apk producer tasks
Browse files Browse the repository at this point in the history
  • Loading branch information
jeppeman committed Sep 8, 2024
1 parent 5ab3070 commit 5c5d99e
Show file tree
Hide file tree
Showing 7 changed files with 258 additions and 35 deletions.
2 changes: 1 addition & 1 deletion globallydynamic-android-lib/deps.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def versions = [
play : '1.10.3',
dynamicability : '1.0.17.300',
autoservice : '1.0-rc6',
globallydynamic_gradle: '1.8.0',
globallydynamic_gradle: '1.9.0',
uiautomator : '2.2.0',
javadoc : '0.3.0'
]
Expand Down
2 changes: 1 addition & 1 deletion globallydynamic-android-lib/minimal-sample/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ buildscript {
}
dependencies {
classpath "com.android.tools.build:gradle:8.6.0"
classpath "com.jeppeman.globallydynamic.gradle:plugin:1.8.0"
classpath "com.jeppeman.globallydynamic.gradle:plugin:1.9.0"
}
}

Expand Down
2 changes: 1 addition & 1 deletion globallydynamic-gradle-plugin/gradle.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
GROUP=com.jeppeman.globallydynamic.gradle
VERSION_NAME=1.9.0-SNAPSHOT
VERSION_NAME=1.10.0-SNAPSHOT

POM_DESCRIPTION=GloballyDynamic - Gradle plugin to facilitate for local dynamic delivery for Android.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ import com.google.common.util.concurrent.MoreExecutors
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.JsonObject
import com.jeppeman.globallydynamic.gradle.extensions.*
import com.jeppeman.globallydynamic.gradle.extensions.deleteCompletely
import com.jeppeman.globallydynamic.gradle.extensions.getTaskName
import com.jeppeman.globallydynamic.gradle.extensions.unzip
import org.gradle.api.DefaultTask
import org.gradle.api.Project
Expand All @@ -41,7 +41,8 @@ abstract class ApkProducerTask : DefaultTask() {
private fun JsonObject.getPropertyCompat(propName: String) =
get(propName) ?: get("m${propName.capitalize()}")

protected open val buildMode: BuildApksCommand.ApkBuildMode = BuildApksCommand.ApkBuildMode.DEFAULT
@get:Input
protected abstract val buildMode: BuildApksCommand.ApkBuildMode
protected abstract fun processApkSet(apkSet: Path)

@get:InputFiles
Expand All @@ -54,7 +55,6 @@ abstract class ApkProducerTask : DefaultTask() {
private set

@get:OutputDirectory
@get:PathSensitive(PathSensitivity.ABSOLUTE)
lateinit var outputDir: File
protected set

Expand All @@ -65,6 +65,7 @@ abstract class ApkProducerTask : DefaultTask() {
@get:Nested
abstract val aapt2: Aapt2Input

@get:Input
lateinit var variantName: String
private set

Expand Down Expand Up @@ -141,19 +142,8 @@ abstract class ApkProducerTask : DefaultTask() {
override fun execute(task: T) {
task.variantName = applicationVariant.name
task.signed = signed
task.bundleDir = task.project.buildDir
.toPath()
.resolve("intermediates")
.resolve("intermediary_bundle")
.resolve(applicationVariant.name)
.toFile()
task.signingConfig = task.project.buildDir
.toPath()
.resolve("intermediates")
.resolve("signing_config_data")
.resolve(applicationVariant.name)
.resolve("signing-config-data.json")
.toFile()
task.bundleDir = task.project.intermediaryBundleDir(applicationVariant)
task.signingConfig = task.project.intermediarySigningConfig(applicationVariant)
createProjectServices(task.project).initializeAapt2Input(task.aapt2)
}
}
Expand Down Expand Up @@ -196,6 +186,8 @@ abstract class BuildUniversalApkTask : ApkProducerTask() {
}

abstract class BuildBaseApkTask : ApkProducerTask() {
override val buildMode: BuildApksCommand.ApkBuildMode = BuildApksCommand.ApkBuildMode.DEFAULT

override fun processApkSet(apkSet: Path) {
val tempDir = Paths.get(outputDir.absolutePath, "temp")
tempDir.deleteCompletely()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import com.android.build.gradle.api.ApplicationVariant
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.JsonObject
import com.jeppeman.globallydynamic.gradle.extensions.getTaskName
import com.jeppeman.globallydynamic.gradle.extensions.*
import com.jeppeman.globallydynamic.gradle.extensions.stackTraceToString
import com.jeppeman.globallydynamic.gradle.extensions.toBase64
import org.apache.http.HttpException
Expand Down Expand Up @@ -145,21 +145,8 @@ open class UploadBundleTask : DefaultTask() {
task.applicationId = applicationVariant.applicationId
task.version = applicationVariant.versionCode
task.serverInfo = task.project.resolveServerInfo(extension)
task.bundleDir = task.project.buildDir
.toPath()
.resolve("intermediates")
.resolve("intermediary_bundle")
.resolve(applicationVariant.name)
.resolve(applicationVariant.getTaskName("package", "Bundle"))
.toFile()
task.signingConfig = task.project.buildDir
.toPath()
.resolve("intermediates")
.resolve("signing_config_data")
.resolve(applicationVariant.name)
.resolve(applicationVariant.getTaskName("signingConfigWriter"))
.resolve("signing-config-data.json")
.toFile()
task.bundleDir = task.project.intermediaryBundleDir(applicationVariant)
task.signingConfig = task.project.intermediarySigningConfig(applicationVariant)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.jeppeman.globallydynamic.gradle.extensions

import com.android.build.gradle.api.ApplicationVariant
import org.gradle.api.Project
import java.io.File

fun Project.intermediaryBundleDir(variant: ApplicationVariant): File = buildDir
.toPath()
.resolve("intermediates")
.resolve("intermediary_bundle")
.resolve(variant.name)
.resolve(variant.getTaskName("package", "Bundle"))
.toFile()

fun Project.intermediarySigningConfig(variant: ApplicationVariant): File = buildDir
.toPath()
.resolve("intermediates")
.resolve("signing_config_data")
.resolve(variant.name)
.resolve(variant.getTaskName("signingConfigWriter"))
.resolve("signing-config-data.json")
.toFile()
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
package com.jeppeman.globallydynamic.gradle

import com.google.common.truth.Truth.assertThat
import com.squareup.javapoet.ClassName
import com.squareup.javapoet.JavaFile
import com.squareup.javapoet.TypeSpec
import org.gradle.testkit.runner.TaskOutcome
import org.junit.jupiter.api.Test
import org.junit.platform.runner.JUnitPlatform
import org.junit.runner.RunWith
import java.nio.file.Paths
import java.util.zip.ZipFile
import kotlin.io.path.exists
import kotlin.io.path.fileSize

abstract class ApkProducerTasksTest : BaseTaskTest() {
override val installTimeFeatureName: String = "installtimefeature"
override val onDemandFeatureName: String = "ondemandfeature"

protected val appModuleOutputsDir get() = appModuleProjectDirPath.resolve("build").resolve("outputs")

override fun beforeEach() {
val testResPath = Paths.get("src", "test", "resources")
val keyStoreFile = Paths.get(testResPath.toString(), "test.keystore")
val keyStorePath = rootProjectDirPath.resolve("test.keystore").apply {
toFile().writeBytes(keyStoreFile.toFile().inputStream().readBytes())
}
val typeSpec = TypeSpec.classBuilder("GloballyDynamicActivity")
.superclass(ClassName.get("androidx.appcompat.app", "AppCompatActivity"))
.build()

val stringsFile = appModuleSourceDir.resolve("res")
.resolve("values")
.apply { toFile().mkdirs() }
.resolve("strings.xml")

stringsFile.toFile().writeText(
"""
<resources>
<string name="title_installtimefeature">Install Time Feature</string>
<string name="title_ondemandfeature">On Demand Feature</string>
</resources>
""".trimIndent()
)

JavaFile.builder(BASE_PACKAGE_NAME, typeSpec)
.build()
.writeTo(appModuleSourceDir)

appModuleAndroidManifestFilePath.toFile().writeText(
"""
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application />
</manifest>
""".trimIndent()
)

appModuleBuildFilePath.toFile().writeText(
"""
plugins {
id 'com.android.application'
id 'com.jeppeman.globallydynamic'
}
android {
namespace '$BASE_PACKAGE_NAME'
compileSdk 32
defaultConfig {
minSdk 16
targetSdk 32
versionCode $VERSION_CODE
}
signingConfigs {
debug {
storeFile file('$keyStorePath')
storePassword 'android'
keyAlias 'androiddebugkey'
keyPassword 'android'
}
}
dynamicFeatures = [':$onDemandFeatureName', ':$installTimeFeatureName']
}
dependencies {
implementation 'com.jeppeman.globallydynamic.android:gplay:${ANDROID_LIB_VERSION}'
implementation 'androidx.appcompat:appcompat:1.4.2'
}
""".trimIndent()
)

onDemandFeatureAndroidManifestFilePath.toFile().writeText(
"""
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:dist="http://schemas.android.com/apk/distribution">
<application/>
<dist:module
dist:instant="false"
dist:title="@string/title_ondemandfeature">
<dist:delivery>
<dist:on-demand />
</dist:delivery>
<dist:fusing dist:include="true" />
</dist:module>
</manifest>
""".trimIndent()
)

installTimeFeatureAndroidManifestFilePath.toFile().writeText(
"""
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:dist="http://schemas.android.com/apk/distribution">
<application/>
<dist:module
dist:instant="false"
dist:title="@string/title_installtimefeature">
<dist:delivery>
<dist:install-time>
<dist:conditions>
<dist:user-countries dist:exclude="false">
<dist:country dist:code="CN"/>
<dist:country dist:code="HK"/>
</dist:user-countries>
<dist:device-feature dist:name="android.hardware.feature.ar"/>
<dist:device-feature dist:name="android.hardware.feature.vr"/>
<dist:min-sdk dist:value="16"/>
</dist:conditions>
</dist:install-time>
</dist:delivery>
<dist:fusing dist:include="true" />
</dist:module>
</manifest>
""".trimIndent()
)
}
}

private const val VERSION_CODE = 1
private const val VARIANT = "debug"

@RunWith(JUnitPlatform::class)
class BuildUniversalApkTaskTest : ApkProducerTasksTest() {
override val taskName: String = ":app:buildUniversalApkFor${VARIANT.capitalize()}"

private val producedApk get() = appModuleOutputsDir.resolve("universal_apk")
.resolve(VARIANT)
.resolve("universal.apk")

@Test
fun `task should produce a universal apk`() {
val result = runTask()

assertThat(result.task(taskName)?.outcome).isEqualTo(TaskOutcome.SUCCESS)
assertThat(producedApk.exists()).isTrue()
assertThat(producedApk.fileSize()).isGreaterThan(0L)
assertThat(ZipFile(producedApk.toFile()).entries().asSequence().any { it.name.matches("META-INF/.+\\.RSA".toRegex()) }).isTrue()
}
}

@RunWith(JUnitPlatform::class)
class BuildUnsignedUniversalApkTaskTest : ApkProducerTasksTest() {
override val taskName: String = ":app:buildUnsignedUniversalApkFor${VARIANT.capitalize()}"

private val producedApk get() = appModuleOutputsDir.resolve("universal_apk")
.resolve(VARIANT)
.resolve("universal-unsigned.apk")

@Test
fun `task should produce an unsigned universal apk`() {
val result = runTask()

assertThat(result.task(taskName)?.outcome).isEqualTo(TaskOutcome.SUCCESS)
assertThat(producedApk.exists()).isTrue()
assertThat(producedApk.fileSize()).isGreaterThan(0L)
assertThat(ZipFile(producedApk.toFile()).entries().asSequence().any { it.name.matches("META-INF/.+\\.RSA".toRegex()) }).isFalse()
}
}

@RunWith(JUnitPlatform::class)
class BuildBaseApkTaskTest : ApkProducerTasksTest() {
override val taskName: String = ":app:buildBaseApkFor${VARIANT.capitalize()}"

private val producedApk get() = appModuleOutputsDir.resolve("base_apk")
.resolve(VARIANT)
.resolve("base-master.apk")

@Test
fun `task should produce a base apk`() {
val result = runTask()

assertThat(result.task(taskName)?.outcome).isEqualTo(TaskOutcome.SUCCESS)
assertThat(producedApk.exists()).isTrue()
assertThat(producedApk.fileSize()).isGreaterThan(0L)
assertThat(ZipFile(producedApk.toFile()).entries().asSequence().any { it.name.matches("META-INF/.+\\.RSA".toRegex()) }).isTrue()
}
}

@RunWith(JUnitPlatform::class)
class BuildUnsignedBaseApkTaskTest : ApkProducerTasksTest() {
override val taskName: String = ":app:buildUnsignedBaseApkFor${VARIANT.capitalize()}"

private val producedApk get() = appModuleOutputsDir.resolve("base_apk")
.resolve(VARIANT)
.resolve("base-master-unsigned.apk")

@Test
fun `task should produce an unsigned base apk`() {
val result = runTask()

assertThat(result.task(taskName)?.outcome).isEqualTo(TaskOutcome.SUCCESS)
assertThat(producedApk.exists()).isTrue()
assertThat(producedApk.fileSize()).isGreaterThan(0L)
assertThat(ZipFile(producedApk.toFile()).entries().asSequence().any { it.name.matches("META-INF/.+\\.RSA".toRegex()) }).isFalse()
}
}

0 comments on commit 5c5d99e

Please sign in to comment.