Skip to content

ref(snapshots): Register snapshot upload tasks per variant#1105

Merged
runningcode merged 2 commits intomainfrom
no/per-variant-snapshot-tasks
Mar 16, 2026
Merged

ref(snapshots): Register snapshot upload tasks per variant#1105
runningcode merged 2 commits intomainfrom
no/per-variant-snapshot-tasks

Conversation

@runningcode
Copy link
Copy Markdown
Contributor

@runningcode runningcode commented Mar 13, 2026

Motivation

In practice, users will want to wire snapshotsPath to the output of another task that generates the snapshots. Configuring this through an extension block is clunky because extensions aren't designed for task-to-task wiring — directly configuring the task makes this straightforward.

Before

A single global task configured via the sentry.snapshots extension block. Users had to manually set appId:

sentry {
    snapshots {
        appId = "com.example.app"
        path = file("path/to/snapshots")
    }
}

In practice, however, the task will need to be configured with the output of the task that generates the snapshot. So the appId is set in the extension and the path in the task.

tasks.named("sentryUploadSnapshots") {
    snapshotsPath.set(generateSnapshots.flatMap { it.outputDir })
}
./gradlew sentryUploadSnapshots

After

Per-variant tasks with appId auto-wired from the variant's applicationId. Users only need to set snapshotsPath on the task directly and wiring the output of a snapshot-generating task is also straightforward.

tasks.named("sentryUploadSnapshotsDebug") {
    snapshotsPath.set(generateSnapshots.flatMap { it.outputDir })
}
./gradlew sentryUploadSnapshotsDebug

Fixes EME-920

#skip-changelog

🤖 Generated with Claude Code

@linear-code
Copy link
Copy Markdown

linear-code bot commented Mar 13, 2026

Move SentryUploadSnapshotsTask registration from SentryPlugin (global)
into AndroidComponentsConfig (per-variant), matching the pattern used by
proguard mappings, source bundles, and native symbols.

This wires applicationId directly from ApplicationVariant, eliminating
the need for the SnapshotsExtension and manual appId configuration.
Also wires sentry telemetry into the task.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@runningcode runningcode force-pushed the no/per-variant-snapshot-tasks branch from abfbe2b to 9d95db7 Compare March 13, 2026 08:59
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 13, 2026

Semver Impact of This PR

🟢 Patch (bug fixes)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


This PR will not appear in the changelog.


🤖 This preview updates automatically when you update the PR.

@runningcode runningcode marked this pull request as ready for review March 13, 2026 09:17
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

task.vcsBaseRef.set(extension.vcsInfo.baseRef)
task.vcsPrNumber.set(extension.vcsInfo.prNumber)
sentryTelemetryProvider?.let { task.sentryTelemetryService.set(it) }
task.withSentryTelemetry(extension, sentryTelemetryProvider)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reversed ordering of asSentryCliExec and withSentryTelemetry

Low Severity

The call order of task.withSentryTelemetry() and task.asSentryCliExec() is reversed compared to every other task registration in the codebase (SentryUploadAppArtifactTask, SentryUploadNativeSymbolsTask, BundleSourcesTask, UploadSourceBundleTask). All other tasks call asSentryCliExec() first, then withSentryTelemetry(). Since both methods register doFirst/doLast actions, and Gradle executes doFirst in LIFO and doLast in FIFO order, this swap changes the runtime execution order of telemetry span tracking relative to CLI error handling.

Fix in Cursor Fix in Web

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably this is good to align with other tasks, since SentryTelemetryService does some things (e.g. error handling) based on the output of SentryCliException (which is handled in asSentryCliExec), so should probably be reordered

sentryProject,
)

variant.configureSnapshotsTasks(
Copy link
Copy Markdown
Contributor Author

@runningcode runningcode Mar 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, this is done inside the isVariantAllowed which means that if you disable sentry for the debug variants then you will need to upload your snapshots with the release variant.
This doesn't pose any problems other than it might be confusing for users.

They will have to do something like this:

afterEvaluate {
tasks.named<SentryUploadSnapshotsTask>("sentryUploadSnapshotsRelease") {
    dependsOn(tasks.named("recordRoborazziDebug"))
    snapshotsPath.set(file("snapshotsDir"))
  }
}

Alternatively, we can move it outside the isVariantAllowed but then we would either not have telemetry or move the telemetry outside the isVariantAllowed.

I think we should do it like this since it is the safest option, we can always move it outside isVariantAllowed if customers complain.

Copy link
Copy Markdown
Member

@romtsn romtsn Mar 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this sounds good -- named should throw if a task with that name does not exist right? So even if they ignored a variant, gradle won't let them wire a non-existing task and would surface this quickly, so hopefully this would resolve the confusion

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Comment on lines 32 to +35
@get:PathSensitive(PathSensitivity.RELATIVE)
abstract val snapshotsPath: DirectoryProperty

@Option(option = "snapshots-path", description = "Path to the snapshots directory to upload")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: The SentryUploadSnapshotsTask will cause a Gradle validation error if the required snapshotsPath property is not explicitly configured by the user, as it's no longer set by default.
Severity: MEDIUM

Suggested Fix

Add the @get:Optional annotation to the snapshotsPath property. In the task's execution logic, check if the property has a value. If it doesn't, either skip the task's execution or log a clear warning message instructing the user on how to configure the snapshotsPath property.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location:
plugin-build/src/main/kotlin/io/sentry/android/gradle/tasks/SentryUploadSnapshotsTask.kt#L32-L35

Potential issue: The `snapshotsPath` property in `SentryUploadSnapshotsTask` is declared
as a required `@InputDirectory` but is no longer configured by default from the Sentry
extension. If a user runs a `sentryUploadSnapshots...` task without manually configuring
the `snapshotsPath` for that specific task, the build will fail with a generic Gradle
validation error. This happens because the property is used via `snapshotsPath.get()`
without a prior check, and the missing `@Optional` annotation causes Gradle to enforce
its presence before task execution. The error message from Gradle is not specific to the
Sentry plugin, making it difficult for users to understand that they need to set this
property.

Did we get this right? 👍 / 👎 to inform future reviews.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is how we expect it to be configured. The task needs to be manually wired now.

Copy link
Copy Markdown
Member

@romtsn romtsn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the approach looks good to me -- given there's no auto-wiring for this task anyway, i think it's fine to configure it directly 👍

@runningcode
Copy link
Copy Markdown
Contributor Author

Thanks!

@runningcode runningcode merged commit 58fade7 into main Mar 16, 2026
20 of 21 checks passed
@runningcode runningcode deleted the no/per-variant-snapshot-tasks branch March 16, 2026 15:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants