Skip to content

Commit

Permalink
Add dependency and module data (#142)
Browse files Browse the repository at this point in the history
* Add dependency and module data

* Updates

* Add comment

* Add includeDependencyInformation to turn off deps, make config cache compliant

* Updates and json changes

* Fix

* Working but not polymormphic

* Working but not polymormphic

* Switch back to polymorphism

* Working

* Fixes

* Remove leading /
  • Loading branch information
rbro112 authored Jul 26, 2024
1 parent f94c9e1 commit bec895c
Show file tree
Hide file tree
Showing 8 changed files with 272 additions and 47 deletions.
11 changes: 4 additions & 7 deletions gradle-plugin/plugin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -125,23 +125,20 @@ dependencies {
compileOnly(libs.android.gradle.plugin)
compileOnly(libs.kotlin.gradle.plugin)

implementation(libs.asm)
implementation(libs.asm.commons)
implementation(libs.dexlib2)
implementation(libs.kotlinx.datetime)
implementation(libs.kotlinx.serialization)
implementation(libs.okhttp)
// Needed for the GradleRunner in the functional tests. We've had issues with the version of Guava
// from one dependency conflicting with that of dexlib2, so we'll use the same version here.
implementation(libs.guava)
implementation(libs.okhttp)
implementation(libs.kotlinx.datetime)
implementation(libs.kotlinx.serialization)
implementation(libs.asm)
implementation(libs.asm.commons)
implementation(libs.okhttp)

testImplementation(libs.google.truth)
testImplementation(libs.junit)
testImplementation(libs.junit.jupiter)
testImplementation(libs.junit.jupiter.api)
testImplementation(libs.google.truth)
testRuntimeOnly(libs.junit.jupiter.engine)
testRuntimeOnly(libs.junit.platform.launcher)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ class EmergePlugin : Plugin<Project> {
it.description = "Builds and uploads an APK for variant ${variant.name} to Emerge."
it.artifactDir.set(variant.artifacts.get(SingleArtifact.APK))
it.proguardMapping.set(variant.artifacts.get(SingleArtifact.OBFUSCATION_MAPPING_FILE))
it.setUploadTaskInputs(extension, appProject)
it.setUploadTaskInputs(extension, appProject, variant)
it.setTagFromProductOptions(extension.sizeOptions, variant)
}
}
Expand All @@ -214,7 +214,7 @@ class EmergePlugin : Plugin<Project> {
it.group = EMERGE_TASK_GROUP
it.description = "Builds and uploads an AAB for variant ${variant.name} to Emerge."
it.artifact.set(variant.artifacts.get(SingleArtifact.BUNDLE))
it.setUploadTaskInputs(extension, appProject)
it.setUploadTaskInputs(extension, appProject, variant)
it.setTagFromProductOptions(extension.sizeOptions, variant)
}
}
Expand Down Expand Up @@ -247,7 +247,7 @@ class EmergePlugin : Plugin<Project> {
it.group = EMERGE_TASK_GROUP
it.description = "Confirms Reaper is initialized and uploads an AAB for variant ${variant.name} to Emerge."
it.artifact.set(variant.artifacts.get(SingleArtifact.BUNDLE))
it.setUploadTaskInputs(extension, appProject)
it.setUploadTaskInputs(extension, appProject, variant)
it.setTagFromProductOptions(extension.reaperOptions, variant)
it.dependsOn(preflightTask)
}
Expand Down Expand Up @@ -283,7 +283,7 @@ class EmergePlugin : Plugin<Project> {
"Emerge with ${performanceProject.name} test APK."
it.artifact.set(appVariant.artifacts.get(SingleArtifact.BUNDLE))
it.perfArtifactDir.set(performanceVariant.artifacts.get(SingleArtifact.APK))
it.setUploadTaskInputs(extension, appProject)
it.setUploadTaskInputs(extension, appProject, appVariant)
it.setTagFromProductOptions(extension.perfOptions, appVariant)
}
}
Expand Down Expand Up @@ -454,7 +454,7 @@ class EmergePlugin : Plugin<Project> {
packageTask.flatMap { packageTask -> packageTask.artifactMetadataPath })
it.apiVersion.set(extension.snapshotOptions.apiVersion)
it.includePrivatePreviews.set(extension.snapshotOptions.includePrivatePreviews)
it.setUploadTaskInputs(extension, appProject)
it.setUploadTaskInputs(extension, appProject, variant)
it.setTagFromProductOptions(extension.snapshotOptions, variant)
it.dependsOn(packageTask)
}
Expand Down Expand Up @@ -560,6 +560,7 @@ class EmergePlugin : Plugin<Project> {
= Emerge configuration =
========================
apiToken: ${if (extension.apiToken.isPresent) "*****" else "MISSING"}
includeDependencyInformation: ${extension.includeDependencyInformation.orElse(true)}
dryRun (optional): ${extension.dryRun.orEmpty()}
verbose (optional): ${extension.verbose.orEmpty()}
size
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ abstract class EmergePluginExtension @Inject constructor(objects: ObjectFactory)
val apiToken: Property<String> = objects.property<String>()
.convention(System.getenv(BaseUploadTask.DEFAULT_API_TOKEN_ENV_KEY))

val includeDependencyInformation: Property<Boolean> = objects.property<Boolean>()
.convention(true)

@get:Nested
abstract val sizeOptions: SizeOptions

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ data class ArtifactMetadata(
val targetArtifactZipPath: String,
val testArtifactZipPath: String? = null,
val proguardMappingsZipPath: String? = null,
// TODO: Source
val dependencyMetadataZipPath: String? = null
) {

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ import com.emergetools.android.gradle.EmergePlugin
import com.emergetools.android.gradle.EmergePluginExtension
import com.emergetools.android.gradle.ProductOptions
import com.emergetools.android.gradle.util.AgpVersions
import com.emergetools.android.gradle.util.dependencies.ARTIFACT_ASSETS
import com.emergetools.android.gradle.util.dependencies.ARTIFACT_CLASSES
import com.emergetools.android.gradle.util.dependencies.ARTIFACT_JNI
import com.emergetools.android.gradle.util.dependencies.ARTIFACT_RESOURCES
import com.emergetools.android.gradle.util.dependencies.Dependencies
import com.emergetools.android.gradle.util.dependencies.buildDependencies
import com.emergetools.android.gradle.util.network.EmergeUploadRequestData
import com.emergetools.android.gradle.util.network.EmergeUploadResponse
import com.emergetools.android.gradle.util.network.SOURCE_GRADLE_PLUGIN
Expand All @@ -17,6 +23,8 @@ import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.OkHttpClient
import org.gradle.api.DefaultTask
import org.gradle.api.Project
import org.gradle.api.artifacts.ArtifactCollection
import org.gradle.api.attributes.Attribute
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Input
Expand Down Expand Up @@ -77,6 +85,23 @@ abstract class BaseUploadTask : DefaultTask() {
@get:Optional
abstract val gitLabProjectId: Property<String>

@get:Input
abstract val includeDependencyInformation: Property<Boolean>

@get:Input
@get:Optional
abstract val appModuleName: Property<String>

@get:Input
@get:Optional
abstract val appModulePath: Property<String>

private lateinit var artifacts: List<ArtifactCollection>

fun setArtifacts(artifacts: List<ArtifactCollection>) {
this.artifacts = artifacts
}

/**
* Internal only for testing.
*/
Expand All @@ -95,11 +120,46 @@ abstract class BaseUploadTask : DefaultTask() {
outputDir.mkdirs()

val zipFile = File(createTempDirectory("").absolutePathString(), OUTPUT_FILE_NAME)
logger.debug("Creating Emerge upload zip: ${zipFile.path}")

ZipOutputStream(BufferedOutputStream(zipFile.outputStream())).use { zos ->
includeFilesInUpload(zos)

val json = Json.encodeToString(artifactMetadata)
var finalArtifactMetadata = artifactMetadata

if (includeDependencyInformation.get()) {
checkNotNull(appModuleName.orNull) {
"appModuleName must be set when includeDependencyInformation is true"
}
checkNotNull(appModulePath.orNull) {
"appModulePath must be set when includeDependencyInformation is true"
}

val dependencies = buildDependencies(
artifacts = artifacts,
defaultModuleName = appModuleName.get(),
defaultModulePath = appModulePath.get(),
logger = logger,
)
val dependenciesJson = Json.encodeToString(dependencies)
val dependenciesFile = File(outputDir, Dependencies.JSON_FILE_NAME).also {
it.createNewFile()
it.writeText(dependenciesJson)
}

if (dependenciesFile.exists()) {
dependenciesFile.inputStream().use { inputStream ->
zos.putNextEntry(ZipEntry(dependenciesFile.name))
inputStream.copyTo(zos)
zos.closeEntry()
}
finalArtifactMetadata = artifactMetadata.copy(
dependencyMetadataZipPath = dependenciesFile.name,
)
}
}

val json = Json.encodeToString(finalArtifactMetadata)
val appMetadataFile = File(outputDir, ArtifactMetadata.JSON_FILE_NAME).also {
it.createNewFile()
it.writeText(json)
Expand Down Expand Up @@ -131,11 +191,7 @@ abstract class BaseUploadTask : DefaultTask() {
val repoName = if (
gitHubRepoOwner.orNull.isNullOrEmpty() ||
gitHubRepoName.orNull.isNullOrBlank()
) {
null
} else {
"${gitHubRepoOwner.get()}/${gitHubRepoName.get()}"
}
) null else "${gitHubRepoOwner.get()}/${gitHubRepoName.get()}"

return EmergeUploadRequestData(
apiToken = apiToken.get(),
Expand All @@ -159,34 +215,34 @@ abstract class BaseUploadTask : DefaultTask() {
if (dryRun.get()) {
file.copyTo(File(outputDir, "/${file.name}"), overwrite = true)
return null
} else {
val okHttpClient = OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.build()

val baseUrl = baseUrl.getOrElse(BuildConfig.EMERGE_BASE_URL)
val baseHttpUrl = baseUrl.toHttpUrlOrNull()
?: error("Invalid baseUrl provided: $baseUrl")

val uploadResponse = fetchSignedUrl(
client = okHttpClient,
uploadData = uploadRequestData(file),
baseUrl = baseHttpUrl
)
val signedUrl = uploadResponse.uploadUrl.toHttpUrlOrNull()
?: error("Error parsing uploadUrl: ${uploadResponse.uploadUrl}")

logger.debug("Uploading file to Emerge: ${file.path}")

postFile(
client = okHttpClient,
file = file,
signedUrl = signedUrl
)
return uploadResponse
}

val okHttpClient = OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.build()

val baseUrl = baseUrl.getOrElse(BuildConfig.EMERGE_BASE_URL)
val baseHttpUrl = baseUrl.toHttpUrlOrNull()
?: error("Invalid baseUrl provided: $baseUrl")

val uploadResponse = fetchSignedUrl(
client = okHttpClient,
uploadData = uploadRequestData(file),
baseUrl = baseHttpUrl
)
val signedUrl = uploadResponse.uploadUrl.toHttpUrlOrNull()
?: error("Error parsing uploadUrl: ${uploadResponse.uploadUrl}")

logger.debug("Uploading file to Emerge: ${file.path}")

postFile(
client = okHttpClient,
file = file,
signedUrl = signedUrl
)
return uploadResponse
}

companion object {
Expand All @@ -200,11 +256,13 @@ abstract class BaseUploadTask : DefaultTask() {
fun BaseUploadTask.setUploadTaskInputs(
extension: EmergePluginExtension,
project: Project,
variant: Variant,
) {
val emergeOutputDir = File(project.buildDir, ARTIFACT_OUTPUT_DIR).also(File::mkdirs)
dryRun.set(extension.dryRun)
apiToken.set(extension.apiToken)
agpVersion.set(AgpVersions.CURRENT.toString())
outputDir.set(File(project.buildDir, ARTIFACT_OUTPUT_DIR).also(File::mkdirs))
outputDir.set(emergeOutputDir)

sha.set(extension.vcsOptions.sha)
baseSha.set(extension.vcsOptions.baseSha)
Expand All @@ -217,6 +275,32 @@ abstract class BaseUploadTask : DefaultTask() {
if (project.hasProperty(BASE_URL_ARG_KEY)) {
baseUrl.set(project.property(BASE_URL_ARG_KEY) as String)
}

includeDependencyInformation.set(extension.includeDependencyInformation.get())
if (extension.includeDependencyInformation.get()) {
appModuleName.set(project.name)
appModulePath.set(project.path)

val runtimeClasspathConfiguration =
project.configurations.getByName("${variant.name}RuntimeClasspath")

val artifacts = listOf(
ARTIFACT_RESOURCES,
ARTIFACT_CLASSES,
ARTIFACT_ASSETS,
ARTIFACT_JNI,
).map { artifactType ->
runtimeClasspathConfiguration.incoming.artifactView { viewConfiguration ->
viewConfiguration.attributes { attributeContainer ->
attributeContainer.attribute(
Attribute.of("artifactType", String::class.java),
artifactType
)
}
}.artifacts
}
setArtifacts(artifacts)
}
}

fun BaseUploadTask.setTagFromProductOptions(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.emergetools.android.gradle.util

fun <K, V> MutableMap<K, List<V>>.putOrAppend(
key: K,
values: List<V>,
appendOnlyDistinct: Boolean = true,
) = get(key)?.let { value ->
val newList = value + values
put(key, if (appendOnlyDistinct) newList.distinct() else newList)
} ?: put(key, values)
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.emergetools.android.gradle.util.dependencies

import kotlinx.serialization.Serializable

@Serializable
data class Dependencies(
val modules: List<Module>,
val libraries: List<Library>,
) {

companion object {
const val JSON_FILE_NAME = "dependencies.json"
}
}

@Serializable
data class Module(
val name: String,
val path: String,
val isRoot: Boolean = false,
val entries: List<String>,
)

@Serializable
data class Library(
val groupId: String,
val artifactId: String,
val version: String,
val entries: List<String>,
)
Loading

0 comments on commit bec895c

Please sign in to comment.