From 00d4e33b77cbb978bd4d04a40980847933c9366c Mon Sep 17 00:00:00 2001 From: Manoel Aranda Neto Date: Wed, 5 Nov 2025 17:47:36 +0100 Subject: [PATCH 01/11] chore: initial proguard support --- posthog-android/consumer-rules.pro | 4 ++++ .../com/posthog/android/PostHogAndroid.kt | 11 ++++++++++- .../com/posthog/android/sample/DoSomething.kt | 1 + .../java/com/posthog/android/sample/MyApp.kt | 5 ++++- posthog/src/main/java/com/posthog/PostHog.kt | 1 + .../main/java/com/posthog/PostHogConfig.kt | 19 +++++++++++++++++++ .../errortracking/ThrowableCoercer.kt | 6 ++++++ 7 files changed, 45 insertions(+), 2 deletions(-) diff --git a/posthog-android/consumer-rules.pro b/posthog-android/consumer-rules.pro index 3a854f20..1f5bb51f 100644 --- a/posthog-android/consumer-rules.pro +++ b/posthog-android/consumer-rules.pro @@ -97,4 +97,8 @@ # used in reflection to check if compose is available at runtime -keepnames class androidx.compose.ui.platform.AndroidComposeView +# Uncomment this to preserve the line number information for +# debugging stack traces. +-keepattributes SourceFile,LineNumberTable + ##---------------End: proguard configuration for okhttp3 ---------- \ No newline at end of file diff --git a/posthog-android/src/main/java/com/posthog/android/PostHogAndroid.kt b/posthog-android/src/main/java/com/posthog/android/PostHogAndroid.kt index 75567be1..e5c42491 100644 --- a/posthog-android/src/main/java/com/posthog/android/PostHogAndroid.kt +++ b/posthog-android/src/main/java/com/posthog/android/PostHogAndroid.kt @@ -16,6 +16,7 @@ import com.posthog.android.internal.PostHogLifecycleObserverIntegration import com.posthog.android.internal.PostHogSharedPreferences import com.posthog.android.internal.appContext import com.posthog.android.internal.getPackageInfo +import com.posthog.android.internal.versionCodeCompat import com.posthog.android.replay.PostHogReplayIntegration import com.posthog.android.replay.internal.PostHogLogCatIntegration import com.posthog.android.surveys.PostHogSurveysIntegration @@ -78,13 +79,16 @@ public class PostHogAndroid private constructor() { val packageInfo = getPackageInfo(context, config) val packageName = packageInfo?.packageName ?: "" + val versionName = packageInfo?.versionName ?: "" + val buildNumber = packageInfo?.versionCodeCompat() ?: 0L // only frames coming from the package name will be considered inApp by default if (packageName.isNotEmpty() && !packageName.startsWith("android.")) { config.errorTrackingConfig.inAppIncludes.add(packageName) } - config.context = config.context ?: PostHogAndroidContext(context, config) + val androidContext = config.context ?: PostHogAndroidContext(context, config) + config.context = androidContext val legacyPath = context.getDir("app_posthog-disk-queue", Context.MODE_PRIVATE) val path = File(context.cacheDir, "posthog-disk-queue") @@ -108,6 +112,11 @@ public class PostHogAndroid private constructor() { config.sdkVersion = BuildConfig.VERSION_NAME } + if (config.releaseIdentifier.isNullOrEmpty()) { + // consider build type and variants + config.releaseIdentifier = "$packageName@$versionName+$buildNumber" + } + val mainHandler = MainHandler() config.addIntegration(PostHogReplayIntegration(context, config, mainHandler)) config.addIntegration(PostHogLogCatIntegration(config)) diff --git a/posthog-samples/posthog-android-sample/src/main/java/com/posthog/android/sample/DoSomething.kt b/posthog-samples/posthog-android-sample/src/main/java/com/posthog/android/sample/DoSomething.kt index 5de3b95a..452cc18d 100644 --- a/posthog-samples/posthog-android-sample/src/main/java/com/posthog/android/sample/DoSomething.kt +++ b/posthog-samples/posthog-android-sample/src/main/java/com/posthog/android/sample/DoSomething.kt @@ -7,6 +7,7 @@ class DoSomething { try { throw MyCustomException("Something went wrong") } catch (e: Throwable) { + e.stackTraceToString() PostHog.captureException(e, mapOf("my-custom-error" to true)) } } diff --git a/posthog-samples/posthog-android-sample/src/main/java/com/posthog/android/sample/MyApp.kt b/posthog-samples/posthog-android-sample/src/main/java/com/posthog/android/sample/MyApp.kt index aa775e86..6b100e0f 100644 --- a/posthog-samples/posthog-android-sample/src/main/java/com/posthog/android/sample/MyApp.kt +++ b/posthog-samples/posthog-android-sample/src/main/java/com/posthog/android/sample/MyApp.kt @@ -41,8 +41,11 @@ class MyApp : Application() { sessionReplayConfig.maskAllImages = false sessionReplayConfig.captureLogcat = true sessionReplayConfig.screenshot = true - surveys = true + surveys = false errorTrackingConfig.autoCapture = true + if (!BuildConfig.DEBUG) { + releaseIdentifier = "${BuildConfig.APPLICATION_ID}@${BuildConfig.VERSION_NAME}+${BuildConfig.VERSION_CODE}" + } } PostHogAndroid.setup(this, config) } diff --git a/posthog/src/main/java/com/posthog/PostHog.kt b/posthog/src/main/java/com/posthog/PostHog.kt index 6020e7ed..b3356313 100644 --- a/posthog/src/main/java/com/posthog/PostHog.kt +++ b/posthog/src/main/java/com/posthog/PostHog.kt @@ -500,6 +500,7 @@ public class PostHog private constructor( throwableCoercer.fromThrowableToPostHogProperties( throwable, inAppIncludes = config?.errorTrackingConfig?.inAppIncludes ?: listOf(), + releaseIdentifier = config?.releaseIdentifier, ) properties?.let { diff --git a/posthog/src/main/java/com/posthog/PostHogConfig.kt b/posthog/src/main/java/com/posthog/PostHogConfig.kt index 8aed5a07..3eabee1d 100644 --- a/posthog/src/main/java/com/posthog/PostHogConfig.kt +++ b/posthog/src/main/java/com/posthog/PostHogConfig.kt @@ -203,6 +203,25 @@ public open class PostHogConfig( * Configuration for PostHog Error Tracking feature. */ public val errorTrackingConfig: PostHogErrorTrackingConfig = PostHogErrorTrackingConfig(), + + /** + * The release identifier used to identify the correct proguard mappings (mapping.txt) + * in order to make minified stack traces into human readable stack traces + * + * If not set, this will be automatically set to a composed version of your eg: + * com.posthog.mobile@1.0.0+1 + * Where: + * 'com.posthog.mobile' is the applicationId + * '1.0.0' is the versionName + * '1' is the versionCode + * + * It can also be a git sha, the hash of the mapping file, etc + * it should be the very same identifier (map-id) used to upload the mapping to the PostHog servers + * + * CLI command example: + * posthog-cli exp proguard upload --path "app/build/outputs/mapping/release/mapping.txt" --map-id "com.posthog.mobile@1.0.0+1" + */ + public var releaseIdentifier: String? = null, ) { @PostHogInternal public var logger: PostHogLogger = PostHogNoOpLogger() diff --git a/posthog/src/main/java/com/posthog/internal/errortracking/ThrowableCoercer.kt b/posthog/src/main/java/com/posthog/internal/errortracking/ThrowableCoercer.kt index 89224d05..ef139861 100644 --- a/posthog/src/main/java/com/posthog/internal/errortracking/ThrowableCoercer.kt +++ b/posthog/src/main/java/com/posthog/internal/errortracking/ThrowableCoercer.kt @@ -22,6 +22,7 @@ internal class ThrowableCoercer { fun fromThrowableToPostHogProperties( throwable: Throwable, inAppIncludes: List = listOf(), + releaseIdentifier: String? = null, ): MutableMap { val exceptions = mutableListOf>() val throwableList = mutableListOf() @@ -68,6 +69,11 @@ internal class ThrowableCoercer { myFrame["function"] = frame.methodName myFrame["platform"] = "java" + // add release identifier for symbolication + if (!releaseIdentifier.isNullOrEmpty()) { + myFrame["map_id"] = releaseIdentifier + } + if (frame.lineNumber >= 0) { myFrame["lineno"] = frame.lineNumber } From fb0d3a4218cd671fe8c78b3ad7fa270ca779dc68 Mon Sep 17 00:00:00 2001 From: Manoel Aranda Neto Date: Wed, 5 Nov 2025 17:49:20 +0100 Subject: [PATCH 02/11] fix --- .../src/main/java/com/posthog/android/PostHogAndroid.kt | 1 - .../src/main/java/com/posthog/android/sample/MyApp.kt | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/posthog-android/src/main/java/com/posthog/android/PostHogAndroid.kt b/posthog-android/src/main/java/com/posthog/android/PostHogAndroid.kt index e5c42491..86782963 100644 --- a/posthog-android/src/main/java/com/posthog/android/PostHogAndroid.kt +++ b/posthog-android/src/main/java/com/posthog/android/PostHogAndroid.kt @@ -113,7 +113,6 @@ public class PostHogAndroid private constructor() { } if (config.releaseIdentifier.isNullOrEmpty()) { - // consider build type and variants config.releaseIdentifier = "$packageName@$versionName+$buildNumber" } diff --git a/posthog-samples/posthog-android-sample/src/main/java/com/posthog/android/sample/MyApp.kt b/posthog-samples/posthog-android-sample/src/main/java/com/posthog/android/sample/MyApp.kt index 6b100e0f..76984923 100644 --- a/posthog-samples/posthog-android-sample/src/main/java/com/posthog/android/sample/MyApp.kt +++ b/posthog-samples/posthog-android-sample/src/main/java/com/posthog/android/sample/MyApp.kt @@ -44,6 +44,7 @@ class MyApp : Application() { surveys = false errorTrackingConfig.autoCapture = true if (!BuildConfig.DEBUG) { + // apps should consider build type and variants releaseIdentifier = "${BuildConfig.APPLICATION_ID}@${BuildConfig.VERSION_NAME}+${BuildConfig.VERSION_CODE}" } } From 162a3a34dedf7faa3ef33b9012fd01d8d48f788c Mon Sep 17 00:00:00 2001 From: Manoel Aranda Neto Date: Wed, 5 Nov 2025 17:51:34 +0100 Subject: [PATCH 03/11] add comment --- posthog/src/main/java/com/posthog/PostHogConfig.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/posthog/src/main/java/com/posthog/PostHogConfig.kt b/posthog/src/main/java/com/posthog/PostHogConfig.kt index 3eabee1d..7d5ec25e 100644 --- a/posthog/src/main/java/com/posthog/PostHogConfig.kt +++ b/posthog/src/main/java/com/posthog/PostHogConfig.kt @@ -208,7 +208,7 @@ public open class PostHogConfig( * The release identifier used to identify the correct proguard mappings (mapping.txt) * in order to make minified stack traces into human readable stack traces * - * If not set, this will be automatically set to a composed version of your eg: + * If not set, this will be automatically set to a composed version of your app eg: * com.posthog.mobile@1.0.0+1 * Where: * 'com.posthog.mobile' is the applicationId @@ -218,6 +218,8 @@ public open class PostHogConfig( * It can also be a git sha, the hash of the mapping file, etc * it should be the very same identifier (map-id) used to upload the mapping to the PostHog servers * + * Soon a Gradle plugin will be provided to do the injection of this identifier automatically + * * CLI command example: * posthog-cli exp proguard upload --path "app/build/outputs/mapping/release/mapping.txt" --map-id "com.posthog.mobile@1.0.0+1" */ From 67109f15c2da5b1ec9cba595c129d2febcea67ec Mon Sep 17 00:00:00 2001 From: Manoel Aranda Neto Date: Thu, 27 Nov 2025 11:12:07 +0100 Subject: [PATCH 04/11] remove --- .../src/main/java/com/posthog/android/sample/MyApp.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/posthog-samples/posthog-android-sample/src/main/java/com/posthog/android/sample/MyApp.kt b/posthog-samples/posthog-android-sample/src/main/java/com/posthog/android/sample/MyApp.kt index 76984923..0380a2d3 100644 --- a/posthog-samples/posthog-android-sample/src/main/java/com/posthog/android/sample/MyApp.kt +++ b/posthog-samples/posthog-android-sample/src/main/java/com/posthog/android/sample/MyApp.kt @@ -26,9 +26,9 @@ class MyApp : Application() { captureDeepLinks = false captureApplicationLifecycleEvents = false captureScreenViews = false - sessionReplay = true + sessionReplay = false preloadFeatureFlags = true - sendFeatureFlagEvent = true + sendFeatureFlagEvent = false onFeatureFlags = PostHogOnFeatureFlags { print("feature flags loaded") } addBeforeSend { event -> if (event.event == "test_event") { From 4ddff477e38b440a2b670f4106d6926afcfb6fd9 Mon Sep 17 00:00:00 2001 From: Manoel Aranda Neto Date: Thu, 27 Nov 2025 16:06:13 +0100 Subject: [PATCH 05/11] draft --- .../build.gradle.kts | 19 +++ .../settings.gradle.kts | 10 ++ .../posthog/android/DirectoryOutputTask.kt | 11 ++ ...jectPostHogMetaPropertiesIntoAssetsTask.kt | 88 ++++++++++++++ .../kotlin/com/posthog/android/OutputPaths.kt | 13 +++ .../android/PostHogAndroidGradlePlugin.kt | 88 ++++++++++++++ .../com/posthog/android/PostHogCliExecTask.kt | 34 ++++++ .../android/PostHogGenerateMapIdTask.kt | 71 ++++++++++++ .../posthog/android/PostHogTasksProvider.kt | 109 ++++++++++++++++++ .../PostHogUploadProguardMappingsTask.kt | 87 ++++++++++++++ .../android/PropertiesFileOutputTask.kt | 11 ++ .../kotlin/com/posthog/android/TasksUtils.kt | 81 +++++++++++++ .../main/kotlin/com/posthog/android/Utils.kt | 91 +++++++++++++++ .../posthog-android-sample/build.gradle.kts | 1 + .../main/java/com/posthog/PostHogConfig.kt | 1 - settings.gradle.kts | 3 + 16 files changed, 717 insertions(+), 1 deletion(-) create mode 100644 posthog-android-gradle-plugin/build.gradle.kts create mode 100644 posthog-android-gradle-plugin/settings.gradle.kts create mode 100644 posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/DirectoryOutputTask.kt create mode 100644 posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/InjectPostHogMetaPropertiesIntoAssetsTask.kt create mode 100644 posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/OutputPaths.kt create mode 100644 posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogAndroidGradlePlugin.kt create mode 100644 posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogCliExecTask.kt create mode 100644 posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogGenerateMapIdTask.kt create mode 100644 posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogTasksProvider.kt create mode 100644 posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogUploadProguardMappingsTask.kt create mode 100644 posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PropertiesFileOutputTask.kt create mode 100644 posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/TasksUtils.kt create mode 100644 posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/Utils.kt diff --git a/posthog-android-gradle-plugin/build.gradle.kts b/posthog-android-gradle-plugin/build.gradle.kts new file mode 100644 index 00000000..777fba43 --- /dev/null +++ b/posthog-android-gradle-plugin/build.gradle.kts @@ -0,0 +1,19 @@ +plugins { + `kotlin-dsl` + id("java-gradle-plugin") +} + +gradlePlugin { + plugins { + create("postHogAndroidPlugin") { + id = "com.posthog.android" + implementationClass = "com.posthog.android.PostHogAndroidGradlePlugin" + displayName = "PostHog Android Gradle Plugin" + } + } +} + +dependencies { + compileOnly(gradleApi()) + compileOnly("com.android.tools.build:gradle:8.0.2") +} diff --git a/posthog-android-gradle-plugin/settings.gradle.kts b/posthog-android-gradle-plugin/settings.gradle.kts new file mode 100644 index 00000000..1d8249a9 --- /dev/null +++ b/posthog-android-gradle-plugin/settings.gradle.kts @@ -0,0 +1,10 @@ +rootProject.name = "posthog-android-gradle-plugin" + +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} diff --git a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/DirectoryOutputTask.kt b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/DirectoryOutputTask.kt new file mode 100644 index 00000000..c62da014 --- /dev/null +++ b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/DirectoryOutputTask.kt @@ -0,0 +1,11 @@ +package com.posthog.android + +import org.gradle.api.DefaultTask +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.tasks.OutputDirectory +import org.gradle.work.DisableCachingByDefault + +@DisableCachingByDefault(because = "abstract task, should not be used directly") +abstract class DirectoryOutputTask : DefaultTask() { + @get:OutputDirectory abstract val output: DirectoryProperty +} diff --git a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/InjectPostHogMetaPropertiesIntoAssetsTask.kt b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/InjectPostHogMetaPropertiesIntoAssetsTask.kt new file mode 100644 index 00000000..6e26903f --- /dev/null +++ b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/InjectPostHogMetaPropertiesIntoAssetsTask.kt @@ -0,0 +1,88 @@ +package com.posthog.android + +import org.gradle.api.DefaultTask +import org.gradle.api.Project +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.RegularFile +import org.gradle.api.provider.Provider +import org.gradle.api.tasks.CacheableTask +import org.gradle.api.tasks.InputFiles +import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.PathSensitive +import org.gradle.api.tasks.PathSensitivity +import org.gradle.api.tasks.TaskAction +import org.gradle.api.tasks.TaskProvider +import java.io.File +import java.util.Properties + +@CacheableTask +abstract class InjectPostHogMetaPropertiesIntoAssetsTask : DefaultTask() { + @get:PathSensitive(PathSensitivity.RELATIVE) + @get:InputFiles + abstract val inputDir: DirectoryProperty + + @get:OutputDirectory + val outputDir: DirectoryProperty = project.objects.directoryProperty() + get() { + // AGP < 8.3 sets an output folder which contains the input folder + // input: app/intermediates/assets/release/mergeReleaseAssets + // output: app/intermediates/assets/release/ + // re-route output to a sub directory instead, + // as otherwise this breaks the gradle cache functionality + + @Suppress("SENSELESS_COMPARISON") + if (field == null || !field.isPresent) { + return field + } + + if (!field.get().asFile.name.equals(name)) { + field.set(File(field.get().asFile, name)) + } + + return field + } + + // we only care about file contents + @get:PathSensitive(PathSensitivity.NONE) + @get:InputFiles + abstract val inputPropertyFiles: ConfigurableFileCollection + + @TaskAction + fun taskAction() { + val input = inputDir.get().asFile + val output = outputDir.getAndDelete() + output.mkdirs() + + input.copyRecursively(output, overwrite = true) + + // merge props + val props = Properties() + inputPropertyFiles.forEach { inputFile -> + loadPropertiesMaybe(inputFile)?.let { props.putAll(it) } + } + + // write props + val propsFile = File(output, POSTHOG_DEBUG_META_PROPERTIES_OUTPUT) + propsFile.writer().use { props.store(it, "Generated by com.posthog.android") } + } + + companion object { + internal const val POSTHOG_DEBUG_META_PROPERTIES_OUTPUT = "posthog-meta.properties" + + fun register( + project: Project, + tasksGeneratingProperties: List>, + taskSuffix: String = "", + ): TaskProvider { + val inputFiles: List> = + tasksGeneratingProperties.mapNotNull { it.flatMap { task -> task.outputFile } } + return project.tasks.register( + "injectPostHogMetaPropertiesIntoAssets$taskSuffix", + InjectPostHogMetaPropertiesIntoAssetsTask::class.java, + ) { + inputPropertyFiles.setFrom(inputFiles) + } + } + } +} diff --git a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/OutputPaths.kt b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/OutputPaths.kt new file mode 100644 index 00000000..be76bceb --- /dev/null +++ b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/OutputPaths.kt @@ -0,0 +1,13 @@ +package com.posthog.android + +import org.gradle.api.Project + +internal const val ROOT_DIR = "intermediates/posthog" + +class OutputPaths(private val project: Project, variantName: String) { + private fun dir(path: String) = project.layout.buildDirectory.dir(path) + + private val variantDirectory = "$ROOT_DIR/$variantName" + + val proguardMapIdDir = dir("$variantDirectory/proguard-mapid") +} diff --git a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogAndroidGradlePlugin.kt b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogAndroidGradlePlugin.kt new file mode 100644 index 00000000..199cc6ef --- /dev/null +++ b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogAndroidGradlePlugin.kt @@ -0,0 +1,88 @@ +package com.posthog.android + +import com.android.build.api.variant.ApplicationAndroidComponentsExtension +import com.android.build.api.variant.ApplicationVariant +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.file.FileCollection +import org.gradle.api.provider.Provider +import org.gradle.api.tasks.TaskProvider + +class PostHogAndroidGradlePlugin : Plugin { + override fun apply(project: Project) { + if (!project.plugins.hasPlugin("com.android.application")) { + project.logger.warn( + """ + Using 'com.posthog.android' is only supported for the app module. + Please make sure that you apply the PostHog gradle plugin alongside 'com.android.application' on the _module_ level, and not on the root project level. + """ + .trimIndent(), + ) + } + + project.pluginManager.withPlugin("com.android.application") { + val androidComponentsExt = + project.extensions.getByType(ApplicationAndroidComponentsExtension::class.java) + + androidComponentsExt.onVariants { variant -> + if (!variant.isMinifyEnabled) { + return@onVariants + } + +// val tasksGeneratingProperties = mutableListOf>() + + // TODO: skip variants, skip autoUpload, release info, allow failure, debug mode + + val paths = OutputPaths(project, variant.name) + val generateMapIdTask = generateMapIdTask(project, variant, paths) + + variant.apply { + } + } + } + } + + private fun generateMapIdTask( + project: Project, + variant: ApplicationVariant, + paths: OutputPaths, + ): TaskProvider { + val generateMapIdTask = + PostHogGenerateMapIdTask.register( + project = project, + proguardMappingFile = variant.mappingFileProvider(project), + taskSuffix = variant.name.capitalizeUS(), + output = paths.proguardMapIdDir, + ) + + val uploadMapIdTask = + uploadMapIdTask( + project = project, + generateMapIdTask = generateMapIdTask, + variant = variant, + mappingFiles = variant.mappingFileProvider(project), + ) + + generateMapIdTask.hookWithMinifyTasks(project, variant.name) + + uploadMapIdTask.hookWithAssembleTasks(project, variant) + + return generateMapIdTask + } + + private fun uploadMapIdTask( + project: Project, + generateMapIdTask: Provider, + variant: ApplicationVariant, + mappingFiles: Provider, + ): TaskProvider { + val uploadMapIdTask = + PostHogUploadProguardMappingsTask.register( + project = project, + generateMapIdTask = generateMapIdTask, + mappingFiles = mappingFiles, + taskSuffix = variant.name.capitalizeUS(), + ) + return uploadMapIdTask + } +} diff --git a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogCliExecTask.kt b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogCliExecTask.kt new file mode 100644 index 00000000..5c03b3e5 --- /dev/null +++ b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogCliExecTask.kt @@ -0,0 +1,34 @@ +package com.posthog.android + +import org.apache.tools.ant.taskdefs.condition.Os +import org.gradle.api.tasks.Exec +import org.gradle.work.DisableCachingByDefault + +@DisableCachingByDefault(because = "abstract task, should not be used directly") +abstract class PostHogCliExecTask : Exec() { + override fun exec() { + computeCommandLineArgs().let { + commandLine(it) + logger.info("cli args: $it") + } + super.exec() + } + + abstract fun getArguments(args: MutableList) + + /** Computes the full list of arguments for the task */ + private fun computeCommandLineArgs(): List { + val args = mutableListOf() + if (Os.isFamily(Os.FAMILY_WINDOWS)) { + args.add(0, "cmd") + args.add(1, "/c") + } + + // TODO: CLI path config + args.add("posthog-cli") + + getArguments(args) + + return args + } +} diff --git a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogGenerateMapIdTask.kt b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogGenerateMapIdTask.kt new file mode 100644 index 00000000..379d2653 --- /dev/null +++ b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogGenerateMapIdTask.kt @@ -0,0 +1,71 @@ +package com.posthog.android + +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.Internal +import org.gradle.api.tasks.TaskAction +import org.gradle.api.tasks.TaskProvider +import org.gradle.work.DisableCachingByDefault +import java.util.UUID + +@DisableCachingByDefault +abstract class PostHogGenerateMapIdTask : PropertiesFileOutputTask() { + init { + description = + "Generates a unique build ID to be used when uploading the PostHog mapping file" + } + + @get:Internal + override val outputFile: Provider + get() = output.file(POSTHOG_MAP_ID_OUTPUT) + + @get:Internal abstract val proguardMappingFiles: ConfigurableFileCollection + + @TaskAction + fun generateProperties() { + val outputDir = output.get().asFile + outputDir.mkdirs() + + val proguardMappingFileHash = + proguardMappingFiles.files.joinToString { if (it.isFile) it.contentHash() else STATIC_HASH } + val uuid = UUID.nameUUIDFromBytes(proguardMappingFileHash.toByteArray()) + outputFile.get().asFile.writer().use { writer -> + writer.appendLine("$POSTHOG_PROGUARD_MAPPING_MAP_ID_PROPERTY=$uuid") + } + + logger.info("PostHogGenerateMapIdTask - outputFile: $outputFile, uuid: $uuid") + } + + companion object { + internal const val STATIC_HASH = "" + internal const val POSTHOG_MAP_ID_OUTPUT = "posthog-proguard-map-id.properties" + const val POSTHOG_PROGUARD_MAPPING_MAP_ID_PROPERTY = "io.posthog.proguard.mapid" + + fun register( + project: Project, + output: Provider? = null, + proguardMappingFile: Provider?, + taskSuffix: String = "", + ): TaskProvider { + val generateMapIdTask = + project.tasks.register( + "postHogGenerateMapIdTask$taskSuffix", + PostHogGenerateMapIdTask::class.java, + ) { + output?.let { + this.output.set(it) + } + proguardMappingFile?.let { + this.proguardMappingFiles.from(proguardMappingFile) + } + this.outputs.upToDateWhen { false } + } + + return generateMapIdTask + } + } +} diff --git a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogTasksProvider.kt b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogTasksProvider.kt new file mode 100644 index 00000000..0dadc888 --- /dev/null +++ b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogTasksProvider.kt @@ -0,0 +1,109 @@ +package com.posthog.android + +import com.android.build.api.variant.ApplicationVariant +import com.android.build.api.variant.impl.VariantImpl +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.UnknownTaskException +import org.gradle.api.tasks.TaskProvider + +internal object PostHogTasksProvider { + /** + * Returns the minify task for the given project and variant. It could be either ProGuard, R8 or + * DexGuard. + * + * @return the task or null otherwise + */ + @JvmStatic + fun getMinifyTask( + project: Project, + variantName: String, + ): TaskProvider? { + val tasks = + listOf( + "minify${variantName.capitalized}WithR8", + "minify${variantName.capitalized}WithProguard", + ) + return project.findTask(tasks) + } + + /** + * Returns the pre bundle task for the given project and variant. + * + * @return the task or null otherwise + */ + @JvmStatic + fun getPreBundleTask( + project: Project, + variantName: String, + ): TaskProvider? = project.findTask(listOf("build${variantName.capitalized}PreBundle")) + + /** + * Returns the pre bundle task for the given project and variant. + * + * @return the task or null otherwise + */ + @JvmStatic + fun getBundleTask( + project: Project, + variantName: String, + ): TaskProvider? = project.findTask(listOf("bundle${variantName.capitalized}")) + + /** + * Returns the package bundle task (App Bundle only) + * + * @return the package task or null if not found + */ + @JvmStatic + fun getPackageBundleTask( + project: Project, + variantName: String, + ): TaskProvider? = + // for APK it uses getPackageProvider + project.findTask(listOf("package${variantName.capitalized}Bundle")) + + /** + * Returns the assemble task provider + * + * @return the provider if found or null otherwise + */ + @JvmStatic + fun getAssembleTaskProvider( + project: Project, + variant: ApplicationVariant, + ): TaskProvider? = variant.assembleProvider() ?: project.findTask(listOf("assemble${variant.name.capitalized}")) + + /** + * Returns the install task provider + * + * @return the provider if found or null otherwise + */ + @JvmStatic + fun getInstallTaskProvider( + project: Project, + variant: ApplicationVariant, + ): TaskProvider? = variant.installProvider() ?: project.findTask(listOf("install${variant.name.capitalized}")) + + /** @return the first task found in the list or null */ + private fun Project.findTask(taskName: List): TaskProvider? = + taskName + .mapNotNull { + try { + project.tasks.named(it) + } catch (e: UnknownTaskException) { + null + } + } + .firstOrNull() + + internal val String.capitalized: String + get() = this.capitalizeUS() +} + +internal fun ApplicationVariant.assembleProvider(): TaskProvider? { + return (this as? VariantImpl<*>)?.taskContainer?.assembleTask +} + +internal fun ApplicationVariant.installProvider(): TaskProvider? { + return (this as? VariantImpl<*>)?.taskContainer?.installTask +} diff --git a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogUploadProguardMappingsTask.kt b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogUploadProguardMappingsTask.kt new file mode 100644 index 00000000..5cc7973c --- /dev/null +++ b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogUploadProguardMappingsTask.kt @@ -0,0 +1,87 @@ +package com.posthog.android + +import org.gradle.api.Project +import org.gradle.api.file.FileCollection +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.provider.Provider +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.InputFiles +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 PostHogUploadProguardMappingsTask : PostHogCliExecTask() { + init { + description = "Uploads the proguard mappings file to PostHog" + + // 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:InputFile + @get:PathSensitive(PathSensitivity.NONE) + abstract val mapIdFile: RegularFileProperty + + @get:InputFiles + @get:PathSensitive(PathSensitivity.RELATIVE) + abstract var mappingsFiles: Provider + + override fun exec() { + if (!mappingsFiles.isPresent || mappingsFiles.get().isEmpty) { + error("[PostHog] Mapping files are missing!") + } + super.exec() + } + + override fun getArguments(args: MutableList) { + val uuid = readMapIdFromFile(mapIdFile.get().asFile) + val firstExistingFile = mappingsFiles.get().files.firstOrNull { it.exists() } + + val mappingFile = + if (firstExistingFile == null) { + logger.warn( + "None of the provided mappingFiles was found on disk. " + + "Upload is most likely going to be skipped", + ) + mappingsFiles.get().files.first() + } else { + firstExistingFile + } + +// args.add("posthog-cli") + args.add("exp proguard upload") + args.add("--map-id") + args.add(uuid) + args.add("--path") + args.add(mappingFile.toString()) + } + + companion object { + fun register( + project: Project, + generateMapIdTask: Provider, + mappingFiles: Provider, + taskSuffix: String = "", + ): TaskProvider { + val uploadPostHogProguardMappingsTask = + project.tasks.register( + "uploadPostHogProguardMappings$taskSuffix", + PostHogUploadProguardMappingsTask::class.java, + ) { + dependsOn(generateMapIdTask) + workingDir(project.rootDir) + this.mapIdFile.set(generateMapIdTask.flatMap { it.outputFile }) + this.mappingsFiles = mappingFiles + } + return uploadPostHogProguardMappingsTask + } + } +} diff --git a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PropertiesFileOutputTask.kt b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PropertiesFileOutputTask.kt new file mode 100644 index 00000000..f493d35b --- /dev/null +++ b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PropertiesFileOutputTask.kt @@ -0,0 +1,11 @@ +package com.posthog.android + +import org.gradle.api.file.RegularFile +import org.gradle.api.provider.Provider +import org.gradle.api.tasks.Internal +import org.gradle.work.DisableCachingByDefault + +@DisableCachingByDefault(because = "abstract task, should not be used directly") +abstract class PropertiesFileOutputTask : DirectoryOutputTask() { + @get:Internal abstract val outputFile: Provider +} diff --git a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/TasksUtils.kt b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/TasksUtils.kt new file mode 100644 index 00000000..fdaa026a --- /dev/null +++ b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/TasksUtils.kt @@ -0,0 +1,81 @@ +package com.posthog.android + +import com.android.build.api.artifact.SingleArtifact +import com.android.build.api.variant.ApplicationVariant +import com.posthog.android.PostHogTasksProvider.getAssembleTaskProvider +import com.posthog.android.PostHogTasksProvider.getBundleTask +import com.posthog.android.PostHogTasksProvider.getInstallTaskProvider +import com.posthog.android.PostHogTasksProvider.getMinifyTask +import com.posthog.android.PostHogTasksProvider.getPackageBundleTask +import com.posthog.android.PostHogTasksProvider.getPreBundleTask +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.file.FileCollection +import org.gradle.api.provider.Provider +import org.gradle.api.tasks.TaskProvider + +fun TaskProvider.hookWithMinifyTasks( + project: Project, + variantName: String, +) { + // we need to wait for project evaluation to have all tasks available, otherwise the new + // AndroidComponentsExtension is configured too early to look up for the tasks + project.afterEvaluate { + val minifyTask = getMinifyTask(project, variantName) + + // we just hack ourselves into the Proguard/R8/DexGuard task's doLast. + minifyTask?.configure { + finalizedBy(this@hookWithMinifyTasks) + } + } +} + +fun TaskProvider.hookWithPackageTasks( + project: Project, + variant: ApplicationVariant, +) { + val variantName = variant.name + val preBundleTaskProvider = + withLogging(project.logger, "preBundleTask") { getPreBundleTask(project, variantName) } + val packageBundleTaskProvider = + withLogging(project.logger, "packageBundleTask") { getPackageBundleTask(project, variantName) } + + // To include proguard uuid file into aab, run before bundle task. + preBundleTaskProvider?.configure { + dependsOn(this@hookWithPackageTasks) + } + // The package task will only be executed if the generateUuidTask has already been executed. + // getPackageProvider(variant)?.configure { task -> task.dependsOn(this) } + + // App bundle has different package task + packageBundleTaskProvider?.configure { + dependsOn(this@hookWithPackageTasks) + } +} + +fun TaskProvider.hookWithAssembleTasks( + project: Project, + variant: ApplicationVariant, +) { + // we need to wait for project evaluation to have all tasks available, otherwise the new + // AndroidComponentsExtension is configured too early to look up for the tasks + project.afterEvaluate { + val bundleTask = + withLogging(project.logger, "bundleTask") { getBundleTask(project, variant.name) } + getAssembleTaskProvider(project, variant)?.configure { + finalizedBy(this@hookWithAssembleTasks) + } + getInstallTaskProvider(project, variant)?.configure { + finalizedBy(this@hookWithAssembleTasks) + } + // if its a bundle aab, assemble might not be executed, so we hook into bundle task + bundleTask?.configure { + finalizedBy(this@hookWithAssembleTasks) + } + } +} + +fun ApplicationVariant.mappingFileProvider(project: Project): Provider = + project.provider { + project.files(artifacts.get(SingleArtifact.OBFUSCATION_MAPPING_FILE)) + } diff --git a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/Utils.kt b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/Utils.kt new file mode 100644 index 00000000..71c4ea1a --- /dev/null +++ b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/Utils.kt @@ -0,0 +1,91 @@ +package com.posthog.android + +import com.posthog.android.PostHogGenerateMapIdTask.Companion.POSTHOG_PROGUARD_MAPPING_MAP_ID_PROPERTY +import org.gradle.api.Task +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.logging.Logger +import org.gradle.api.tasks.TaskProvider +import java.io.File +import java.security.DigestInputStream +import java.security.MessageDigest +import java.util.Locale +import java.util.Properties + +internal fun ByteArray.toHex(): String { + val result = CharArray(size * 2) { ' ' } + var i = 0 + forEach { + val n = it.toInt() + result[i++] = Character.forDigit(n shr 4 and 0xF, 16) + result[i++] = Character.forDigit(n and 0xF, 16) + } + return String(result) +} + +/** Returns md5/sha256 hash of the file contents */ +fun File.contentHash(): String { + // first try to read hash generated by R8, which is located in the first 20 lines of mapping + // lines are lazily iterated over, so even on huge files performance should not be a problem + var hash: String? = null + var index = 0 + bufferedReader().lines().use { lines -> + for (line in lines) { + hash = line.substringAfter("# pg_map_hash: SHA-256 ", missingDelimiterValue = "") + if (++index > 20 || !hash.isNullOrBlank()) { + break + } + } + } + if (!hash.isNullOrBlank()) { + return hash!! + } + + // otherwise fall back to calculating hash ourselves + val md = MessageDigest.getInstance("MD5") + DigestInputStream(inputStream(), md).buffered().readAllBytes() + return md.digest().toHex() +} + +fun String.capitalizeUS() = + if (isEmpty()) { + "" + } else { + substring(0, 1).uppercase(Locale.US) + substring(1) + } + +internal fun loadProperties(file: File): Properties { + check(file.exists()) { "${file.name} properties file is missing" } + + return Properties().also { properties -> file.inputStream().use { properties.load(it) } } +} + +fun loadPropertiesMaybe(file: File): Properties? { + if (!file.exists()) { + return null + } + + return loadProperties(file) +} + +fun readMapIdFromFile(file: File): String { + val props = loadProperties(file) + val uuid = props.getProperty(POSTHOG_PROGUARD_MAPPING_MAP_ID_PROPERTY) + check(uuid != null) { "$POSTHOG_PROGUARD_MAPPING_MAP_ID_PROPERTY property is missing" } + return uuid +} + +fun withLogging( + logger: Logger, + varName: String, + initializer: () -> TaskProvider?, +) = initializer().also { logger.info("$varName is ${it?.name}") } + +fun DirectoryProperty.getAndDelete(): File { + val file = get().asFile + if (file.isDirectory) { + file.deleteRecursively() + } else { + file.delete() + } + return file +} diff --git a/posthog-samples/posthog-android-sample/build.gradle.kts b/posthog-samples/posthog-android-sample/build.gradle.kts index 94a19e87..0282e209 100644 --- a/posthog-samples/posthog-android-sample/build.gradle.kts +++ b/posthog-samples/posthog-android-sample/build.gradle.kts @@ -1,6 +1,7 @@ plugins { id("com.android.application") kotlin("android") + id("com.posthog.android") } android { diff --git a/posthog/src/main/java/com/posthog/PostHogConfig.kt b/posthog/src/main/java/com/posthog/PostHogConfig.kt index 20995a62..1c6a1b69 100644 --- a/posthog/src/main/java/com/posthog/PostHogConfig.kt +++ b/posthog/src/main/java/com/posthog/PostHogConfig.kt @@ -239,7 +239,6 @@ public open class PostHogConfig( * Configuration for PostHog Error Tracking feature. */ public val errorTrackingConfig: PostHogErrorTrackingConfig = PostHogErrorTrackingConfig(), - /** * The release identifier used to identify the correct proguard mappings (mapping.txt) * in order to make minified stack traces into human readable stack traces diff --git a/settings.gradle.kts b/settings.gradle.kts index 8a7128a6..6f5864ea 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -23,3 +23,6 @@ include(":posthog-server") include(":posthog-samples:posthog-android-sample") include(":posthog-samples:posthog-java-sample") include(":posthog-samples:posthog-spring-sample") + +// Include the plugin as a composite build +includeBuild("posthog-android-gradle-plugin") From 75245fbaba7af4aa777eef197d94c62e69048313 Mon Sep 17 00:00:00 2001 From: Manoel Aranda Neto Date: Fri, 28 Nov 2025 09:47:26 +0100 Subject: [PATCH 06/11] fix --- ...jectPostHogMetaPropertiesIntoAssetsTask.kt | 2 +- .../android/PostHogAndroidGradlePlugin.kt | 33 ++++++++++++++- .../posthog/android/PostHogTasksProvider.kt | 40 ++++--------------- .../PostHogUploadProguardMappingsTask.kt | 1 - .../kotlin/com/posthog/android/TasksUtils.kt | 25 ------------ 5 files changed, 40 insertions(+), 61 deletions(-) diff --git a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/InjectPostHogMetaPropertiesIntoAssetsTask.kt b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/InjectPostHogMetaPropertiesIntoAssetsTask.kt index 6e26903f..119decf5 100644 --- a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/InjectPostHogMetaPropertiesIntoAssetsTask.kt +++ b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/InjectPostHogMetaPropertiesIntoAssetsTask.kt @@ -76,7 +76,7 @@ abstract class InjectPostHogMetaPropertiesIntoAssetsTask : DefaultTask() { taskSuffix: String = "", ): TaskProvider { val inputFiles: List> = - tasksGeneratingProperties.mapNotNull { it.flatMap { task -> task.outputFile } } + tasksGeneratingProperties.map { it.flatMap { task -> task.outputFile } } return project.tasks.register( "injectPostHogMetaPropertiesIntoAssets$taskSuffix", InjectPostHogMetaPropertiesIntoAssetsTask::class.java, diff --git a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogAndroidGradlePlugin.kt b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogAndroidGradlePlugin.kt index 199cc6ef..fc2502a5 100644 --- a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogAndroidGradlePlugin.kt +++ b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogAndroidGradlePlugin.kt @@ -1,9 +1,12 @@ package com.posthog.android +import com.android.build.api.artifact.SingleArtifact import com.android.build.api.variant.ApplicationAndroidComponentsExtension import com.android.build.api.variant.ApplicationVariant import org.gradle.api.Plugin import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.FileCollection import org.gradle.api.provider.Provider import org.gradle.api.tasks.TaskProvider @@ -29,19 +32,47 @@ class PostHogAndroidGradlePlugin : Plugin { return@onVariants } -// val tasksGeneratingProperties = mutableListOf>() + val tasksGeneratingProperties = mutableListOf>() // TODO: skip variants, skip autoUpload, release info, allow failure, debug mode val paths = OutputPaths(project, variant.name) val generateMapIdTask = generateMapIdTask(project, variant, paths) + tasksGeneratingProperties.add(generateMapIdTask) variant.apply { + val injectAssetsTask = + InjectPostHogMetaPropertiesIntoAssetsTask.register( + project = project, + tasksGeneratingProperties = tasksGeneratingProperties, + taskSuffix = variant.name, + ) + + assetsWiredWithDirectories( + variant = variant, + task = injectAssetsTask, + inputDir = InjectPostHogMetaPropertiesIntoAssetsTask::inputDir, + outputDir = InjectPostHogMetaPropertiesIntoAssetsTask::outputDir, + ) + + // TODO: flutter doesn't use the transform API, and manually wires up task dependencies } } } } + private fun assetsWiredWithDirectories( + variant: ApplicationVariant, + task: TaskProvider, + inputDir: (T) -> DirectoryProperty, + outputDir: (T) -> DirectoryProperty, + ) { + variant.artifacts + .use(task) + .wiredWithDirectories(inputDir, outputDir) + .toTransform(SingleArtifact.ASSETS) + } + private fun generateMapIdTask( project: Project, variant: ApplicationVariant, diff --git a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogTasksProvider.kt b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogTasksProvider.kt index 0dadc888..70d0e672 100644 --- a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogTasksProvider.kt +++ b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogTasksProvider.kt @@ -27,17 +27,6 @@ internal object PostHogTasksProvider { return project.findTask(tasks) } - /** - * Returns the pre bundle task for the given project and variant. - * - * @return the task or null otherwise - */ - @JvmStatic - fun getPreBundleTask( - project: Project, - variantName: String, - ): TaskProvider? = project.findTask(listOf("build${variantName.capitalized}PreBundle")) - /** * Returns the pre bundle task for the given project and variant. * @@ -49,19 +38,6 @@ internal object PostHogTasksProvider { variantName: String, ): TaskProvider? = project.findTask(listOf("bundle${variantName.capitalized}")) - /** - * Returns the package bundle task (App Bundle only) - * - * @return the package task or null if not found - */ - @JvmStatic - fun getPackageBundleTask( - project: Project, - variantName: String, - ): TaskProvider? = - // for APK it uses getPackageProvider - project.findTask(listOf("package${variantName.capitalized}Bundle")) - /** * Returns the assemble task provider * @@ -86,17 +62,15 @@ internal object PostHogTasksProvider { /** @return the first task found in the list or null */ private fun Project.findTask(taskName: List): TaskProvider? = - taskName - .mapNotNull { - try { - project.tasks.named(it) - } catch (e: UnknownTaskException) { - null - } + taskName.firstNotNullOfOrNull { + try { + project.tasks.named(it) + } catch (e: UnknownTaskException) { + null } - .firstOrNull() + } - internal val String.capitalized: String + private val String.capitalized: String get() = this.capitalizeUS() } diff --git a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogUploadProguardMappingsTask.kt b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogUploadProguardMappingsTask.kt index 5cc7973c..8717c14b 100644 --- a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogUploadProguardMappingsTask.kt +++ b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogUploadProguardMappingsTask.kt @@ -56,7 +56,6 @@ abstract class PostHogUploadProguardMappingsTask : PostHogCliExecTask() { firstExistingFile } -// args.add("posthog-cli") args.add("exp proguard upload") args.add("--map-id") args.add(uuid) diff --git a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/TasksUtils.kt b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/TasksUtils.kt index fdaa026a..24f4ed59 100644 --- a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/TasksUtils.kt +++ b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/TasksUtils.kt @@ -6,8 +6,6 @@ import com.posthog.android.PostHogTasksProvider.getAssembleTaskProvider import com.posthog.android.PostHogTasksProvider.getBundleTask import com.posthog.android.PostHogTasksProvider.getInstallTaskProvider import com.posthog.android.PostHogTasksProvider.getMinifyTask -import com.posthog.android.PostHogTasksProvider.getPackageBundleTask -import com.posthog.android.PostHogTasksProvider.getPreBundleTask import org.gradle.api.Project import org.gradle.api.Task import org.gradle.api.file.FileCollection @@ -30,29 +28,6 @@ fun TaskProvider.hookWithMinifyTasks( } } -fun TaskProvider.hookWithPackageTasks( - project: Project, - variant: ApplicationVariant, -) { - val variantName = variant.name - val preBundleTaskProvider = - withLogging(project.logger, "preBundleTask") { getPreBundleTask(project, variantName) } - val packageBundleTaskProvider = - withLogging(project.logger, "packageBundleTask") { getPackageBundleTask(project, variantName) } - - // To include proguard uuid file into aab, run before bundle task. - preBundleTaskProvider?.configure { - dependsOn(this@hookWithPackageTasks) - } - // The package task will only be executed if the generateUuidTask has already been executed. - // getPackageProvider(variant)?.configure { task -> task.dependsOn(this) } - - // App bundle has different package task - packageBundleTaskProvider?.configure { - dependsOn(this@hookWithPackageTasks) - } -} - fun TaskProvider.hookWithAssembleTasks( project: Project, variant: ApplicationVariant, From 92559853f9fd482a8bd90e539ec81d7276c8354f Mon Sep 17 00:00:00 2001 From: Manoel Aranda Neto Date: Fri, 28 Nov 2025 11:08:01 +0100 Subject: [PATCH 07/11] fix --- .../posthog/android/PostHogUploadProguardMappingsTask.kt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogUploadProguardMappingsTask.kt b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogUploadProguardMappingsTask.kt index 8717c14b..cc57e7c9 100644 --- a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogUploadProguardMappingsTask.kt +++ b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogUploadProguardMappingsTask.kt @@ -56,11 +56,13 @@ abstract class PostHogUploadProguardMappingsTask : PostHogCliExecTask() { firstExistingFile } - args.add("exp proguard upload") - args.add("--map-id") - args.add(uuid) + args.add("exp") + args.add("proguard") + args.add("upload") args.add("--path") args.add(mappingFile.toString()) + args.add("--map-id") + args.add(uuid) } companion object { From bd1373f1b02ffb849572aa5d953b223c142b0e0f Mon Sep 17 00:00:00 2001 From: Manoel Aranda Neto Date: Fri, 28 Nov 2025 12:07:14 +0100 Subject: [PATCH 08/11] fix --- ...jectPostHogMetaPropertiesIntoAssetsTask.kt | 4 +- .../com/posthog/android/PostHogAndroid.kt | 7 +- .../internal/PostHogMetaPropertiesApplier.kt | 65 +++++++++++++++++++ .../java/com/posthog/android/sample/MyApp.kt | 4 -- posthog/api/posthog.api | 10 +-- 5 files changed, 77 insertions(+), 13 deletions(-) create mode 100644 posthog-android/src/main/java/com/posthog/android/internal/PostHogMetaPropertiesApplier.kt diff --git a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/InjectPostHogMetaPropertiesIntoAssetsTask.kt b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/InjectPostHogMetaPropertiesIntoAssetsTask.kt index 119decf5..5529c5be 100644 --- a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/InjectPostHogMetaPropertiesIntoAssetsTask.kt +++ b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/InjectPostHogMetaPropertiesIntoAssetsTask.kt @@ -63,12 +63,12 @@ abstract class InjectPostHogMetaPropertiesIntoAssetsTask : DefaultTask() { } // write props - val propsFile = File(output, POSTHOG_DEBUG_META_PROPERTIES_OUTPUT) + val propsFile = File(output, POSTHOG_META_PROPERTIES_OUTPUT) propsFile.writer().use { props.store(it, "Generated by com.posthog.android") } } companion object { - internal const val POSTHOG_DEBUG_META_PROPERTIES_OUTPUT = "posthog-meta.properties" + internal const val POSTHOG_META_PROPERTIES_OUTPUT = "posthog-meta.properties" fun register( project: Project, diff --git a/posthog-android/src/main/java/com/posthog/android/PostHogAndroid.kt b/posthog-android/src/main/java/com/posthog/android/PostHogAndroid.kt index 86782963..3d82a65c 100644 --- a/posthog-android/src/main/java/com/posthog/android/PostHogAndroid.kt +++ b/posthog-android/src/main/java/com/posthog/android/PostHogAndroid.kt @@ -13,6 +13,7 @@ import com.posthog.android.internal.PostHogAndroidLogger import com.posthog.android.internal.PostHogAndroidNetworkStatus import com.posthog.android.internal.PostHogAppInstallIntegration import com.posthog.android.internal.PostHogLifecycleObserverIntegration +import com.posthog.android.internal.PostHogMetaPropertiesApplier import com.posthog.android.internal.PostHogSharedPreferences import com.posthog.android.internal.appContext import com.posthog.android.internal.getPackageInfo @@ -112,9 +113,9 @@ public class PostHogAndroid private constructor() { config.sdkVersion = BuildConfig.VERSION_NAME } - if (config.releaseIdentifier.isNullOrEmpty()) { - config.releaseIdentifier = "$packageName@$versionName+$buildNumber" - } + val releaseIdentifierFallback = "$packageName@$versionName+$buildNumber" + val metaPropertiesApplier = PostHogMetaPropertiesApplier() + metaPropertiesApplier.applyToConfig(context, config, releaseIdentifierFallback) val mainHandler = MainHandler() config.addIntegration(PostHogReplayIntegration(context, config, mainHandler)) diff --git a/posthog-android/src/main/java/com/posthog/android/internal/PostHogMetaPropertiesApplier.kt b/posthog-android/src/main/java/com/posthog/android/internal/PostHogMetaPropertiesApplier.kt new file mode 100644 index 00000000..9527b85d --- /dev/null +++ b/posthog-android/src/main/java/com/posthog/android/internal/PostHogMetaPropertiesApplier.kt @@ -0,0 +1,65 @@ +package com.posthog.android.internal + +import android.content.Context +import com.posthog.android.PostHogAndroidConfig +import java.io.BufferedInputStream +import java.io.FileNotFoundException +import java.util.Properties + +internal class PostHogMetaPropertiesApplier() { + private fun loadMetaProperties( + context: Context, + config: PostHogAndroidConfig, + ): List? { + val assets = context.assets + + // one may have thousands of asset files and looking up this list might slow down the SDK init. + // quite a bit, for this reason, we try to open the file directly and take care of errors + // like FileNotFoundException + try { + BufferedInputStream(assets.open(POSTHOG_META_PROPERTIES_OUTPUT)).use { `is` -> + val properties = Properties() + properties.load(`is`) + return listOf(properties) + } + } catch (e: FileNotFoundException) { + // ignore + } catch (e: Throwable) { + config.logger.log("Failed reading the meta properties: $e.") + } + + return null + } + + fun applyToConfig( + context: Context, + config: PostHogAndroidConfig, + releaseIdentifierFallback: String, + ) { + val metaProperties = loadMetaProperties(context, config) + + // if releaseIdentifier is already set, we don't need to do anything + if (!config.releaseIdentifier.isNullOrEmpty() || metaProperties.isNullOrEmpty()) { + config.logger.log("releaseIdentifier not found, using fallback: $releaseIdentifierFallback") + config.releaseIdentifier = releaseIdentifierFallback + return + } + + for (property in metaProperties) { + val uuid = property.getProperty(POSTHOG_PROGUARD_MAPPING_MAP_ID_PROPERTY) + + if (uuid.isNullOrEmpty()) { + continue + } + + config.logger.log("releaseIdentifier found: $uuid") + config.releaseIdentifier = uuid + break + } + } + + companion object { + private const val POSTHOG_PROGUARD_MAPPING_MAP_ID_PROPERTY = "io.posthog.proguard.mapid" + private const val POSTHOG_META_PROPERTIES_OUTPUT = "posthog-meta.properties" + } +} diff --git a/posthog-samples/posthog-android-sample/src/main/java/com/posthog/android/sample/MyApp.kt b/posthog-samples/posthog-android-sample/src/main/java/com/posthog/android/sample/MyApp.kt index 0380a2d3..ddff28ab 100644 --- a/posthog-samples/posthog-android-sample/src/main/java/com/posthog/android/sample/MyApp.kt +++ b/posthog-samples/posthog-android-sample/src/main/java/com/posthog/android/sample/MyApp.kt @@ -43,10 +43,6 @@ class MyApp : Application() { sessionReplayConfig.screenshot = true surveys = false errorTrackingConfig.autoCapture = true - if (!BuildConfig.DEBUG) { - // apps should consider build type and variants - releaseIdentifier = "${BuildConfig.APPLICATION_ID}@${BuildConfig.VERSION_NAME}+${BuildConfig.VERSION_CODE}" - } } PostHogAndroid.setup(this, config) } diff --git a/posthog/api/posthog.api b/posthog/api/posthog.api index 21e728ba..e5d9bdcd 100644 --- a/posthog/api/posthog.api +++ b/posthog/api/posthog.api @@ -94,8 +94,8 @@ public class com/posthog/PostHogConfig { public static final field DEFAULT_HOST Ljava/lang/String; public static final field DEFAULT_US_ASSETS_HOST Ljava/lang/String; public static final field DEFAULT_US_HOST Ljava/lang/String; - public fun (Ljava/lang/String;Ljava/lang/String;ZZZIZLjava/util/List;ZZIIIILcom/posthog/PostHogEncryption;Lcom/posthog/PostHogOnFeatureFlags;ZLcom/posthog/PostHogPropertiesSanitizer;Lkotlin/jvm/functions/Function1;ZLcom/posthog/PersonProfiles;ZLjava/net/Proxy;Lcom/posthog/surveys/PostHogSurveysConfig;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function5;Lcom/posthog/errortracking/PostHogErrorTrackingConfig;)V - public synthetic fun (Ljava/lang/String;Ljava/lang/String;ZZZIZLjava/util/List;ZZIIIILcom/posthog/PostHogEncryption;Lcom/posthog/PostHogOnFeatureFlags;ZLcom/posthog/PostHogPropertiesSanitizer;Lkotlin/jvm/functions/Function1;ZLcom/posthog/PersonProfiles;ZLjava/net/Proxy;Lcom/posthog/surveys/PostHogSurveysConfig;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function5;Lcom/posthog/errortracking/PostHogErrorTrackingConfig;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/lang/String;Ljava/lang/String;ZZZIZLjava/util/List;ZZIIIILcom/posthog/PostHogEncryption;Lcom/posthog/PostHogOnFeatureFlags;ZLcom/posthog/PostHogPropertiesSanitizer;Lkotlin/jvm/functions/Function1;ZLcom/posthog/PersonProfiles;ZLjava/net/Proxy;Lcom/posthog/surveys/PostHogSurveysConfig;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function5;Lcom/posthog/errortracking/PostHogErrorTrackingConfig;Ljava/lang/String;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;ZZZIZLjava/util/List;ZZIIIILcom/posthog/PostHogEncryption;Lcom/posthog/PostHogOnFeatureFlags;ZLcom/posthog/PostHogPropertiesSanitizer;Lkotlin/jvm/functions/Function1;ZLcom/posthog/PersonProfiles;ZLjava/net/Proxy;Lcom/posthog/surveys/PostHogSurveysConfig;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function5;Lcom/posthog/errortracking/PostHogErrorTrackingConfig;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun addBeforeSend (Lcom/posthog/PostHogBeforeSend;)V public final fun addIntegration (Lcom/posthog/PostHogIntegration;)V public final fun getApiKey ()Ljava/lang/String; @@ -125,6 +125,7 @@ public class com/posthog/PostHogConfig { public final fun getPropertiesSanitizer ()Lcom/posthog/PostHogPropertiesSanitizer; public final fun getProxy ()Ljava/net/Proxy; public final fun getQueueProvider ()Lkotlin/jvm/functions/Function5; + public final fun getReleaseIdentifier ()Ljava/lang/String; public final fun getRemoteConfig ()Z public final fun getRemoteConfigProvider ()Lkotlin/jvm/functions/Function4; public final fun getReplayStoragePrefix ()Ljava/lang/String; @@ -162,6 +163,7 @@ public class com/posthog/PostHogConfig { public final fun setPreloadFeatureFlags (Z)V public final fun setPropertiesSanitizer (Lcom/posthog/PostHogPropertiesSanitizer;)V public final fun setProxy (Ljava/net/Proxy;)V + public final fun setReleaseIdentifier (Ljava/lang/String;)V public final fun setRemoteConfig (Z)V public final fun setReplayStoragePrefix (Ljava/lang/String;)V public final fun setReuseAnonymousId (Z)V @@ -881,8 +883,8 @@ public final class com/posthog/internal/errortracking/ThrowableCoercer { public static final field EXCEPTION_LEVEL_ATTRIBUTE Ljava/lang/String; public static final field EXCEPTION_LEVEL_FATAL Ljava/lang/String; public fun ()V - public final fun fromThrowableToPostHogProperties (Ljava/lang/Throwable;Ljava/util/List;)Ljava/util/Map; - public static synthetic fun fromThrowableToPostHogProperties$default (Lcom/posthog/internal/errortracking/ThrowableCoercer;Ljava/lang/Throwable;Ljava/util/List;ILjava/lang/Object;)Ljava/util/Map; + public final fun fromThrowableToPostHogProperties (Ljava/lang/Throwable;Ljava/util/List;Ljava/lang/String;)Ljava/util/Map; + public static synthetic fun fromThrowableToPostHogProperties$default (Lcom/posthog/internal/errortracking/ThrowableCoercer;Ljava/lang/Throwable;Ljava/util/List;Ljava/lang/String;ILjava/lang/Object;)Ljava/util/Map; } public abstract interface class com/posthog/internal/replay/PostHogSessionReplayHandler { From c95c3d14e4d04438aee44b6327512eac7643e18e Mon Sep 17 00:00:00 2001 From: Manoel Aranda Neto Date: Fri, 28 Nov 2025 14:58:12 +0100 Subject: [PATCH 09/11] fixes --- CHANGELOG.md | 1 + LICENSE.md | 27 +++++++++++++++++++ Makefile | 6 +++++ README.md | 13 ++++++++- dangerfile.js | 1 + gradle.properties | 1 + posthog-android-gradle-plugin/CHANGELOG.md | 3 +++ posthog-android-gradle-plugin/USAGE.md | 0 .../build.gradle.kts | 6 +++++ .../posthog/android/DirectoryOutputTask.kt | 2 ++ ...jectPostHogMetaPropertiesIntoAssetsTask.kt | 2 ++ .../kotlin/com/posthog/android/OutputPaths.kt | 2 ++ .../android/PostHogAndroidGradlePlugin.kt | 2 ++ .../com/posthog/android/PostHogCliExecTask.kt | 2 ++ .../android/PostHogGenerateMapIdTask.kt | 2 ++ .../posthog/android/PostHogTasksProvider.kt | 2 ++ .../PostHogUploadProguardMappingsTask.kt | 2 ++ .../android/PropertiesFileOutputTask.kt | 2 ++ .../kotlin/com/posthog/android/TasksUtils.kt | 2 ++ .../main/kotlin/com/posthog/android/Utils.kt | 2 ++ posthog-android/CHANGELOG.md | 2 ++ .../posthog-android-sample/build.gradle.kts | 2 +- scripts/bump-version.sh | 4 +++ 23 files changed, 86 insertions(+), 2 deletions(-) create mode 100644 posthog-android-gradle-plugin/CHANGELOG.md create mode 100644 posthog-android-gradle-plugin/USAGE.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d8afe4a..c27f26da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,3 +7,4 @@ Changelogs can be found in the individual packages: - [posthog](./posthog/CHANGELOG.md) - [posthog-android](./posthog-android/CHANGELOG.md) - [posthog-server](./posthog-server/CHANGELOG.md) +- [posthog-android-gradle-plugin](./posthog-android-gradle-plugin/CHANGELOG.md) diff --git a/LICENSE.md b/LICENSE.md index 7e799e3c..cfb4c4b3 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -19,3 +19,30 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--- + +Some files in this codebase contain code from getsentry/sentry-android-gradle-plugin by Software, Inc. dba Sentry. +In such cases it is explicitly stated in the file header. This license only applies to the relevant code in such cases. + +MIT License + +Copyright (c) 2020 Sentry + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile index bd8f6096..af5bfd64 100644 --- a/Makefile +++ b/Makefile @@ -32,6 +32,9 @@ dryReleaseAndroid: dryReleaseServer: ./gradlew :posthog-server:publishToMavenLocal +dryReleaseAndroidPlugin: + ./gradlew :posthog-android-gradle-plugin:publishToMavenLocal + release: ./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository @@ -44,6 +47,9 @@ releaseAndroid: releaseServer: ./gradlew :posthog-server:publishToSonatype closeAndReleaseSonatypeStagingRepository +releaseAndroidPlugin: + ./gradlew :posthog-android-gradle-plugin:publishToSonatype closeAndReleaseSonatypeStagingRepository + testReport: ./gradlew koverHtmlReport diff --git a/README.md b/README.md index 384da9a5..73b37d9f 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,10 @@ [![GH Workflow](https://img.shields.io/github/actions/workflow/status/PostHog/posthog-android/build.yml?branch=main)](https://github.com/PostHog/posthog-android/actions) | Packages | Maven Central | Min Version | -| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- | +|-----------------| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- | | posthog-android | [![Maven Central](https://maven-badges.herokuapp.com/sonatype-central/com.posthog/posthog-android/badge.svg)](https://maven-badges.herokuapp.com/sonatype-central/com.posthog/posthog-android) | Android API 21 | | posthog (core) | [![Maven Central](https://maven-badges.herokuapp.com/sonatype-central/com.posthog/posthog/badge.svg)](https://maven-badges.herokuapp.com/sonatype-central/com.posthog/posthog) | Java 8 | +| posthog-server | [![Maven Central](https://maven-badges.herokuapp.com/sonatype-central/com.posthog/posthog-server/badge.svg)](https://maven-badges.herokuapp.com/sonatype-central/com.posthog/posthog-server) | Java 8 | # PostHog Android & JVM SDKs @@ -31,6 +32,16 @@ implementation("com.posthog:posthog:$latestVersion") **Documentation:** [posthog/](./posthog/) | **Usage:** [posthog/USAGE.md](./posthog/USAGE.md) +### posthog-server + +Pure Kotlin/JVM library suitable for server integrations. + +```kotlin +implementation("com.posthog:posthog-server:$latestVersion") +``` + +**Documentation:** [posthog-server/](./posthog-server/) | **Usage:** [posthog-server/USAGE.md](./posthog-server/USAGE.md) + ## Documentation Please see the main [PostHog docs](https://posthog.com/docs). diff --git a/dangerfile.js b/dangerfile.js index 04ab5bcd..ef6440ae 100644 --- a/dangerfile.js +++ b/dangerfile.js @@ -45,6 +45,7 @@ Please add an entry to the appropriate changelog: - \`posthog/CHANGELOG.md\` (core module) - \`posthog-android/CHANGELOG.md\` (android module) - \`posthog-server/CHANGELOG.md\` (server module) +- \`posthog-android-gradle-plugin/CHANGELOG.md\` (android plugin module) Make sure the entry includes this PR's number. Example: diff --git a/gradle.properties b/gradle.properties index 13c1488b..3c8bf2b4 100644 --- a/gradle.properties +++ b/gradle.properties @@ -25,3 +25,4 @@ android.defaults.buildfeatures.resvalues=false coreVersion=5.1.0 androidVersion=3.26.0 serverVersion=2.0.1 +androidPluginVersion=1.0.0 \ No newline at end of file diff --git a/posthog-android-gradle-plugin/CHANGELOG.md b/posthog-android-gradle-plugin/CHANGELOG.md new file mode 100644 index 00000000..f20c1dfe --- /dev/null +++ b/posthog-android-gradle-plugin/CHANGELOG.md @@ -0,0 +1,3 @@ +## Next + +- feat: proguard support ([#316](https://github.com/PostHog/posthog-android/pull/316)) diff --git a/posthog-android-gradle-plugin/USAGE.md b/posthog-android-gradle-plugin/USAGE.md new file mode 100644 index 00000000..e69de29b diff --git a/posthog-android-gradle-plugin/build.gradle.kts b/posthog-android-gradle-plugin/build.gradle.kts index 777fba43..74642969 100644 --- a/posthog-android-gradle-plugin/build.gradle.kts +++ b/posthog-android-gradle-plugin/build.gradle.kts @@ -1,6 +1,12 @@ +version = properties["androidPluginVersion"].toString() + plugins { `kotlin-dsl` id("java-gradle-plugin") + + // publish + `maven-publish` + signing } gradlePlugin { diff --git a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/DirectoryOutputTask.kt b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/DirectoryOutputTask.kt index c62da014..5c5c3fda 100644 --- a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/DirectoryOutputTask.kt +++ b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/DirectoryOutputTask.kt @@ -1,3 +1,5 @@ +// borrowed from https://github.com/getsentry/sentry-android-gradle-plugin/blob/0ce926822756c8379e281bed8c33237a400c9582/plugin-build/src/main/kotlin/io/sentry/android/gradle/tasks/DirectoryOutputTask.kt#L3 + package com.posthog.android import org.gradle.api.DefaultTask diff --git a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/InjectPostHogMetaPropertiesIntoAssetsTask.kt b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/InjectPostHogMetaPropertiesIntoAssetsTask.kt index 5529c5be..1ebf5f89 100644 --- a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/InjectPostHogMetaPropertiesIntoAssetsTask.kt +++ b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/InjectPostHogMetaPropertiesIntoAssetsTask.kt @@ -1,3 +1,5 @@ +// adapted from https://github.com/getsentry/sentry-android-gradle-plugin/blob/0ce926822756c8379e281bed8c33237a400c9582/plugin-build/src/main/kotlin/io/sentry/android/gradle/tasks/InjectSentryMetaPropertiesIntoAssetsTask.kt#L31 + package com.posthog.android import org.gradle.api.DefaultTask diff --git a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/OutputPaths.kt b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/OutputPaths.kt index be76bceb..4bf15e69 100644 --- a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/OutputPaths.kt +++ b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/OutputPaths.kt @@ -1,3 +1,5 @@ +// adapted from https://github.com/getsentry/sentry-android-gradle-plugin/blob/0ce926822756c8379e281bed8c33237a400c9582/plugin-build/src/main/kotlin/io/sentry/android/gradle/sourcecontext/OutputPaths.kt#L1-L2 + package com.posthog.android import org.gradle.api.Project diff --git a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogAndroidGradlePlugin.kt b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogAndroidGradlePlugin.kt index fc2502a5..a94c28a5 100644 --- a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogAndroidGradlePlugin.kt +++ b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogAndroidGradlePlugin.kt @@ -1,3 +1,5 @@ +// adapted from https://github.com/getsentry/sentry-android-gradle-plugin/blob/0ce926822756c8379e281bed8c33237a400c9582/plugin-build/src/main/kotlin/io/sentry/android/gradle/SentryPlugin.kt#L9 + package com.posthog.android import com.android.build.api.artifact.SingleArtifact diff --git a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogCliExecTask.kt b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogCliExecTask.kt index 5c03b3e5..cde0cdce 100644 --- a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogCliExecTask.kt +++ b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogCliExecTask.kt @@ -1,3 +1,5 @@ +// adapted from https://github.com/getsentry/sentry-android-gradle-plugin/blob/0ce926822756c8379e281bed8c33237a400c9582/plugin-build/src/main/kotlin/io/sentry/android/gradle/tasks/SentryCliExecTask.kt#L13 + package com.posthog.android import org.apache.tools.ant.taskdefs.condition.Os diff --git a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogGenerateMapIdTask.kt b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogGenerateMapIdTask.kt index 379d2653..01025cc7 100644 --- a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogGenerateMapIdTask.kt +++ b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogGenerateMapIdTask.kt @@ -1,3 +1,5 @@ +// adapted from https://github.com/getsentry/sentry-android-gradle-plugin/blob/0ce926822756c8379e281bed8c33237a400c9582/plugin-build/src/main/kotlin/io/sentry/android/gradle/tasks/SentryGenerateProguardUuidTask.kt#L12 + package com.posthog.android import org.gradle.api.Project diff --git a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogTasksProvider.kt b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogTasksProvider.kt index 70d0e672..42b77edf 100644 --- a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogTasksProvider.kt +++ b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogTasksProvider.kt @@ -1,3 +1,5 @@ +// adapted from https://github.com/getsentry/sentry-android-gradle-plugin/blob/0ce926822756c8379e281bed8c33237a400c9582/plugin-build/src/main/kotlin/io/sentry/android/gradle/SentryTasksProvider.kt#L10 + package com.posthog.android import com.android.build.api.variant.ApplicationVariant diff --git a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogUploadProguardMappingsTask.kt b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogUploadProguardMappingsTask.kt index cc57e7c9..1356d99f 100644 --- a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogUploadProguardMappingsTask.kt +++ b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogUploadProguardMappingsTask.kt @@ -1,3 +1,5 @@ +// adapted from https://github.com/getsentry/sentry-android-gradle-plugin/blob/0ce926822756c8379e281bed8c33237a400c9582/plugin-build/src/main/kotlin/io/sentry/android/gradle/tasks/SentryUploadProguardMappingsTask.kt#L16 + package com.posthog.android import org.gradle.api.Project diff --git a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PropertiesFileOutputTask.kt b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PropertiesFileOutputTask.kt index f493d35b..1671f92c 100644 --- a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PropertiesFileOutputTask.kt +++ b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PropertiesFileOutputTask.kt @@ -1,3 +1,5 @@ +// copied from https://github.com/getsentry/sentry-android-gradle-plugin/blob/0ce926822756c8379e281bed8c33237a400c9582/plugin-build/src/main/kotlin/io/sentry/android/gradle/tasks/PropertiesFileOutputTask.kt#L6 + package com.posthog.android import org.gradle.api.file.RegularFile diff --git a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/TasksUtils.kt b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/TasksUtils.kt index 24f4ed59..13c6bc10 100644 --- a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/TasksUtils.kt +++ b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/TasksUtils.kt @@ -1,3 +1,5 @@ +// adapted from https://github.com/getsentry/sentry-android-gradle-plugin/blob/0ce926822756c8379e281bed8c33237a400c9582/plugin-build/src/main/kotlin/io/sentry/android/gradle/util/tasks.kt#L5 + package com.posthog.android import com.android.build.api.artifact.SingleArtifact diff --git a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/Utils.kt b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/Utils.kt index 71c4ea1a..43d16565 100644 --- a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/Utils.kt +++ b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/Utils.kt @@ -1,3 +1,5 @@ +// adapted from https://github.com/getsentry/sentry-android-gradle-plugin/blob/0ce926822756c8379e281bed8c33237a400c9582/plugin-build/src/main/kotlin/io/sentry/android/gradle/util/Files.kt#L5 + package com.posthog.android import com.posthog.android.PostHogGenerateMapIdTask.Companion.POSTHOG_PROGUARD_MAPPING_MAP_ID_PROPERTY diff --git a/posthog-android/CHANGELOG.md b/posthog-android/CHANGELOG.md index c14bf711..bfb96c98 100644 --- a/posthog-android/CHANGELOG.md +++ b/posthog-android/CHANGELOG.md @@ -1,5 +1,7 @@ ## Next +- feat: proguard support ([#316](https://github.com/PostHog/posthog-android/pull/316)) + ## 3.26.0 - 2025-11-05 - feat: Cache properties for flag evaluation ([#315](https://github.com/PostHog/posthog-android/pull/315)) diff --git a/posthog-samples/posthog-android-sample/build.gradle.kts b/posthog-samples/posthog-android-sample/build.gradle.kts index 0282e209..10f0a632 100644 --- a/posthog-samples/posthog-android-sample/build.gradle.kts +++ b/posthog-samples/posthog-android-sample/build.gradle.kts @@ -1,7 +1,7 @@ plugins { id("com.android.application") kotlin("android") - id("com.posthog.android") + // id("com.posthog.android") } android { diff --git a/scripts/bump-version.sh b/scripts/bump-version.sh index 20f83ff4..7932c11c 100755 --- a/scripts/bump-version.sh +++ b/scripts/bump-version.sh @@ -25,12 +25,16 @@ case "$MODULE" in "server") perl -pi -e "s/serverVersion=.*/serverVersion=$NEW_VERSION/g" $GRADLE_FILEPATH ;; + "androidPlugin") + perl -pi -e "s/androidPluginVersion=.*/androidPluginVersion=$NEW_VERSION/g" $GRADLE_FILEPATH + ;; *) echo "Usage: $0 {core|android|server} " echo "Examples:" echo " $0 core 3.0.1" echo " $0 android 3.0.2" echo " $0 server 1.0.1" + echo " $0 androidPlugin 3.0.2" exit 1 ;; esac From fe3589b88682842c4d8a2336e458c66343dfe6a2 Mon Sep 17 00:00:00 2001 From: Manoel Aranda Neto Date: Fri, 28 Nov 2025 15:09:52 +0100 Subject: [PATCH 10/11] fixes --- .github/workflows/release.yml | 6 +++++- README.md | 11 +++++++++++ RELEASING.md | 23 ++++++++++++++++++++++- posthog-android-gradle-plugin/USAGE.md | 7 +++++++ 4 files changed, 45 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f8008888..17d3647f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -54,8 +54,12 @@ jobs: echo "dry_target=dryReleaseServer" >> $GITHUB_OUTPUT echo "release_target=releaseServer" >> $GITHUB_OUTPUT echo "module=server" >> $GITHUB_OUTPUT + elif [[ "$TAG" == androidPlugin-v* ]]; then + echo "dry_target=dryReleaseAndroidPlugin" >> $GITHUB_OUTPUT + echo "release_target=releaseAndroidPlugin" >> $GITHUB_OUTPUT + echo "module=androidPlugin" >> $GITHUB_OUTPUT else - echo "ERROR: Unknown tag format '$TAG'. Expected 'core-v*', 'android-v*', or 'server-v*'" + echo "ERROR: Unknown tag format '$TAG'. Expected 'core-v*', 'android-v*', 'server-v*', or 'androidPlugin-v*'." exit 1 fi diff --git a/README.md b/README.md index 73b37d9f..1dc2472c 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ | posthog-android | [![Maven Central](https://maven-badges.herokuapp.com/sonatype-central/com.posthog/posthog-android/badge.svg)](https://maven-badges.herokuapp.com/sonatype-central/com.posthog/posthog-android) | Android API 21 | | posthog (core) | [![Maven Central](https://maven-badges.herokuapp.com/sonatype-central/com.posthog/posthog/badge.svg)](https://maven-badges.herokuapp.com/sonatype-central/com.posthog/posthog) | Java 8 | | posthog-server | [![Maven Central](https://maven-badges.herokuapp.com/sonatype-central/com.posthog/posthog-server/badge.svg)](https://maven-badges.herokuapp.com/sonatype-central/com.posthog/posthog-server) | Java 8 | +| posthog-android-gradle-plugin | [![Maven Central](https://maven-badges.herokuapp.com/sonatype-central/com.posthog/posthog-android-gradle-plugin/badge.svg)](https://maven-badges.herokuapp.com/sonatype-central/com.posthog/posthog-android-gradle-plugin) | Java 8 | # PostHog Android & JVM SDKs @@ -42,6 +43,16 @@ implementation("com.posthog:posthog-server:$latestVersion") **Documentation:** [posthog-server/](./posthog-server/) | **Usage:** [posthog-server/USAGE.md](./posthog-server/USAGE.md) +### posthog-android-gradle-plugin + +Gradle plugin suitable for Android-specific features. + +```kotlin +implementation("com.posthog:posthog-android-gradle-plugin:$latestVersion") +``` + +**Documentation:** [posthog-android-gradle-plugin/](./posthog-android-gradle-plugin/) | **Usage:** [posthog-android-gradle-plugin/USAGE.md](./posthog-android-gradle-plugin/USAGE.md) + ## Documentation Please see the main [PostHog docs](https://posthog.com/docs). diff --git a/RELEASING.md b/RELEASING.md index c50c2386..ce75b66f 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -62,13 +62,34 @@ Since `main` is protected, releases are done via pull requests. 10. A GitHub action triggers `make releaseServer` automatically 11. Done +### Android plugin module (posthog-android-gradle-plugin): + +1. Update `posthog-android-gradle-plugin/CHANGELOG.md` with the version and date +2. Run: `./scripts/prepare-release.sh androidPlugin 1.0.1` + - This creates a release branch, bumps version, commits, and pushes +3. Create a PR from the release branch to `main` +4. Get approval and merge the PR +5. After merge, create and push the tag from `main`: + ```bash + git checkout main && git pull + git tag -a androidPlugin-v1.0.1 -m "androidPlugin 1.0.1" + git push --tags + ``` +6. Go to [GH Releases](https://github.com/PostHog/posthog-android/releases) and draft a new release +7. Choose the tag you just created (e.g. `androidPlugin-v1.0.1`) and use it as the release name +8. Write a description of the release +9. Publish the release +10. A GitHub action triggers `make releaseAndroidPlugin` automatically +11. Done + ## Tag Naming Convention - `core-v3.23.0` → releases only posthog core module - `android-v3.23.0` → releases only posthog-android module - `server-v1.0.1` → releases only posthog-server module +- `androidPlugin-v1.0.1` → releases only posthog-android-gradle-plugin module -Preview releases follow the pattern `core-v3.0.0-alpha.1`, `android-v3.0.0-beta.1`, `server-v1.0.0-alpha.1`, etc. +Preview releases follow the pattern `core-v3.0.0-alpha.1`, `android-v3.0.0-beta.1`, `server-v1.0.0-alpha.1`, `androidPlugin-v1.0.0-alpha.1`, etc. # Rotating Sonatype User Token diff --git a/posthog-android-gradle-plugin/USAGE.md b/posthog-android-gradle-plugin/USAGE.md index e69de29b..2f6275a0 100644 --- a/posthog-android-gradle-plugin/USAGE.md +++ b/posthog-android-gradle-plugin/USAGE.md @@ -0,0 +1,7 @@ +```kotlin +plugins { + id("com.android.application") + kotlin("android") + id("com.posthog.android") // <- add this plugin +} +``` From f2f4f3a870f4e8e8de5430c0bce935d21e929e35 Mon Sep 17 00:00:00 2001 From: Manoel Aranda Neto Date: Fri, 28 Nov 2025 16:16:38 +0100 Subject: [PATCH 11/11] fix --- buildSrc/build.gradle.kts | 3 +- .../build.gradle.kts | 111 ++++++++++++++++++ .../settings.gradle.kts | 11 ++ .../posthog/android/DirectoryOutputTask.kt | 2 +- ...jectPostHogMetaPropertiesIntoAssetsTask.kt | 2 +- .../kotlin/com/posthog/android/OutputPaths.kt | 2 +- .../android/PostHogAndroidGradlePlugin.kt | 2 +- .../com/posthog/android/PostHogCliExecTask.kt | 2 +- .../android/PostHogGenerateMapIdTask.kt | 2 +- .../PostHogUploadProguardMappingsTask.kt | 2 +- .../android/PropertiesFileOutputTask.kt | 2 +- .../kotlin/com/posthog/android/TasksUtils.kt | 6 +- .../main/kotlin/com/posthog/android/Utils.kt | 12 +- .../posthog-android-sample/build.gradle.kts | 5 +- 14 files changed, 145 insertions(+), 19 deletions(-) diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 71d2cf76..320d7d61 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -14,9 +14,10 @@ kotlin { dependencies { // do not upgrade to >= 1.9 otherwise it does not work with Kotlin 1.7 - // also update PosthogBuildConfig.Kotlin.KOTLIN + // also update PosthogBuildConfig.Kotlin.KOTLIN and posthog-android-gradle-plugin val kotlinVersion = "1.8.22" // there's no 1.8.22 for dokka yet + // also update posthog-android-gradle-plugin val dokkaVersion = "1.8.20" // 8.3+ throws Could not determine the dependencies of task ':posthog:generateJvmTestLintModel'. implementation("com.android.tools.build:gradle:8.2.2") diff --git a/posthog-android-gradle-plugin/build.gradle.kts b/posthog-android-gradle-plugin/build.gradle.kts index 74642969..41e70620 100644 --- a/posthog-android-gradle-plugin/build.gradle.kts +++ b/posthog-android-gradle-plugin/build.gradle.kts @@ -1,3 +1,5 @@ +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + version = properties["androidPluginVersion"].toString() plugins { @@ -7,6 +9,54 @@ plugins { // publish `maven-publish` signing + id("org.jetbrains.dokka") +} + +java { + withSourcesJar() +} + +kotlin { + jvmToolchain(17) +} + +val dokkaJavadocJar by tasks.register("dokkaJavadocJar") { + dependsOn(tasks.dokkaJavadoc) + from(tasks.dokkaJavadoc.flatMap { it.outputDirectory }) + archiveClassifier.set("javadoc") +} + +val dokkaHtmlJar by tasks.register("dokkaHtmlJar") { + dependsOn(tasks.dokkaHtml) + from(tasks.dokkaHtml.flatMap { it.outputDirectory }) + archiveClassifier.set("html-doc") +} + +tasks.withType().configureEach { + kotlinOptions { + jvmTarget = JavaVersion.VERSION_17.toString() + languageVersion = "1.6" + allWarningsAsErrors = true + apiVersion = "1.6" + freeCompilerArgs += "-Xexplicit-api=strict" + } +} + +kotlin { + explicitApi() + jvmToolchain(JavaVersion.VERSION_17.majorVersion.toInt()) +} + +configure { + test { + java.srcDir("src/test/java") + } +} + +tasks.javadoc { + if (JavaVersion.current().isJava9Compatible) { + (options as StandardJavadocDocletOptions).addBooleanOption("html5", true) + } } gradlePlugin { @@ -19,7 +69,68 @@ gradlePlugin { } } +publishing { + publications { + // Configure the automatic plugin publication + withType { + if (name == "pluginMaven") { + groupId = "com.posthog" + version = project.version.toString() + // Add dokka artifacts to the plugin publication + artifact(dokkaJavadocJar) + artifact(dokkaHtmlJar) + } + } + } + + publications.withType { + pom { + val repo = "posthog-android" + name.set(project.name) + description.set("PostHog Android Gradle Plugin") + url.set("https://github.com/postHog/$repo") + + licenses { + license { + name.set("MIT") + url.set("http://opensource.org/licenses/MIT") + } + } + organization { + name.set("PostHog") + url.set("https://posthog.com") + } + developers { + developer { + name.set("PostHog") + email.set("engineering@posthog.com") + organization.set("PostHog") + organizationUrl.set("https://posthog.com") + } + } + + scm { + url.set("https://github.com/postHog/$repo") + connection.set("scm:git:git@github.com:PostHog/$repo.git") + developerConnection.set("scm:git:git@github.com:PostHog/$repo.git") + } + } + } + signing { + // created using manoel at posthog.com + val privateKey = System.getenv("GPG_PRIVATE_KEY") + val password = System.getenv("GPG_PASSPHRASE") + // releases are only signed on CI, so skip this locally + isRequired = System.getenv("CI")?.toBoolean() ?: false + useInMemoryPgpKeys(privateKey, password) + // Sign all publications + sign(publishing.publications) + } +} + dependencies { compileOnly(gradleApi()) + // pinned to 8.0.x so we compile against the min. supported version. compileOnly("com.android.tools.build:gradle:8.0.2") + implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.22") } diff --git a/posthog-android-gradle-plugin/settings.gradle.kts b/posthog-android-gradle-plugin/settings.gradle.kts index 1d8249a9..e0af027a 100644 --- a/posthog-android-gradle-plugin/settings.gradle.kts +++ b/posthog-android-gradle-plugin/settings.gradle.kts @@ -1,5 +1,16 @@ rootProject.name = "posthog-android-gradle-plugin" +pluginManagement { + repositories { + google() + mavenCentral() + gradlePluginPortal() + } + plugins { + id("org.jetbrains.dokka") version "1.8.20" + } +} + dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { diff --git a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/DirectoryOutputTask.kt b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/DirectoryOutputTask.kt index 5c5c3fda..1745edab 100644 --- a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/DirectoryOutputTask.kt +++ b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/DirectoryOutputTask.kt @@ -8,6 +8,6 @@ import org.gradle.api.tasks.OutputDirectory import org.gradle.work.DisableCachingByDefault @DisableCachingByDefault(because = "abstract task, should not be used directly") -abstract class DirectoryOutputTask : DefaultTask() { +internal abstract class DirectoryOutputTask : DefaultTask() { @get:OutputDirectory abstract val output: DirectoryProperty } diff --git a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/InjectPostHogMetaPropertiesIntoAssetsTask.kt b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/InjectPostHogMetaPropertiesIntoAssetsTask.kt index 1ebf5f89..e1533de8 100644 --- a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/InjectPostHogMetaPropertiesIntoAssetsTask.kt +++ b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/InjectPostHogMetaPropertiesIntoAssetsTask.kt @@ -19,7 +19,7 @@ import java.io.File import java.util.Properties @CacheableTask -abstract class InjectPostHogMetaPropertiesIntoAssetsTask : DefaultTask() { +internal abstract class InjectPostHogMetaPropertiesIntoAssetsTask : DefaultTask() { @get:PathSensitive(PathSensitivity.RELATIVE) @get:InputFiles abstract val inputDir: DirectoryProperty diff --git a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/OutputPaths.kt b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/OutputPaths.kt index 4bf15e69..423cdd30 100644 --- a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/OutputPaths.kt +++ b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/OutputPaths.kt @@ -6,7 +6,7 @@ import org.gradle.api.Project internal const val ROOT_DIR = "intermediates/posthog" -class OutputPaths(private val project: Project, variantName: String) { +internal class OutputPaths(private val project: Project, variantName: String) { private fun dir(path: String) = project.layout.buildDirectory.dir(path) private val variantDirectory = "$ROOT_DIR/$variantName" diff --git a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogAndroidGradlePlugin.kt b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogAndroidGradlePlugin.kt index a94c28a5..e5fc22fa 100644 --- a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogAndroidGradlePlugin.kt +++ b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogAndroidGradlePlugin.kt @@ -13,7 +13,7 @@ import org.gradle.api.file.FileCollection import org.gradle.api.provider.Provider import org.gradle.api.tasks.TaskProvider -class PostHogAndroidGradlePlugin : Plugin { +internal class PostHogAndroidGradlePlugin : Plugin { override fun apply(project: Project) { if (!project.plugins.hasPlugin("com.android.application")) { project.logger.warn( diff --git a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogCliExecTask.kt b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogCliExecTask.kt index cde0cdce..63b4a7e2 100644 --- a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogCliExecTask.kt +++ b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogCliExecTask.kt @@ -7,7 +7,7 @@ import org.gradle.api.tasks.Exec import org.gradle.work.DisableCachingByDefault @DisableCachingByDefault(because = "abstract task, should not be used directly") -abstract class PostHogCliExecTask : Exec() { +internal abstract class PostHogCliExecTask : Exec() { override fun exec() { computeCommandLineArgs().let { commandLine(it) diff --git a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogGenerateMapIdTask.kt b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogGenerateMapIdTask.kt index 01025cc7..212f336a 100644 --- a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogGenerateMapIdTask.kt +++ b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogGenerateMapIdTask.kt @@ -15,7 +15,7 @@ import org.gradle.work.DisableCachingByDefault import java.util.UUID @DisableCachingByDefault -abstract class PostHogGenerateMapIdTask : PropertiesFileOutputTask() { +internal abstract class PostHogGenerateMapIdTask : PropertiesFileOutputTask() { init { description = "Generates a unique build ID to be used when uploading the PostHog mapping file" diff --git a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogUploadProguardMappingsTask.kt b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogUploadProguardMappingsTask.kt index 1356d99f..c6b23eb4 100644 --- a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogUploadProguardMappingsTask.kt +++ b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogUploadProguardMappingsTask.kt @@ -14,7 +14,7 @@ import org.gradle.api.tasks.TaskProvider import org.gradle.work.DisableCachingByDefault @DisableCachingByDefault(because = "Uploads should not be cached") -abstract class PostHogUploadProguardMappingsTask : PostHogCliExecTask() { +internal abstract class PostHogUploadProguardMappingsTask : PostHogCliExecTask() { init { description = "Uploads the proguard mappings file to PostHog" diff --git a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PropertiesFileOutputTask.kt b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PropertiesFileOutputTask.kt index 1671f92c..f3e8284c 100644 --- a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PropertiesFileOutputTask.kt +++ b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PropertiesFileOutputTask.kt @@ -8,6 +8,6 @@ import org.gradle.api.tasks.Internal import org.gradle.work.DisableCachingByDefault @DisableCachingByDefault(because = "abstract task, should not be used directly") -abstract class PropertiesFileOutputTask : DirectoryOutputTask() { +internal abstract class PropertiesFileOutputTask : DirectoryOutputTask() { @get:Internal abstract val outputFile: Provider } diff --git a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/TasksUtils.kt b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/TasksUtils.kt index 13c6bc10..13979d3c 100644 --- a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/TasksUtils.kt +++ b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/TasksUtils.kt @@ -14,7 +14,7 @@ import org.gradle.api.file.FileCollection import org.gradle.api.provider.Provider import org.gradle.api.tasks.TaskProvider -fun TaskProvider.hookWithMinifyTasks( +internal fun TaskProvider.hookWithMinifyTasks( project: Project, variantName: String, ) { @@ -30,7 +30,7 @@ fun TaskProvider.hookWithMinifyTasks( } } -fun TaskProvider.hookWithAssembleTasks( +internal fun TaskProvider.hookWithAssembleTasks( project: Project, variant: ApplicationVariant, ) { @@ -52,7 +52,7 @@ fun TaskProvider.hookWithAssembleTasks( } } -fun ApplicationVariant.mappingFileProvider(project: Project): Provider = +internal fun ApplicationVariant.mappingFileProvider(project: Project): Provider = project.provider { project.files(artifacts.get(SingleArtifact.OBFUSCATION_MAPPING_FILE)) } diff --git a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/Utils.kt b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/Utils.kt index 43d16565..93d63f92 100644 --- a/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/Utils.kt +++ b/posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/Utils.kt @@ -25,7 +25,7 @@ internal fun ByteArray.toHex(): String { } /** Returns md5/sha256 hash of the file contents */ -fun File.contentHash(): String { +internal fun File.contentHash(): String { // first try to read hash generated by R8, which is located in the first 20 lines of mapping // lines are lazily iterated over, so even on huge files performance should not be a problem var hash: String? = null @@ -48,7 +48,7 @@ fun File.contentHash(): String { return md.digest().toHex() } -fun String.capitalizeUS() = +internal fun String.capitalizeUS() = if (isEmpty()) { "" } else { @@ -61,7 +61,7 @@ internal fun loadProperties(file: File): Properties { return Properties().also { properties -> file.inputStream().use { properties.load(it) } } } -fun loadPropertiesMaybe(file: File): Properties? { +internal fun loadPropertiesMaybe(file: File): Properties? { if (!file.exists()) { return null } @@ -69,20 +69,20 @@ fun loadPropertiesMaybe(file: File): Properties? { return loadProperties(file) } -fun readMapIdFromFile(file: File): String { +internal fun readMapIdFromFile(file: File): String { val props = loadProperties(file) val uuid = props.getProperty(POSTHOG_PROGUARD_MAPPING_MAP_ID_PROPERTY) check(uuid != null) { "$POSTHOG_PROGUARD_MAPPING_MAP_ID_PROPERTY property is missing" } return uuid } -fun withLogging( +internal fun withLogging( logger: Logger, varName: String, initializer: () -> TaskProvider?, ) = initializer().also { logger.info("$varName is ${it?.name}") } -fun DirectoryProperty.getAndDelete(): File { +internal fun DirectoryProperty.getAndDelete(): File { val file = get().asFile if (file.isDirectory) { file.deleteRecursively() diff --git a/posthog-samples/posthog-android-sample/build.gradle.kts b/posthog-samples/posthog-android-sample/build.gradle.kts index 10f0a632..fee9f78d 100644 --- a/posthog-samples/posthog-android-sample/build.gradle.kts +++ b/posthog-samples/posthog-android-sample/build.gradle.kts @@ -1,7 +1,10 @@ plugins { id("com.android.application") kotlin("android") - // id("com.posthog.android") + // so we don't upload mappings from our release builds on CI + if (!PosthogBuildConfig.isCI()) { + id("com.posthog.android") + } } android {