diff --git a/CHANGELOG.md b/CHANGELOG.md index ab45d4eb..0a80f7b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ - Fall back to `findTask` if `assembleProvider` of AndroidVariant is null when hooking source bundle and native symbols upload tasks ([#639](https://github.com/getsentry/sentry-android-gradle-plugin/pull/639)) - Hook source context tasks to also run after `install{Variant}` tasks ([#643](https://github.com/getsentry/sentry-android-gradle-plugin/pull/643)) - Do not run sentry-cli commands if telemetry is disabled ([#648](https://github.com/getsentry/sentry-android-gradle-plugin/pull/648)) +- Proguard and source context tasks don't run on every build ([#634](https://github.com/getsentry/sentry-android-gradle-plugin/pull/634)) + - Proguard UUID task now depends on the proguard mapping file. I.e. it will only run if the mapping file has changed + - Source context tasks now depend on source file changes, if there are no source changes, the tasks won't run ### Dependencies diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/AndroidComponentsConfig.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/AndroidComponentsConfig.kt index c95daad7..a839ce7e 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/AndroidComponentsConfig.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/AndroidComponentsConfig.kt @@ -42,6 +42,7 @@ import io.sentry.android.gradle.util.hookWithMinifyTasks import io.sentry.android.gradle.util.info import java.io.File import org.gradle.api.Project +import org.gradle.api.file.Directory import org.gradle.api.provider.Provider import org.gradle.api.provider.SetProperty import org.gradle.api.tasks.TaskProvider @@ -73,6 +74,18 @@ fun AndroidComponentsExtension<*, *, *>.configure( variant.configureDependenciesTask(project, extension, sentryTelemetryProvider) + // TODO: do this only once, and all other tasks should be SentryVariant.configureSomething + val sentryVariant = if (isAGP74) AndroidVariant74(variant) else null + + val additionalSourcesProvider = project.provider { + extension.additionalSourceDirsForSourceContext.getOrElse(emptySet()) + .map { project.layout.projectDirectory.dir(it) } + } + val sourceFiles = sentryVariant?.sources( + project, + additionalSourcesProvider + ) + val tasksGeneratingProperties = mutableListOf>() val sourceContextTasks = variant.configureSourceBundleTasks( @@ -80,6 +93,7 @@ fun AndroidComponentsExtension<*, *, *>.configure( extension, sentryTelemetryProvider, paths, + sourceFiles, cliExecutable, sentryOrg, sentryProject @@ -97,8 +111,6 @@ fun AndroidComponentsExtension<*, *, *>.configure( ) generateProguardUuidTask?.let { tasksGeneratingProperties.add(it) } - // TODO: do this only once, and all other tasks should be SentryVariant.configureSomething - val sentryVariant = if (isAGP74) AndroidVariant74(variant) else null sentryVariant?.configureNativeSymbolsTask( project, extension, @@ -265,6 +277,7 @@ private fun Variant.configureSourceBundleTasks( extension: SentryPluginExtension, sentryTelemetryProvider: Provider, paths: OutputPaths, + sourceFiles: Provider>?, cliExecutable: Provider, sentryOrg: String?, sentryProject: String? @@ -280,6 +293,7 @@ private fun Variant.configureSourceBundleTasks( sentryTelemetryProvider, variant, paths, + sourceFiles, cliExecutable, sentryOrg, sentryProject, @@ -354,6 +368,7 @@ private fun Variant.configureProguardMappingsTasks( project = project, extension, sentryTelemetryProvider, + proguardMappingFile = getMappingFileProvider(project, variant, dexguardEnabled), taskSuffix = name.capitalized, output = paths.proguardUuidDir ) diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/AppConfig.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/AppConfig.kt index 0e7956db..f14fc98e 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/AppConfig.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/AppConfig.kt @@ -31,6 +31,7 @@ import io.sentry.android.gradle.util.hookWithPackageTasks import io.sentry.android.gradle.util.info import org.gradle.api.Project import org.gradle.api.Task +import org.gradle.api.file.Directory import org.gradle.api.provider.Provider import org.gradle.api.tasks.TaskProvider import org.gradle.internal.build.event.BuildEventListenerRegistryInternal @@ -68,11 +69,32 @@ fun AppExtension.configure( buildEvents ) + // TODO: do this only once, and all other tasks should be SentryVariant.configureSomething + val sentryVariant = if (isAGP74) null else AndroidVariant70(variant) + sentryVariant?.configureNativeSymbolsTask( + project, + extension, + sentryTelemetryProvider, + cliExecutable, + sentryOrg, + sentryProject + ) + + val additionalSourcesProvider = project.provider { + extension.additionalSourceDirsForSourceContext.getOrElse(emptySet()) + .map { project.layout.projectDirectory.dir(it) } + } + val sourceFiles = sentryVariant?.sources( + project, + additionalSourcesProvider + ) + val tasksGeneratingProperties = mutableListOf>() val sourceContextTasks = variant.configureSourceBundleTasks( project, extension, sentryTelemetryProvider, + sourceFiles, cliExecutable, sentryOrg, sentryProject @@ -97,17 +119,6 @@ fun AppExtension.configure( ) generateProguardUuidTask?.let { tasksGeneratingProperties.add(it) } - // TODO: do this only once, and all other tasks should be SentryVariant.configureSomething - val sentryVariant = if (isAGP74) null else AndroidVariant70(variant) - sentryVariant?.configureNativeSymbolsTask( - project, - extension, - sentryTelemetryProvider, - cliExecutable, - sentryOrg, - sentryProject - ) - variant.configureDebugMetaPropertiesTask( project, extension, @@ -187,6 +198,7 @@ private fun ApplicationVariant.configureSourceBundleTasks( project: Project, extension: SentryPluginExtension, sentryTelemetryProvider: Provider, + sourceFiles: Provider>?, cliExecutable: Provider, sentryOrg: String?, sentryProject: String? @@ -208,6 +220,7 @@ private fun ApplicationVariant.configureSourceBundleTasks( sentryTelemetryProvider, variant, paths, + sourceFiles, cliExecutable, sentryOrg, sentryProject, @@ -289,6 +302,11 @@ private fun ApplicationVariant.configureProguardMappingsTasks( extension, sentryTelemetryProvider, output = outputDir, + proguardMappingFile = SentryTasksProvider.getMappingFileProvider( + project, + variant, + dexguardEnabled + ), taskSuffix = name.capitalized ) diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/sourcecontext/CollectSourcesTask.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/sourcecontext/CollectSourcesTask.kt index ff324736..04e49e79 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/sourcecontext/CollectSourcesTask.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/sourcecontext/CollectSourcesTask.kt @@ -55,7 +55,7 @@ abstract class CollectSourcesTask : DirectoryOutputTask() { project: Project, extension: SentryPluginExtension, sentryTelemetryProvider: Provider?, - sourceDirs: Provider>, + sourceDirs: Provider>?, output: Provider, includeSourceContext: Property, taskSuffix: String = "" diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/sourcecontext/GenerateBundleIdTask.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/sourcecontext/GenerateBundleIdTask.kt index edf93a9c..d6037f85 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/sourcecontext/GenerateBundleIdTask.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/sourcecontext/GenerateBundleIdTask.kt @@ -8,19 +8,22 @@ import io.sentry.android.gradle.util.info import java.util.Properties import java.util.UUID import org.gradle.api.Project +import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.Directory import org.gradle.api.file.RegularFile import org.gradle.api.provider.Property import org.gradle.api.provider.Provider import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputFiles import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.PathSensitive +import org.gradle.api.tasks.PathSensitivity import org.gradle.api.tasks.TaskAction import org.gradle.api.tasks.TaskProvider abstract class GenerateBundleIdTask : PropertiesFileOutputTask() { init { - outputs.upToDateWhen { false } description = "Generates a unique build ID to be used " + "when bundling sources for upload to Sentry" @@ -33,6 +36,10 @@ abstract class GenerateBundleIdTask : PropertiesFileOutputTask() { @get:Input abstract val includeSourceContext: Property + @get:PathSensitive(PathSensitivity.RELATIVE) + @get:InputFiles + abstract val sourceDirs: ConfigurableFileCollection + @get:Internal override val outputFile: Provider get() = output.file(SENTRY_BUNDLE_ID_OUTPUT) @@ -64,6 +71,7 @@ abstract class GenerateBundleIdTask : PropertiesFileOutputTask() { project: Project, extension: SentryPluginExtension, sentryTelemetryProvider: Provider?, + sourceDirs: Provider>?, output: Provider? = null, includeSourceContext: Property, taskSuffix: String = "" @@ -75,6 +83,7 @@ abstract class GenerateBundleIdTask : PropertiesFileOutputTask() { output?.let { task.output.set(it) } task.includeSourceContext.set(includeSourceContext) task.withSentryTelemetry(extension, sentryTelemetryProvider) + task.sourceDirs.setFrom(sourceDirs) } return generateBundleIdTask } diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/sourcecontext/SourceContext.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/sourcecontext/SourceContext.kt index b032cb68..dc5455c3 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/sourcecontext/SourceContext.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/sourcecontext/SourceContext.kt @@ -4,6 +4,7 @@ import io.sentry.android.gradle.extensions.SentryPluginExtension import io.sentry.android.gradle.telemetry.SentryTelemetryService import io.sentry.gradle.common.SentryVariant import org.gradle.api.Project +import org.gradle.api.file.Directory import org.gradle.api.provider.Provider import org.gradle.api.tasks.TaskProvider @@ -15,23 +16,17 @@ class SourceContext { sentryTelemetryProvider: Provider?, variant: SentryVariant, paths: OutputPaths, + sourceFiles: Provider>?, cliExecutable: Provider, sentryOrg: String?, sentryProject: String?, taskSuffix: String ): SourceContextTasks { - val additionalSourcesProvider = project.provider { - extension.additionalSourceDirsForSourceContext.getOrElse(emptySet()) - .map { project.layout.projectDirectory.dir(it) } - } - val sourceFiles = variant.sources( - project, - additionalSourcesProvider - ) val generateBundleIdTask = GenerateBundleIdTask.register( project, extension, sentryTelemetryProvider, + sourceFiles, output = paths.bundleIdDir, extension.includeSourceContext, taskSuffix diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/sourcecontext/UploadSourceBundleTask.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/sourcecontext/UploadSourceBundleTask.kt index e97b3c5a..1934bad2 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/sourcecontext/UploadSourceBundleTask.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/sourcecontext/UploadSourceBundleTask.kt @@ -33,6 +33,12 @@ abstract class UploadSourceBundleTask : Exec() { includeSourceContext.getOrElse(false) && !sourceBundleDir.asFileTree.isEmpty } + + // Allows gradle to consider this task up-to-date if the inputs haven't changed + // As this task does not have any outputs, it will always be considered to be out-of-date otherwise + // More info here https://docs.gradle.org/current/userguide/more_about_tasks.html#sec:task_outcomes + // and https://docs.gradle.org/current/userguide/incremental_build.html#sec:custom_up_to_date_logic + outputs.upToDateWhen { true } } @get:Input diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/tasks/SentryGenerateDebugMetaPropertiesTask.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/tasks/SentryGenerateDebugMetaPropertiesTask.kt index 0c32bc91..5e32c1c8 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/tasks/SentryGenerateDebugMetaPropertiesTask.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/tasks/SentryGenerateDebugMetaPropertiesTask.kt @@ -18,7 +18,6 @@ import org.gradle.api.tasks.TaskProvider abstract class SentryGenerateDebugMetaPropertiesTask : DirectoryOutputTask() { init { - outputs.upToDateWhen { false } description = "Combines multiple properties files into sentry-debug-meta.properties" } diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/tasks/SentryGenerateProguardUuidTask.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/tasks/SentryGenerateProguardUuidTask.kt index 8297f8bb..c661ed84 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/tasks/SentryGenerateProguardUuidTask.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/tasks/SentryGenerateProguardUuidTask.kt @@ -7,17 +7,21 @@ import io.sentry.android.gradle.util.info import java.util.Properties import java.util.UUID import org.gradle.api.Project +import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.Directory +import org.gradle.api.file.FileCollection import org.gradle.api.file.RegularFile import org.gradle.api.provider.Provider +import org.gradle.api.tasks.InputFiles import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.PathSensitive +import org.gradle.api.tasks.PathSensitivity import org.gradle.api.tasks.TaskAction import org.gradle.api.tasks.TaskProvider abstract class SentryGenerateProguardUuidTask : PropertiesFileOutputTask() { init { - outputs.upToDateWhen { false } description = "Generates a unique build ID to be used " + "when uploading the Sentry mapping file" } @@ -25,6 +29,10 @@ abstract class SentryGenerateProguardUuidTask : PropertiesFileOutputTask() { @get:Internal override val outputFile: Provider get() = output.file(SENTRY_UUID_OUTPUT) + @get:PathSensitive(PathSensitivity.RELATIVE) + @get:InputFiles + abstract val proguardMappingFile: ConfigurableFileCollection + @TaskAction fun generateProperties() { val outputDir = output.get().asFile @@ -54,6 +62,7 @@ abstract class SentryGenerateProguardUuidTask : PropertiesFileOutputTask() { extension: SentryPluginExtension, sentryTelemetryProvider: Provider?, output: Provider? = null, + proguardMappingFile: Provider?, taskSuffix: String = "" ): TaskProvider { val generateUuidTask = project.tasks.register( @@ -62,6 +71,7 @@ abstract class SentryGenerateProguardUuidTask : PropertiesFileOutputTask() { ) { task -> output?.let { task.output.set(it) } task.withSentryTelemetry(extension, sentryTelemetryProvider) + task.proguardMappingFile.setFrom(proguardMappingFile) } return generateUuidTask } diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/tasks/SentryUploadProguardMappingsTask.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/tasks/SentryUploadProguardMappingsTask.kt index 9dd17acf..4eeabb55 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/tasks/SentryUploadProguardMappingsTask.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/tasks/SentryUploadProguardMappingsTask.kt @@ -28,6 +28,12 @@ abstract class SentryUploadProguardMappingsTask : Exec() { init { description = "Uploads the proguard mappings file to Sentry" + + // Allows gradle to consider this task up-to-date if the inputs haven't changed + // As this task does not have any outputs, it will always be considered to be out-of-date otherwise + // More info here https://docs.gradle.org/current/userguide/more_about_tasks.html#sec:task_outcomes + // and https://docs.gradle.org/current/userguide/incremental_build.html#sec:custom_up_to_date_logic + outputs.upToDateWhen { true } } @get:Input diff --git a/plugin-build/src/main/kotlin/io/sentry/jvm/gradle/SentryJvmPlugin.kt b/plugin-build/src/main/kotlin/io/sentry/jvm/gradle/SentryJvmPlugin.kt index 87eefdcb..d9333de9 100644 --- a/plugin-build/src/main/kotlin/io/sentry/jvm/gradle/SentryJvmPlugin.kt +++ b/plugin-build/src/main/kotlin/io/sentry/jvm/gradle/SentryJvmPlugin.kt @@ -80,12 +80,22 @@ class SentryJvmPlugin @Inject constructor( buildEvents.onOperationCompletion(sentryTelemetryProvider) } + val additionalSourcesProvider = project.provider { + extension.additionalSourceDirsForSourceContext.getOrElse(emptySet()) + .map { project.layout.projectDirectory.dir(it) } + } + val sourceFiles = javaVariant.sources( + project, + additionalSourcesProvider + ) + val sourceContextTasks = SourceContext.register( project, extension, sentryTelemetryProvider, javaVariant, outputPaths, + sourceFiles, cliExecutable, sentryOrgParameter, sentryProjectParameter, diff --git a/plugin-build/src/test/kotlin/io/sentry/android/gradle/integration/SentryPluginSourceContextTest.kt b/plugin-build/src/test/kotlin/io/sentry/android/gradle/integration/SentryPluginSourceContextTest.kt index 1fb478c3..bdfdacf0 100644 --- a/plugin-build/src/test/kotlin/io/sentry/android/gradle/integration/SentryPluginSourceContextTest.kt +++ b/plugin-build/src/test/kotlin/io/sentry/android/gradle/integration/SentryPluginSourceContextTest.kt @@ -1,14 +1,20 @@ package io.sentry.android.gradle.integration import io.sentry.BuildConfig +import io.sentry.android.gradle.SentryCliProvider import io.sentry.android.gradle.util.GradleVersions import io.sentry.android.gradle.verifySourceBundleContents import io.sentry.android.gradle.withDummyComposeFile import io.sentry.android.gradle.withDummyCustomFile import io.sentry.android.gradle.withDummyJavaFile +import java.io.File +import java.nio.file.Files +import java.nio.file.Path import kotlin.test.assertEquals import kotlin.test.assertTrue import org.gradle.testkit.runner.TaskOutcome.SKIPPED +import org.gradle.testkit.runner.TaskOutcome.SUCCESS +import org.gradle.testkit.runner.TaskOutcome.UP_TO_DATE import org.gradle.util.GradleVersion import org.hamcrest.CoreMatchers.`is` import org.junit.Assume.assumeThat @@ -55,6 +61,120 @@ class SentryPluginSourceContextTest : assertTrue(result.output) { "BUILD SUCCESSFUL" in result.output } } + @Test + fun `generateBundleId and collectSources are up-to-date on subsequent builds`() { + runner.appendArguments("app:assembleRelease") + appBuildFile.writeText( + // language=Groovy + """ + plugins { + id "com.android.application" + id "io.sentry.android.gradle" + } + + android { + namespace 'com.example' + + buildFeatures { + buildConfig false + } + } + + sentry { + includeSourceContext = true + autoUploadSourceContext = false + autoUploadProguardMapping = false + org = "sentry-sdks" + projectName = "sentry-android" + url = "https://some-host.sentry.io" + } + """.trimIndent() + ) + val firstBuild = runner.build() + + val subsequentBuild = runner.build() + + assertEquals( + firstBuild.task(":app:generateSentryBundleIdRelease")?.outcome, + SUCCESS + ) + + assertEquals( + firstBuild.task(":app:sentryCollectSourcesRelease")?.outcome, + SUCCESS + ) + + assertEquals( + subsequentBuild.task(":app:generateSentryBundleIdRelease")?.outcome, + UP_TO_DATE + ) + + assertEquals( + subsequentBuild.task(":app:sentryCollectSourcesRelease")?.outcome, + UP_TO_DATE + ) + + assertTrue(subsequentBuild.output) { "BUILD SUCCESSFUL" in subsequentBuild.output } + } + + @Test + fun `generateBundleId and collectSources are not up-to-date if sources change`() { + runner.appendArguments("app:assembleRelease") + appBuildFile.writeText( + // language=Groovy + """ + plugins { + id "com.android.application" + id "io.sentry.android.gradle" + } + + android { + namespace 'com.example' + + buildFeatures { + buildConfig false + } + } + + sentry { + includeSourceContext = true + autoUploadSourceContext = false + autoUploadProguardMapping = false + org = "sentry-sdks" + projectName = "sentry-android" + url = "https://some-host.sentry.io" + } + """.trimIndent() + ) + val firstBuild = runner.build() + + testProjectDir.withDummyComposeFile() + + val subsequentBuild = runner.build() + + assertEquals( + firstBuild.task(":app:generateSentryBundleIdRelease")?.outcome, + SUCCESS + ) + + assertEquals( + firstBuild.task(":app:sentryCollectSourcesRelease")?.outcome, + SUCCESS + ) + + assertEquals( + subsequentBuild.task(":app:generateSentryBundleIdRelease")?.outcome, + SUCCESS + ) + + assertEquals( + subsequentBuild.task(":app:sentryCollectSourcesRelease")?.outcome, + SUCCESS + ) + + assertTrue(subsequentBuild.output) { "BUILD SUCCESSFUL" in subsequentBuild.output } + } + @Test fun `bundles source context`() { appBuildFile.writeText( @@ -173,4 +293,112 @@ class SentryPluginSourceContextTest : ktContents ) } + + @Test + fun `uploadSourceBundle task is up-to-date on subsequent builds`() { + val sentryCli = SentryCliProvider.getSentryCliPath(File(""), File("")) + sentryPropertiesFile.writeText("cli.executable=$sentryCli") + + runner.appendArguments("app:assembleRelease") + appBuildFile.writeText( + // language=Groovy + """ + plugins { + id "com.android.application" + id "io.sentry.android.gradle" + } + + android { + namespace 'com.example' + + buildFeatures { + buildConfig false + } + } + + sentry { + includeSourceContext = true + autoUploadSourceContext = false + autoUploadProguardMapping = false + org = "sentry-sdks" + projectName = "sentry-android" + } + """.trimIndent() + ) + testProjectDir.withDummyComposeFile() + + val firstBuild = runner.build() + + val subsequentBuild = runner.build() + + assertEquals( + firstBuild.task(":app:sentryUploadSourceBundleRelease")?.outcome, + SUCCESS + ) + + assertEquals( + subsequentBuild.task(":app:sentryUploadSourceBundleRelease")?.outcome, + UP_TO_DATE + ) + + assertTrue(subsequentBuild.output) { "BUILD SUCCESSFUL" in subsequentBuild.output } + } + + @Test + fun `uploadSourceBundle task is not up-to-date on subsequent builds if cli path changes`() { + val sentryCli = SentryCliProvider.getSentryCliPath(File(""), File("")) + sentryPropertiesFile.writeText("cli.executable=$sentryCli") + + runner.appendArguments("app:assembleRelease") + appBuildFile.writeText( + // language=Groovy + """ + plugins { + id "com.android.application" + id "io.sentry.android.gradle" + } + + android { + namespace 'com.example' + + buildFeatures { + buildConfig false + } + } + + sentry { + includeSourceContext = true + autoUploadSourceContext = false + autoUploadProguardMapping = false + org = "sentry-sdks" + projectName = "sentry-android" + } + """.trimIndent() + ) + testProjectDir.withDummyComposeFile() + + val firstBuild = runner.build() + + val tempDir = Files.createTempDirectory("sentry-test") + val newCliPath = tempDir.resolve("sentry-cli") + Files.copy(Path.of(sentryCli), tempDir.resolve("sentry-cli")) + newCliPath.toFile().deleteOnExit() + tempDir.toFile().deleteOnExit() + + sentryPropertiesFile.writeText("cli.executable=$newCliPath") + + val subsequentBuild = runner.build() + + assertEquals( + firstBuild.task(":app:sentryUploadSourceBundleRelease")?.outcome, + SUCCESS + ) + + assertEquals( + subsequentBuild.task(":app:sentryUploadSourceBundleRelease")?.outcome, + SUCCESS + ) + + assertTrue(subsequentBuild.output) { "BUILD SUCCESSFUL" in subsequentBuild.output } + } } diff --git a/plugin-build/src/test/kotlin/io/sentry/android/gradle/integration/SentryPluginTest.kt b/plugin-build/src/test/kotlin/io/sentry/android/gradle/integration/SentryPluginTest.kt index 51f002d4..d9b781d1 100644 --- a/plugin-build/src/test/kotlin/io/sentry/android/gradle/integration/SentryPluginTest.kt +++ b/plugin-build/src/test/kotlin/io/sentry/android/gradle/integration/SentryPluginTest.kt @@ -7,10 +7,12 @@ import io.sentry.android.gradle.util.SemVer import io.sentry.android.gradle.verifyDependenciesReportAndroid import io.sentry.android.gradle.verifyIntegrationList import io.sentry.android.gradle.verifyProguardUuid +import java.io.File import kotlin.test.assertEquals import kotlin.test.assertFalse import kotlin.test.assertNotEquals import kotlin.test.assertTrue +import org.gradle.testkit.runner.TaskOutcome import org.gradle.util.GradleVersion import org.hamcrest.CoreMatchers.`is` import org.junit.Assert.assertThrows @@ -44,7 +46,7 @@ class SentryPluginTest : } @Test - fun `regenerates UUID every build`() { + fun `does not regenerate UUID every build`() { runner.appendArguments(":app:assembleRelease") runner.build() @@ -53,9 +55,179 @@ class SentryPluginTest : runner.build() val uuid2 = verifyProguardUuid(testProjectDir.root) + assertEquals(uuid1, uuid2) + } + + @Test + fun `regenerates UUID if mapping file changes`() { + runner.appendArguments(":app:assembleRelease") + + val sourceDir = File(testProjectDir.root, "app/src/main/java/com/example").apply { + mkdirs() + } + + val manifest = File(testProjectDir.root, "app/src/main/AndroidManifest.xml") + manifest.writeText( + """ + + + + + + """.trimIndent() + ) + + val sourceFile = File(sourceDir, "MainActivity.java") + sourceFile.createNewFile() + sourceFile.writeText( + """ + package com.example; + import android.app.Activity; + import android.os.Bundle; + + public class MainActivity extends Activity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + hello(); + } + + public void hello() { + System.out.println("Hello"); + } + } + """.trimIndent() + ) + + runner.appendArguments(":app:assembleRelease").build() + val uuid1 = verifyProguardUuid(testProjectDir.root) + + sourceFile.writeText( + """ + package com.example; + import android.app.Activity; + import android.os.Bundle; + + public class MainActivity extends Activity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + hello(); + hello2(); + } + + public void hello() { + System.out.println("Hello"); + } + + public void hello2() { + System.out.println("Hello2"); + } + } + """.trimIndent() + ) + + runner.appendArguments(":app:assembleRelease").build() + val uuid2 = verifyProguardUuid(testProjectDir.root) + assertNotEquals(uuid1, uuid2) } + @Test + fun `does not regenerate UUID if mapping file stays the same`() { + runner.appendArguments(":app:assembleRelease") + + val sourceDir = File(testProjectDir.root, "app/src/main/java/com/example").apply { + mkdirs() + } + + val manifest = File(testProjectDir.root, "app/src/main/AndroidManifest.xml") + manifest.writeText( + """ + + + + + + """.trimIndent() + ) + + val sourceFile = File(sourceDir, "MainActivity.java") + sourceFile.createNewFile() + sourceFile.writeText( + """ + package com.example; + import android.app.Activity; + import android.os.Bundle; + + public class MainActivity extends Activity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + hello(); + } + + public void hello() { + System.out.println("Hello"); + } + } + """.trimIndent() + ) + + runner.appendArguments(":app:assembleRelease").build() + val uuid1 = verifyProguardUuid(testProjectDir.root) + + sourceFile.writeText( + """ + package com.example; + import android.app.Activity; + import android.os.Bundle; + + public class MainActivity extends Activity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + hello(); + } + + public void hello() { + System.out.println("Hello2"); + } + } + """.trimIndent() + ) + + val build = runner.appendArguments(":app:assembleRelease").build() + val uuid2 = verifyProguardUuid(testProjectDir.root) + + assertEquals( + build.task(":app:generateSentryProguardUuidRelease")?.outcome, + TaskOutcome.UP_TO_DATE + ) + + assertEquals(uuid1, uuid2) + } + + @Test + fun `generateSentryDebugMetaProperties task is up-to-date on subsequent builds`() { + runner.appendArguments(":app:assembleRelease") + + val firstBuild = runner.build() + val subsequentBuild = runner.build() + + assertEquals( + firstBuild.task(":app:generateSentryDebugMetaPropertiesRelease")?.outcome, + TaskOutcome.SUCCESS + ) + + assertEquals( + subsequentBuild.task(":app:generateSentryDebugMetaPropertiesRelease")?.outcome, + TaskOutcome.UP_TO_DATE + ) + + assertTrue(subsequentBuild.output) { "BUILD SUCCESSFUL" in subsequentBuild.output } + } + @Test fun `does not include a UUID in the APK`() { // isMinifyEnabled is disabled by default in debug builds diff --git a/plugin-build/src/test/kotlin/io/sentry/android/gradle/tasks/GenerateBundleIdTaskTest.kt b/plugin-build/src/test/kotlin/io/sentry/android/gradle/tasks/GenerateBundleIdTaskTest.kt index ea6e738c..0e2ee410 100644 --- a/plugin-build/src/test/kotlin/io/sentry/android/gradle/tasks/GenerateBundleIdTaskTest.kt +++ b/plugin-build/src/test/kotlin/io/sentry/android/gradle/tasks/GenerateBundleIdTaskTest.kt @@ -23,6 +23,7 @@ class GenerateBundleIdTaskTest { project, project.extensions.findByName("sentry") as SentryPluginExtension, null, + null, project.layout.buildDirectory.dir("dummy/folder/"), project.objects.property(Boolean::class.java).convention(true), "test" @@ -46,6 +47,7 @@ class GenerateBundleIdTaskTest { project, project.extensions.findByName("sentry") as SentryPluginExtension, null, + null, project.layout.buildDirectory.dir("dummy/folder/"), project.objects.property(Boolean::class.java).convention(true), "test" diff --git a/plugin-build/src/test/kotlin/io/sentry/android/gradle/tasks/SentryGenerateDebugMetaPropertiesTaskTest.kt b/plugin-build/src/test/kotlin/io/sentry/android/gradle/tasks/SentryGenerateDebugMetaPropertiesTaskTest.kt index dbd4b6c5..39c54e68 100644 --- a/plugin-build/src/test/kotlin/io/sentry/android/gradle/tasks/SentryGenerateDebugMetaPropertiesTaskTest.kt +++ b/plugin-build/src/test/kotlin/io/sentry/android/gradle/tasks/SentryGenerateDebugMetaPropertiesTaskTest.kt @@ -18,10 +18,13 @@ class SentryGenerateDebugMetaPropertiesTaskTest { @Test fun `generate debug-meta properties generates proguard mapping UUID and bundle id correctly`() { val project = createProject() + val sourceDirs = project.files() + sourceDirs.from("dummy/src/a") val bundleIdTask = GenerateBundleIdTask.register( project, project.extensions.findByName("sentry") as SentryPluginExtension, null, + null, project.layout.buildDirectory.dir("dummy/folder/"), project.objects.property(Boolean::class.java).convention(true), "test" @@ -31,6 +34,7 @@ class SentryGenerateDebugMetaPropertiesTaskTest { project.extensions.findByName("sentry") as SentryPluginExtension, null, project.layout.buildDirectory.dir("dummy/folder/"), + null, "test" ) val idGenerationTasks = listOf( @@ -66,6 +70,7 @@ class SentryGenerateDebugMetaPropertiesTaskTest { project, project.extensions.findByName("sentry") as SentryPluginExtension, null, + null, project.layout.buildDirectory.dir("dummy/folder/"), project.objects.property(Boolean::class.java).convention(true), "test" @@ -75,6 +80,7 @@ class SentryGenerateDebugMetaPropertiesTaskTest { project.extensions.findByName("sentry") as SentryPluginExtension, null, project.layout.buildDirectory.dir("dummy/folder/"), + null, "test" ) val idGenerationTasks = listOf(