Skip to content

Commit a02999b

Browse files
runningcodeclaudesentry-release-bot[bot]
authored
feat(snapshot): Merge snapshot extension into main sentry DSL (#1136)
* feat(snapshot): Merge snapshot extension into main sentry DSL Move snapshot configuration from the standalone `sentrySnapshot` plugin into the main `sentry { snapshots { } }` extension. This consolidates the DSL surface and auto-detects Paparazzi for test generation. - Add `SnapshotsExtension` nested under `SentryPluginExtension` - Move Paparazzi detection and test generation wiring into `SentryPlugin` - Wire `sentryUploadSnapshots` task to Paparazzi output directory - Rename generate task to `sentryGenerateSnapshotsTests` for consistency - Remove standalone `SentrySnapshotPlugin` and `SentrySnapshotExtension` - Use plural "snapshots" naming consistently Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(snapshot): Wire upload task to test task output Use dependsOn to ensure Paparazzi record task runs before upload, and derive snapshotsPath from the test task's output directory instead of a hardcoded path. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(snapshot): Add enabled flag to snapshots extension Gate test generation and dependency addition on snapshots.enabled so that applying Paparazzi alongside the Sentry plugin doesn't affect normal Paparazzi task execution. Defaults to false. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(snapshot): Remove stale distribution config for deleted plugin Remove the sentrySnapshotPlugin marker distribution and publication task references that were left behind when the plugin was removed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(snapshot): Gate upload task registration on snapshots.enabled The upload task and its Paparazzi wiring were registered unconditionally for all variants. This caused projects using both the Sentry plugin and Paparazzi to get unwanted task dependencies even with snapshots disabled. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(snapshot): Move dependency addition outside onVariants loop testImplementation is a global configuration, not per-variant. Move the ComposablePreviewScanner dependency addition to afterEvaluate (gated on enabled) so it runs once instead of once per variant. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * release: 6.4.0-alpha.5 * fix(snapshot): Defer test task lookup to afterEvaluate The testDebugUnitTest task is not yet registered by AGP when onVariants runs. Wrap the Paparazzi wiring in afterEvaluate so the test task exists when we look it up. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(snapshot): Consolidate all snapshot logic in AndroidComponentsConfig Move test generation, dependency addition, and upload task wiring into configureSnapshotsTasks so all snapshot logic lives in one place. Use the AGP hostTests/unitTest API for generated source wiring, and afterEvaluate for the test task output lookup. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: Remove snapshot changelog entry Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style(snapshot): Apply spotless formatting Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(snapshot): Guard against missing unit test tasks Not all variants have unit test tasks (e.g. users can disable them), so skip wiring the snapshot upload task if the test task doesn't exist. Uses tasks.names to avoid eagerly realizing tasks. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(snapshot): Remove unnecessary afterEvaluate for dependency addition Adding the testImplementation dependency inside a nested afterEvaluate risks the configuration already being resolved and immutable. The withPlugin callback already fires during configuration, so the wrapper is unnecessary. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(snapshot): Move dependency addition outside per-variant loop The testImplementation dependency for ComposablePreviewScanner was being added N times (once per variant) since configureSnapshotsTasks runs inside onVariants. Move it to a single withPlugin block before onVariants to add it only once. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Revert "fix(snapshot): Move dependency addition outside per-variant loop" This reverts commit f77e724. --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: runningcode <332597+runningcode@users.noreply.github.com> Co-authored-by: sentry-release-bot[bot] <180476844+sentry-release-bot[bot]@users.noreply.github.com>
1 parent da847d2 commit a02999b

9 files changed

Lines changed: 100 additions & 105 deletions

File tree

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
# Changelog
22

3+
## 6.4.0-alpha.5
4+
5+
### Internal Changes 🔧
6+
7+
#### Deps
8+
9+
- Update CLI to v3.3.5 by @github-actions in [#1132](https://github.com/getsentry/sentry-android-gradle-plugin/pull/1132)
10+
- Update CLI to v3.3.4 by @github-actions in [#1122](https://github.com/getsentry/sentry-android-gradle-plugin/pull/1122)
11+
312
## 6.4.0-alpha.4
413

514
### Internal Changes 🔧

plugin-build/build.gradle.kts

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -164,10 +164,6 @@ gradlePlugin {
164164
id = "io.sentry.jvm.gradle"
165165
implementationClass = "io.sentry.jvm.gradle.SentryJvmPlugin"
166166
}
167-
register("sentrySnapshotPlugin") {
168-
id = "io.sentry.android.snapshot"
169-
implementationClass = "io.sentry.android.gradle.snapshot.SentrySnapshotPlugin"
170-
}
171167
register("sentrySnapshotMetadataPlugin") {
172168
id = "io.sentry.android.snapshot.metadata"
173169
implementationClass =
@@ -205,9 +201,6 @@ distributions {
205201
create("sentryJvmPluginMarker") {
206202
contents { from("build${sep}publications${sep}sentryJvmPluginPluginMarkerMaven") }
207203
}
208-
create("sentrySnapshotPluginMarker") {
209-
contents { from("build${sep}publications${sep}sentrySnapshotPluginPluginMarkerMaven") }
210-
}
211204
create("sentrySnapshotMetadataPluginMarker") {
212205
contents { from("build${sep}publications${sep}sentrySnapshotMetadataPluginPluginMarkerMaven") }
213206
}
@@ -257,14 +250,6 @@ tasks.named("sentryPluginMarkerDistZip").configure {
257250
dependsOn("generatePomFileForSentryPluginPluginMarkerMavenPublication")
258251
}
259252

260-
tasks.named("sentrySnapshotPluginMarkerDistTar").configure {
261-
dependsOn("generatePomFileForSentrySnapshotPluginPluginMarkerMavenPublication")
262-
}
263-
264-
tasks.named("sentrySnapshotPluginMarkerDistZip").configure {
265-
dependsOn("generatePomFileForSentrySnapshotPluginPluginMarkerMavenPublication")
266-
}
267-
268253
tasks.named("sentrySnapshotMetadataPluginMarkerDistTar").configure {
269254
dependsOn("generatePomFileForSentrySnapshotMetadataPluginPluginMarkerMavenPublication")
270255
}

plugin-build/gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ org.gradle.parallel=true
77

88
name = sentry-android-gradle-plugin
99
group = io.sentry
10-
version = 6.4.0-alpha.4
10+
version = 6.4.0-alpha.5
1111
sdk_version = 8.37.1
1212

1313
# publication pom properties

plugin-build/src/main/kotlin/io/sentry/android/gradle/AndroidComponentsConfig.kt

Lines changed: 72 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ import com.android.build.api.instrumentation.InstrumentationParameters
99
import com.android.build.api.instrumentation.InstrumentationScope
1010
import com.android.build.api.variant.ApplicationAndroidComponentsExtension
1111
import com.android.build.api.variant.ApplicationVariant
12+
import com.android.build.api.variant.HostTestBuilder.Companion.UNIT_TEST_TYPE
1213
import com.android.build.api.variant.Variant
14+
import com.android.build.gradle.BaseExtension
1315
import com.android.build.gradle.internal.utils.setDisallowChanges
1416
import io.sentry.android.gradle.SentryPlugin.Companion.sep
1517
import io.sentry.android.gradle.SentryPropertiesFileProvider.getPropertiesFilePath
@@ -20,6 +22,7 @@ import io.sentry.android.gradle.SentryTasksProvider.getMappingFileProvider
2022
import io.sentry.android.gradle.extensions.SentryPluginExtension
2123
import io.sentry.android.gradle.instrumentation.SpanAddingClassVisitorFactory
2224
import io.sentry.android.gradle.services.SentryModulesService
25+
import io.sentry.android.gradle.snapshot.GenerateSnapshotTestsTask
2326
import io.sentry.android.gradle.sourcecontext.OutputPaths
2427
import io.sentry.android.gradle.sourcecontext.SourceContext
2528
import io.sentry.android.gradle.tasks.GenerateDistributionPropertiesTask
@@ -46,6 +49,7 @@ import org.gradle.api.file.Directory
4649
import org.gradle.api.provider.Provider
4750
import org.gradle.api.provider.SetProperty
4851
import org.gradle.api.tasks.TaskProvider
52+
import org.gradle.api.tasks.testing.Test
4953
import org.gradle.internal.build.event.BuildEventListenerRegistryInternal
5054

5155
fun ApplicationAndroidComponentsExtension.configure(
@@ -78,7 +82,9 @@ fun ApplicationAndroidComponentsExtension.configure(
7882
}
7983
}
8084

81-
variant.configureSnapshotsTasks(project, extension, cliExecutable, sentryOrg, sentryProject)
85+
if (extension.snapshots.enabled.get()) {
86+
variant.configureSnapshotsTasks(project, extension, cliExecutable, sentryOrg, sentryProject)
87+
}
8288

8389
if (isVariantAllowed(extension, variant.name, variant.flavorName, variant.buildType)) {
8490
val paths = OutputPaths(project, variant.name)
@@ -461,21 +467,74 @@ private fun ApplicationVariant.configureSnapshotsTasks(
461467
cliExecutable: Provider<String>,
462468
sentryOrg: String?,
463469
sentryProject: String?,
464-
): TaskProvider<SentryUploadSnapshotsTask> {
470+
) {
465471
val variant = AndroidVariant74(this)
466472
val sentryProps = getPropertiesFilePath(project, variant)
473+
val taskSuffix = name.capitalized
467474

468-
return SentryUploadSnapshotsTask.register(
469-
project = project,
470-
extension = extension,
471-
sentryTelemetryProvider = null,
472-
cliExecutable = cliExecutable,
473-
sentryOrgOverride = sentryOrg,
474-
sentryProjectOverride = sentryProject,
475-
applicationId = applicationId,
476-
sentryProperties = sentryProps,
477-
taskSuffix = name.capitalized,
478-
)
475+
// Register the upload task
476+
val uploadTask =
477+
SentryUploadSnapshotsTask.register(
478+
project = project,
479+
extension = extension,
480+
sentryTelemetryProvider = null,
481+
cliExecutable = cliExecutable,
482+
sentryOrgOverride = sentryOrg,
483+
sentryProjectOverride = sentryProject,
484+
applicationId = applicationId,
485+
sentryProperties = sentryProps,
486+
taskSuffix = taskSuffix,
487+
)
488+
489+
// Wire Paparazzi test generation and upload task when the Paparazzi plugin is applied
490+
project.pluginManager.withPlugin("app.cash.paparazzi") {
491+
val android = project.extensions.getByType(BaseExtension::class.java)
492+
493+
project.dependencies.add(
494+
"testImplementation",
495+
"io.github.sergio-sastre.ComposablePreviewScanner:android:0.8.1",
496+
)
497+
498+
val generateTask =
499+
GenerateSnapshotTestsTask.register(
500+
project,
501+
extension.snapshots,
502+
android,
503+
this@configureSnapshotsTasks,
504+
)
505+
506+
if (AgpVersions.isAGP90(AgpVersions.CURRENT)) {
507+
hostTests[UNIT_TEST_TYPE]?.apply {
508+
sources.java?.addGeneratedSourceDirectory(
509+
generateTask,
510+
GenerateSnapshotTestsTask::outputDir,
511+
)
512+
}
513+
} else {
514+
@Suppress("DEPRECATION_ERROR")
515+
unitTest?.apply {
516+
sources.java?.addGeneratedSourceDirectory(
517+
generateTask,
518+
GenerateSnapshotTestsTask::outputDir,
519+
)
520+
}
521+
}
522+
523+
project.afterEvaluate {
524+
// Not all variants have unit test tasks (e.g. users can disable them),
525+
// so skip wiring if the task doesn't exist.
526+
val testTaskName = "test${taskSuffix}UnitTest"
527+
if (testTaskName !in project.tasks.names) return@afterEvaluate
528+
529+
val testTask = project.tasks.named(testTaskName, Test::class.java)
530+
uploadTask.configure { task ->
531+
task.dependsOn("recordPaparazzi$taskSuffix")
532+
task.snapshotsPath.fileProvider(
533+
testTask.map { it.outputs.files.files.first { file -> file.name == "snapshots" } }
534+
)
535+
}
536+
}
537+
}
479538
}
480539

481540
/**

plugin-build/src/main/kotlin/io/sentry/android/gradle/extensions/SentryPluginExtension.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,13 @@ abstract class SentryPluginExtension @Inject constructor(project: Project) {
127127
vcsInfoAction.execute(vcsInfo)
128128
}
129129

130+
val snapshots: SnapshotsExtension = objects.newInstance(SnapshotsExtension::class.java)
131+
132+
@Experimental
133+
fun snapshots(snapshotsAction: Action<SnapshotsExtension>) {
134+
snapshotsAction.execute(snapshots)
135+
}
136+
130137
/**
131138
* Disables or enables the reporting of dependencies metadata for Sentry. If enabled the plugin
132139
* will collect external dependencies and will take care of uploading them to Sentry as part of

plugin-build/src/main/kotlin/io/sentry/android/gradle/snapshot/SentrySnapshotExtension.kt renamed to plugin-build/src/main/kotlin/io/sentry/android/gradle/extensions/SnapshotsExtension.kt

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
1-
package io.sentry.android.gradle.snapshot
1+
package io.sentry.android.gradle.extensions
22

3+
import javax.inject.Inject
34
import org.gradle.api.model.ObjectFactory
45
import org.gradle.api.provider.ListProperty
56
import org.gradle.api.provider.Property
67
import org.jetbrains.annotations.ApiStatus
78

8-
/**
9-
* Experimental extension for configuring Compose @Preview snapshot testing. This API is subject to
10-
* change and will eventually be merged into the main `sentry` extension.
11-
*/
129
@ApiStatus.Experimental
13-
abstract class SentrySnapshotExtension(objects: ObjectFactory) {
10+
open class SnapshotsExtension @Inject constructor(objects: ObjectFactory) {
11+
12+
val enabled: Property<Boolean> = objects.property(Boolean::class.java).convention(false)
1413

1514
val includePrivatePreviews: Property<Boolean> =
1615
objects.property(Boolean::class.java).convention(false)

plugin-build/src/main/kotlin/io/sentry/android/gradle/snapshot/GenerateSnapshotTestsTask.kt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package io.sentry.android.gradle.snapshot
33
import com.android.build.api.variant.ApplicationVariant
44
import com.android.build.gradle.BaseExtension
55
import io.sentry.android.gradle.SentryTasksProvider.capitalized
6+
import io.sentry.android.gradle.extensions.SnapshotsExtension
67
import java.io.File
78
import org.gradle.api.DefaultTask
89
import org.gradle.api.Project
@@ -58,24 +59,25 @@ abstract class GenerateSnapshotTestsTask : DefaultTask() {
5859

5960
fun register(
6061
project: Project,
61-
extension: SentrySnapshotExtension,
62+
extension: SnapshotsExtension,
6263
android: BaseExtension,
6364
variant: ApplicationVariant,
6465
): TaskProvider<GenerateSnapshotTestsTask> {
6566
return project.tasks.register(
66-
"generateSentrySnapshotTests${variant.name.capitalized}",
67+
"sentryGenerateSnapshotsTests${variant.name.capitalized}",
6768
GenerateSnapshotTestsTask::class.java,
6869
) { task ->
6970
task.includePrivatePreviews.set(extension.includePrivatePreviews)
7071
task.theme.set(extension.theme)
7172
// Fall back to the Android namespace when the user doesn't configure packageTrees
73+
// TODO do we actually need this?
7274
task.packageTrees.set(
7375
extension.packageTrees.map { packages ->
7476
packages.ifEmpty { listOf(android.namespace!!) }
7577
}
7678
)
7779
task.outputDir.set(
78-
project.layout.buildDirectory.dir("generated/sentry/snapshotTests/${variant.name}")
80+
project.layout.buildDirectory.dir("generated/sentry/snapshotsTests/${variant.name}")
7981
)
8082
}
8183
}

plugin-build/src/main/kotlin/io/sentry/android/gradle/snapshot/SentrySnapshotPlugin.kt

Lines changed: 0 additions & 66 deletions
This file was deleted.

sentry-kotlin-compiler-plugin/gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ org.gradle.parallel=true
44

55
GROUP = io.sentry
66
POM_ARTIFACT_ID = sentry-kotlin-compiler-plugin
7-
VERSION_NAME = 6.4.0-alpha.4
7+
VERSION_NAME = 6.4.0-alpha.5
88

99
# publication pom properties
1010
POM_NAME=Sentry Kotlin Compiler Plugin

0 commit comments

Comments
 (0)