Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add auto-installation for sentry-android SDK and integrations (#…
…282)
- Loading branch information
Showing
24 changed files
with
879 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
48 changes: 48 additions & 0 deletions
48
plugin-build/src/main/kotlin/io/sentry/android/gradle/autoinstall/AbstractInstallStrategy.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package io.sentry.android.gradle.autoinstall | ||
|
||
import io.sentry.android.gradle.util.SemVer | ||
import io.sentry.android.gradle.util.info | ||
import io.sentry.android.gradle.util.warn | ||
import org.gradle.api.artifacts.ComponentMetadataContext | ||
import org.gradle.api.artifacts.ComponentMetadataRule | ||
import org.slf4j.Logger | ||
|
||
abstract class AbstractInstallStrategy : ComponentMetadataRule { | ||
|
||
protected lateinit var logger: Logger | ||
|
||
protected abstract val moduleId: String | ||
|
||
protected abstract val shouldInstallModule: Boolean | ||
|
||
protected open val minSupportedVersion: SemVer = SemVer(0, 0, 0) | ||
|
||
override fun execute(context: ComponentMetadataContext) { | ||
val autoInstallState = AutoInstallState.getInstance() | ||
if (!shouldInstallModule) { | ||
logger.info { | ||
"$moduleId won't be installed because it was already installed directly" | ||
} | ||
return | ||
} | ||
val semVer = SemVer.parse(context.details.id.version) | ||
if (semVer < minSupportedVersion) { | ||
logger.warn { | ||
"$moduleId won't be installed because the current version is " + | ||
"lower than the minimum supported version ($minSupportedVersion)" | ||
} | ||
return | ||
} | ||
|
||
context.details.allVariants { metadata -> | ||
metadata.withDependencies { dependencies -> | ||
val sentryVersion = autoInstallState.sentryVersion | ||
dependencies.add("$SENTRY_GROUP:$moduleId:$sentryVersion") | ||
|
||
logger.info { | ||
"$moduleId was successfully installed with version: $sentryVersion" | ||
} | ||
} | ||
} | ||
} | ||
} |
75 changes: 75 additions & 0 deletions
75
plugin-build/src/main/kotlin/io/sentry/android/gradle/autoinstall/AutoInstall.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
package io.sentry.android.gradle.autoinstall | ||
|
||
import io.sentry.android.gradle.autoinstall.fragment.FragmentInstallStrategy | ||
import io.sentry.android.gradle.autoinstall.fragment.FragmentInstallStrategy.Registrar.SENTRY_FRAGMENT_ID | ||
import io.sentry.android.gradle.autoinstall.okhttp.OkHttpInstallStrategy | ||
import io.sentry.android.gradle.autoinstall.okhttp.OkHttpInstallStrategy.Registrar.SENTRY_OKHTTP_ID | ||
import io.sentry.android.gradle.autoinstall.timber.TimberInstallStrategy | ||
import io.sentry.android.gradle.autoinstall.timber.TimberInstallStrategy.Registrar.SENTRY_TIMBER_ID | ||
import io.sentry.android.gradle.extensions.SentryPluginExtension | ||
import io.sentry.android.gradle.util.info | ||
import org.gradle.api.Project | ||
import org.gradle.api.artifacts.DependencySet | ||
|
||
internal const val SENTRY_GROUP = "io.sentry" | ||
private const val SENTRY_ANDROID_ID = "sentry-android" | ||
private const val SENTRY_ANDROID_CORE_ID = "sentry-android-core" | ||
|
||
private val strategies = listOf( | ||
OkHttpInstallStrategy.Registrar, | ||
TimberInstallStrategy.Registrar, | ||
FragmentInstallStrategy.Registrar | ||
) | ||
|
||
fun Project.installDependencies(extension: SentryPluginExtension) { | ||
configurations.named("implementation").configure { configuration -> | ||
configuration.withDependencies { dependencies -> | ||
// if autoInstallation is disabled, the autoInstallState will contain initial values | ||
// which all default to false, hence, the integrations won't be installed as well | ||
if (extension.autoInstallation.enabled.get()) { | ||
val sentryVersion = dependencies.findSentryAndroidVersion() | ||
with(AutoInstallState.getInstance(gradle)) { | ||
this.sentryVersion = installSentrySdk(sentryVersion, dependencies, extension) | ||
|
||
installOkHttp = !dependencies.isModuleAvailable(SENTRY_OKHTTP_ID) | ||
installTimber = !dependencies.isModuleAvailable(SENTRY_TIMBER_ID) | ||
installFragment = !dependencies.isModuleAvailable(SENTRY_FRAGMENT_ID) | ||
} | ||
} | ||
} | ||
} | ||
project.dependencies.components { component -> | ||
strategies.forEach { it.register(component) } | ||
} | ||
} | ||
|
||
private fun Project.installSentrySdk( | ||
sentryVersion: String?, | ||
dependencies: DependencySet, | ||
extension: SentryPluginExtension | ||
): String { | ||
return if (sentryVersion == null) { | ||
val userDefinedVersion = extension.autoInstallation.sentryVersion.get() | ||
val sentryAndroidDep = | ||
this.dependencies.create("$SENTRY_GROUP:$SENTRY_ANDROID_ID:$userDefinedVersion") | ||
dependencies.add(sentryAndroidDep) | ||
logger.info { | ||
"sentry-android was successfully installed with version: $userDefinedVersion" | ||
} | ||
userDefinedVersion | ||
} else { | ||
logger.info { | ||
"sentry-android won't be installed because it was already installed directly" | ||
} | ||
sentryVersion | ||
} | ||
} | ||
|
||
private fun DependencySet.findSentryAndroidVersion(): String? = | ||
find { | ||
it.group == SENTRY_GROUP && | ||
(it.name == SENTRY_ANDROID_ID || it.name == SENTRY_ANDROID_CORE_ID) | ||
}?.version | ||
|
||
private fun DependencySet.isModuleAvailable(id: String): Boolean = | ||
any { it.group == SENTRY_GROUP && it.name == id } |
77 changes: 77 additions & 0 deletions
77
plugin-build/src/main/kotlin/io/sentry/android/gradle/autoinstall/AutoInstallState.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
package io.sentry.android.gradle.autoinstall | ||
|
||
import io.sentry.android.gradle.SentryPlugin.Companion.SENTRY_SDK_VERSION | ||
import java.io.Serializable | ||
import org.gradle.api.invocation.Gradle | ||
|
||
class AutoInstallState private constructor() : Serializable { | ||
|
||
@get:Synchronized | ||
@set:Synchronized | ||
var sentryVersion: String = SENTRY_SDK_VERSION | ||
|
||
@get:Synchronized | ||
@set:Synchronized | ||
var installOkHttp: Boolean = false | ||
|
||
@get:Synchronized | ||
@set:Synchronized | ||
var installFragment: Boolean = false | ||
|
||
@get:Synchronized | ||
@set:Synchronized | ||
var installTimber: Boolean = false | ||
|
||
override fun toString(): String { | ||
return "AutoInstallState(sentryVersion='$sentryVersion', " + | ||
"installOkHttp=$installOkHttp, " + | ||
"installFragment=$installFragment, " + | ||
"installTimber=$installTimber)" | ||
} | ||
|
||
override fun equals(other: Any?): Boolean { | ||
if (this === other) return true | ||
if (javaClass != other?.javaClass) return false | ||
|
||
other as AutoInstallState | ||
|
||
if (sentryVersion != other.sentryVersion) return false | ||
if (installOkHttp != other.installOkHttp) return false | ||
if (installFragment != other.installFragment) return false | ||
if (installTimber != other.installTimber) return false | ||
|
||
return true | ||
} | ||
|
||
override fun hashCode(): Int { | ||
var result = sentryVersion.hashCode() | ||
result = 31 * result + installOkHttp.hashCode() | ||
result = 31 * result + installFragment.hashCode() | ||
result = 31 * result + installTimber.hashCode() | ||
return result | ||
} | ||
|
||
// We can't use Kotlin object because we need new instance on each Gradle rebuild | ||
// But if we're inside Gradle daemon, Kotlin object will be shared between builds | ||
companion object { | ||
@field:Volatile | ||
private var instance: AutoInstallState? = null | ||
|
||
@JvmStatic | ||
@Synchronized | ||
fun getInstance(gradle: Gradle? = null): AutoInstallState { | ||
if (instance != null) { | ||
return instance!! | ||
} | ||
|
||
val state = AutoInstallState() | ||
instance = state | ||
|
||
if (gradle != null) { | ||
BuildFinishedListenerService.getInstance(gradle).onClose { instance = null } | ||
} | ||
|
||
return state | ||
} | ||
} | ||
} |
35 changes: 35 additions & 0 deletions
35
...uild/src/main/kotlin/io/sentry/android/gradle/autoinstall/BuildFinishedListenerService.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
@file:Suppress("UnstableApiUsage") | ||
|
||
package io.sentry.android.gradle.autoinstall | ||
|
||
import io.sentry.android.gradle.util.getBuildServiceName | ||
import org.gradle.api.invocation.Gradle | ||
import org.gradle.api.services.BuildService | ||
import org.gradle.api.services.BuildServiceParameters | ||
|
||
abstract class BuildFinishedListenerService : | ||
BuildService<BuildServiceParameters.None>, | ||
AutoCloseable { | ||
|
||
private val actionsOnClose = mutableListOf<() -> Unit>() | ||
|
||
fun onClose(action: () -> Unit) { | ||
actionsOnClose.add(action) | ||
} | ||
|
||
override fun close() { | ||
for (action in actionsOnClose) { | ||
action() | ||
} | ||
actionsOnClose.clear() | ||
} | ||
|
||
companion object { | ||
fun getInstance(gradle: Gradle): BuildFinishedListenerService { | ||
return gradle.sharedServices.registerIfAbsent( | ||
getBuildServiceName(BuildFinishedListenerService::class.java), | ||
BuildFinishedListenerService::class.java | ||
) {}.get() | ||
} | ||
} | ||
} |
7 changes: 7 additions & 0 deletions
7
...in-build/src/main/kotlin/io/sentry/android/gradle/autoinstall/InstallStrategyRegistrar.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package io.sentry.android.gradle.autoinstall | ||
|
||
import org.gradle.api.artifacts.dsl.ComponentMetadataHandler | ||
|
||
interface InstallStrategyRegistrar { | ||
fun register(component: ComponentMetadataHandler) | ||
} |
38 changes: 38 additions & 0 deletions
38
.../src/main/kotlin/io/sentry/android/gradle/autoinstall/fragment/FragmentInstallStrategy.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package io.sentry.android.gradle.autoinstall.fragment | ||
|
||
import io.sentry.android.gradle.SentryPlugin | ||
import io.sentry.android.gradle.autoinstall.AbstractInstallStrategy | ||
import io.sentry.android.gradle.autoinstall.AutoInstallState | ||
import io.sentry.android.gradle.autoinstall.InstallStrategyRegistrar | ||
import javax.inject.Inject | ||
import org.gradle.api.artifacts.dsl.ComponentMetadataHandler | ||
import org.slf4j.Logger | ||
|
||
// @CacheableRule // TODO: make it cacheable somehow (probably depends on parameters) | ||
abstract class FragmentInstallStrategy : AbstractInstallStrategy { | ||
|
||
constructor(logger: Logger) : super() { | ||
this.logger = logger | ||
} | ||
|
||
@Suppress("unused") // used by Gradle | ||
@Inject // inject is needed to avoid Gradle error | ||
constructor() : this(SentryPlugin.logger) | ||
|
||
override val moduleId: String get() = SENTRY_FRAGMENT_ID | ||
|
||
override val shouldInstallModule: Boolean get() = AutoInstallState.getInstance().installFragment | ||
|
||
companion object Registrar : InstallStrategyRegistrar { | ||
private const val FRAGMENT_GROUP = "androidx.fragment" | ||
private const val FRAGMENT_ID = "fragment" | ||
internal const val SENTRY_FRAGMENT_ID = "sentry-android-fragment" | ||
|
||
override fun register(component: ComponentMetadataHandler) { | ||
component.withModule( | ||
"$FRAGMENT_GROUP:$FRAGMENT_ID", | ||
FragmentInstallStrategy::class.java | ||
) {} | ||
} | ||
} | ||
} |
Oops, something went wrong.