Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions plugin-build/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,10 @@ gradlePlugin {
implementationClass =
"io.sentry.android.gradle.snapshot.metadata.SentrySnapshotMetadataPlugin"
}
register("sentrySettingsPlugin") {
id = "io.sentry.android.gradle.settings"
implementationClass = "io.sentry.android.gradle.SentrySettingsPlugin"
}
}
}

Expand Down Expand Up @@ -203,6 +207,9 @@ distributions {
create("sentrySnapshotMetadataPluginMarker") {
contents { from("build${sep}publications${sep}sentrySnapshotMetadataPluginPluginMarkerMaven") }
}
create("sentrySettingsPluginMarker") {
contents { from("build${sep}publications${sep}sentrySettingsPluginPluginMarkerMaven") }
}
}

tasks.named("distZip") {
Expand Down Expand Up @@ -257,6 +264,14 @@ tasks.named("sentrySnapshotMetadataPluginMarkerDistZip").configure {
dependsOn("generatePomFileForSentrySnapshotMetadataPluginPluginMarkerMavenPublication")
}

tasks.named("sentrySettingsPluginMarkerDistTar").configure {
dependsOn("generatePomFileForSentrySettingsPluginPluginMarkerMavenPublication")
}

tasks.named("sentrySettingsPluginMarkerDistZip").configure {
dependsOn("generatePomFileForSentrySettingsPluginPluginMarkerMavenPublication")
}

tasks.withType<Test>().configureEach {
testLogging {
events = setOf(TestLogEvent.SKIPPED, TestLogEvent.PASSED, TestLogEvent.FAILED)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import io.sentry.android.gradle.services.SentryModulesService
import io.sentry.android.gradle.snapshot.GenerateSnapshotTestsTask
import io.sentry.android.gradle.sourcecontext.OutputPaths
import io.sentry.android.gradle.sourcecontext.SourceContext
import io.sentry.android.gradle.sourcecontext.resolveDependencySources
import io.sentry.android.gradle.tasks.GenerateDistributionPropertiesTask
import io.sentry.android.gradle.tasks.InjectSentryMetaPropertiesIntoAssetsTask
import io.sentry.android.gradle.tasks.PropertiesFileOutputTask
Expand All @@ -44,6 +45,7 @@ import io.sentry.android.gradle.util.SentryPluginUtils.isMinificationEnabled
import io.sentry.android.gradle.util.SentryPluginUtils.isVariantAllowed
import io.sentry.android.gradle.util.collectModules
import io.sentry.android.gradle.util.hookWithAssembleTasks
import io.sentry.gradle.common.filterBuildConfig
import java.io.File
import org.gradle.api.Project
import org.gradle.api.file.Directory
Expand Down Expand Up @@ -103,7 +105,19 @@ fun ApplicationAndroidComponentsExtension.configure(
project.layout.projectDirectory.dir(it)
}
}
val sourceFiles = sentryVariant.sources(project, additionalSourcesProvider)
val moduleSourceFiles = sentryVariant.sources(project, additionalSourcesProvider)

val sourceFiles =
if (extension.includeSourceContext.get()) {
val dependencySources = resolveDependencySources(project, variant.name)
moduleSourceFiles?.map { currentSources ->
val depDirs =
dependencySources.files.map { project.layout.projectDirectory.dir(it.absolutePath) }
(currentSources + depDirs).filterBuildConfig().toSet()
}
} else {
moduleSourceFiles
}

val tasksGeneratingProperties = mutableListOf<TaskProvider<out PropertiesFileOutputTask>>()
val sourceContextTasks =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.android.build.api.variant.ApplicationAndroidComponentsExtension
import io.sentry.BuildConfig
import io.sentry.android.gradle.autoinstall.installDependencies
import io.sentry.android.gradle.extensions.SentryPluginExtension
import io.sentry.android.gradle.sourcecontext.registerSentrySourceElements
import io.sentry.android.gradle.util.AgpVersions
import java.io.File
import javax.inject.Inject
Expand All @@ -30,11 +31,14 @@ constructor(private val buildEvents: BuildEventListenerRegistryInternal) : Plugi
.trimIndent()
)
}
if (!project.plugins.hasPlugin("com.android.application")) {
if (
!project.plugins.hasPlugin("com.android.application") &&
!project.plugins.hasPlugin("com.android.library")
) {
project.logger.warn(
"""
WARNING: Using 'io.sentry.android.gradle' is only supported for the app module.
Please make sure that you apply the Sentry gradle plugin alongside 'com.android.application' on the _module_ level, and not on the root project level.
WARNING: Using 'io.sentry.android.gradle' is only supported for app and library modules.
Please make sure that you apply the Sentry gradle plugin alongside 'com.android.application' or 'com.android.library' on the _module_ level, and not on the root project level.
https://docs.sentry.io/platforms/android/configuration/gradle/
"""
.trimIndent()
Expand Down Expand Up @@ -67,6 +71,10 @@ constructor(private val buildEvents: BuildEventListenerRegistryInternal) : Plugi

project.installDependencies(extension, true)
}

project.pluginManager.withPlugin("com.android.library") {
registerSentrySourceElements(project)
}
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.sentry.android.gradle

import org.gradle.api.Plugin
import org.gradle.api.initialization.Settings

class SentrySettingsPlugin : Plugin<Settings> {

override fun apply(settings: Settings) {
settings.gradle.beforeProject { project ->
project.pluginManager.withPlugin("com.android.library") {
project.pluginManager.apply("io.sentry.android.gradle")
}
project.pluginManager.withPlugin("java-library") {
project.pluginManager.apply("io.sentry.android.gradle")
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package io.sentry.android.gradle.sourcecontext

import io.sentry.android.gradle.util.SentryPluginUtils.capitalizeUS
import org.gradle.api.Project
import org.gradle.api.attributes.Attribute
import org.gradle.api.file.FileCollection

val SENTRY_ARTIFACT_ATTR: Attribute<String> = Attribute.of("io.sentry.artifact", String::class.java)

const val SENTRY_SOURCES_VALUE = "sentry-sources"

fun registerSentrySourceElements(project: Project) {
val config =
project.configurations.create("sentrySourceElements") {
it.isCanBeConsumed = true
it.isCanBeResolved = false
it.attributes { attrs -> attrs.attribute(SENTRY_ARTIFACT_ATTR, SENTRY_SOURCES_VALUE) }
}
listOf("src/main/java", "src/main/kotlin").forEach { path ->
val dir = project.file(path)
if (dir.isDirectory) {
config.outgoing.artifact(dir)
}
}
}

fun resolveDependencySources(project: Project, variantName: String): FileCollection {
val sentrySourcesPath =
project.configurations.create("sentrySourcesFor${variantName.capitalizeUS()}") {
it.isCanBeConsumed = false
it.isCanBeResolved = true
it.attributes { attrs -> attrs.attribute(SENTRY_ARTIFACT_ATTR, SENTRY_SOURCES_VALUE) }
}

val runtimeClasspath = project.configurations.getByName("${variantName}RuntimeClasspath")
runtimeClasspath.extendsFrom
.filter { !it.isCanBeResolved && !it.isCanBeConsumed }
.forEach { sentrySourcesPath.extendsFrom(it) }

return sentrySourcesPath.incoming.artifactView { view -> view.lenient(true) }.files
}
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,116 @@ class SentryPluginSourceContextTest :
assertTrue(subsequentBuild.output) { "BUILD SUCCESSFUL" in subsequentBuild.output }
}

@Test
fun `bundles source context from library module dependencies`() {
// Set up settings.gradle to include the :library module
File(testProjectDir.root, "settings.gradle")
.writeText(
// language=Groovy
"""
include ':app', ':module', ':library'
"""
.trimIndent()
)

// Create the library module with the sentry plugin to publish source elements
val libraryDir = File(testProjectDir.root, "library").apply { mkdirs() }
File(libraryDir, "build.gradle")
.writeText(
// language=Groovy
"""
plugins {
id 'com.android.library'
id 'io.sentry.android.gradle'
}

android {
namespace 'com.example.library'
compileSdkVersion 35
defaultConfig {
minSdkVersion 21
}
}

sentry {
autoInstallation.enabled = false
telemetry = false
}
"""
.trimIndent()
)

val libSrcDir = File(libraryDir, "src/main/java/com/example/library")
libSrcDir.mkdirs()
val libContents =
// language=java
"""
package com.example.library;

public class LibHelper {
public static int add(int a, int b) { return a + b; }
}
"""
.trimIndent()
File(libSrcDir, "LibHelper.java").writeText(libContents)

appBuildFile.writeText(
// language=Groovy
"""
plugins {
id "com.android.application"
id "io.sentry.android.gradle"
}

android {
namespace 'com.example'

buildFeatures {
buildConfig false
}

buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}

dependencies {
implementation project(':library')
}

sentry {
debug = true
includeSourceContext = true
autoUploadSourceContext = false
autoUploadProguardMapping = false
org = "sentry-sdks"
projectName = "sentry-android"
}
"""
.trimIndent()
)

sentryPropertiesFile.writeText("")
val ktContents = testProjectDir.withDummyKtFile()

val result = runner.appendArguments("app:assembleRelease").build()

assertTrue(result.output) { "BUILD SUCCESSFUL" in result.output }

// App module sources should be bundled
verifySourceBundleContents(testProjectDir.root, "files/_/_/com/example/Example.jvm", ktContents)

// Library module sources should also be bundled
verifySourceBundleContents(
testProjectDir.root,
"files/_/_/com/example/library/LibHelper.jvm",
libContents,
)
}

@Test
fun `uploadSourceBundle task is not up-to-date on subsequent builds if cli path changes`() {
val sentryCli = SentryCliProvider.getSentryCliPath(File(""), File("build"), File(""))
Expand Down
Loading