Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

### Features

- (Experimental) Add snapshot upload support via `sentryUploadSnapshots` task ([#1091](https://github.com/getsentry/sentry-android-gradle-plugin/pull/1091))

### Dependencies

- Bump CLI from v3.2.0 to v3.2.3 ([#1084](https://github.com/getsentry/sentry-android-gradle-plugin/pull/1084), [#1090](https://github.com/getsentry/sentry-android-gradle-plugin/pull/1090))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ 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.tasks.SentryUploadSnapshotsTask
import io.sentry.android.gradle.telemetry.SentryTelemetryService
import io.sentry.android.gradle.util.AgpVersions
import java.io.File
import javax.inject.Inject
Expand Down Expand Up @@ -65,6 +67,22 @@ constructor(private val buildEvents: BuildEventListenerRegistryInternal) : Plugi
sentryProjectParameter,
)

val sentryTelemetryProvider = SentryTelemetryService.register(project)
SentryUploadSnapshotsTask.register(
Comment thread
runningcode marked this conversation as resolved.
project = project,
extension = extension,
sentryTelemetryProvider = sentryTelemetryProvider,
debug = extension.debug,
cliExecutable = cliExecutable,
sentryOrg = sentryOrgParameter?.let { project.provider { it } } ?: extension.org,
sentryProject =
sentryProjectParameter?.let { project.provider { it } } ?: extension.projectName,
sentryAuthToken = extension.authToken,
sentryUrl = extension.url,
appId = extension.snapshots.appId,
snapshotsPath = extension.snapshots.path,
)
Comment thread
sentry[bot] marked this conversation as resolved.

project.installDependencies(extension, true)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,14 @@ abstract class SentryPluginExtension @Inject constructor(project: Project) {
vcsInfoAction.execute(vcsInfo)
}

val snapshots: SnapshotsExtension = objects.newInstance(SnapshotsExtension::class.java)

/** Configure the snapshots upload. */
@Experimental
fun snapshots(snapshotsAction: Action<SnapshotsExtension>) {
snapshotsAction.execute(snapshots)
}

/**
* Disables or enables the reporting of dependencies metadata for Sentry. If enabled the plugin
* will collect external dependencies and will take care of uploading them to Sentry as part of
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package io.sentry.android.gradle.extensions

import javax.inject.Inject
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.Property
import org.jetbrains.annotations.ApiStatus.Experimental

@Experimental
open class SnapshotsExtension @Inject constructor(objects: ObjectFactory) {

/** The application identifier used to associate snapshots with an app. */
val appId: Property<String> = objects.property(String::class.java)
Comment thread
runningcode marked this conversation as resolved.

/** The path to the folder containing snapshots to upload. */
val path: DirectoryProperty = objects.directoryProperty()
Comment thread
runningcode marked this conversation as resolved.
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package io.sentry.android.gradle.tasks

import io.sentry.android.gradle.autoinstall.SENTRY_GROUP
import io.sentry.android.gradle.extensions.SentryPluginExtension
import io.sentry.android.gradle.telemetry.SentryTelemetryService
import io.sentry.android.gradle.telemetry.withSentryTelemetry
import io.sentry.android.gradle.util.asSentryCliExec
import org.gradle.api.Project
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputDirectory
import org.gradle.api.tasks.PathSensitive
import org.gradle.api.tasks.PathSensitivity
import org.gradle.api.tasks.TaskProvider
import org.gradle.work.DisableCachingByDefault

@DisableCachingByDefault(because = "Uploads should not be cached")
abstract class SentryUploadSnapshotsTask : SentryCliExecTask() {

init {
group = SENTRY_GROUP
description = "Uploads snapshots to Sentry"
}

@get:Input abstract val appId: Property<String>

@get:InputDirectory
@get:PathSensitive(PathSensitivity.RELATIVE)
abstract val snapshotsPath: DirectoryProperty
Comment thread
sentry[bot] marked this conversation as resolved.

override fun getArguments(args: MutableList<String>) {
args.add("build")
args.add("snapshots")
args.add("--app-id")
args.add(appId.get())
args.add(snapshotsPath.get().asFile.absolutePath)
}

companion object {
fun register(
project: Project,
extension: SentryPluginExtension,
sentryTelemetryProvider: Provider<SentryTelemetryService>?,
debug: Property<Boolean>,
cliExecutable: Provider<String>,
sentryOrg: Provider<String>,
sentryProject: Provider<String>,
sentryAuthToken: Property<String>,
sentryUrl: Property<String>,
appId: Property<String>,
snapshotsPath: DirectoryProperty,
): TaskProvider<SentryUploadSnapshotsTask> {
return project.tasks.register(
"sentryUploadSnapshots",
SentryUploadSnapshotsTask::class.java,
) { task ->
task.workingDir(project.rootDir)
task.debug.set(debug)
task.cliExecutable.set(cliExecutable)
task.sentryOrganization.set(sentryOrg)
task.sentryProject.set(sentryProject)
task.sentryAuthToken.set(sentryAuthToken)
task.sentryUrl.set(sentryUrl)
task.appId.set(appId)
task.snapshotsPath.set(snapshotsPath)
task.sentryTelemetryService.set(sentryTelemetryProvider)
task.asSentryCliExec()
task.withSentryTelemetry(extension, sentryTelemetryProvider)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package io.sentry.android.gradle.tasks

import java.io.File
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertNull
import kotlin.test.assertTrue
import org.gradle.api.Project
import org.gradle.testfixtures.ProjectBuilder
import org.junit.Test

class SentryUploadSnapshotsTaskTest {

@Test
fun `cli-executable is set correctly`() {
val task =
createTestTask {
it.cliExecutable.set("sentry-cli")
it.appId.set("com.example")
it.snapshotsPath.set(File("/path/to/snapshots"))
}

val args = task.computeCommandLineArgs()

assertTrue("sentry-cli" in args)
assertTrue("build" in args)
assertTrue("snapshots" in args)
assertTrue("--app-id" in args)
assertTrue("com.example" in args)
assertTrue("/path/to/snapshots" in args)
assertFalse("--log-level=debug" in args)
}

@Test
fun `--log-level=debug is set correctly`() {
val task =
createTestTask {
it.cliExecutable.set("sentry-cli")
it.appId.set("com.example")
it.snapshotsPath.set(File("/path/to/snapshots"))
it.debug.set(true)
}

val args = task.computeCommandLineArgs()

assertTrue("--log-level=debug" in args)
}

@Test
fun `with sentryProperties file SENTRY_PROPERTIES is set correctly`() {
val project = createProject()
val propertiesFile = project.file("dummy/folder/sentry.properties")
val task = createTestTask(project) { it.sentryProperties.set(propertiesFile) }

task.setSentryPropertiesEnv()

assertEquals(propertiesFile.absolutePath, task.environment["SENTRY_PROPERTIES"].toString())
}

@Test
fun `without sentryProperties file SENTRY_PROPERTIES is not set`() {
val task = createTestTask()

task.setSentryPropertiesEnv()

assertNull(task.environment["SENTRY_PROPERTIES"])
}

@Test
fun `with sentryOrganization adds --org`() {
val task =
createTestTask {
it.cliExecutable.set("sentry-cli")
it.sentryOrganization.set("dummy-org")
it.appId.set("com.example")
it.snapshotsPath.set(File("/path/to/snapshots"))
}

val args = task.computeCommandLineArgs()

assertTrue("--org" in args)
assertTrue("dummy-org" in args)
}

@Test
fun `with sentryProject adds --project`() {
val task =
createTestTask {
it.cliExecutable.set("sentry-cli")
it.sentryProject.set("dummy-proj")
it.appId.set("com.example")
it.snapshotsPath.set(File("/path/to/snapshots"))
}

val args = task.computeCommandLineArgs()

assertTrue("--project" in args)
assertTrue("dummy-proj" in args)
}

@Test
fun `with sentryUrl adds --url`() {
val task =
createTestTask {
it.cliExecutable.set("sentry-cli")
it.sentryUrl.set("https://some-host.sentry.io")
it.appId.set("com.example")
it.snapshotsPath.set(File("/path/to/snapshots"))
}

val args = task.computeCommandLineArgs()

assertTrue("--url" in args)
assertTrue("https://some-host.sentry.io" in args)
}

@Test
fun `the --url parameter is placed as the first argument`() {
val task =
createTestTask {
it.cliExecutable.set("sentry-cli")
it.sentryUrl.set("https://some-host.sentry.io")
it.appId.set("com.example")
it.snapshotsPath.set(File("/path/to/snapshots"))
}

val args = task.computeCommandLineArgs()

assertEquals(1, args.indexOf("--url"))
}

private fun createProject(): Project {
with(ProjectBuilder.builder().build()) {
plugins.apply("io.sentry.android.gradle")
return this
}
}

private fun createTestTask(
project: Project = createProject(),
block: (SentryUploadSnapshotsTask) -> Unit = {},
): SentryUploadSnapshotsTask =
project.tasks
.register("testUploadSnapshots", SentryUploadSnapshotsTask::class.java) { block(it) }
.get()
}
Loading