Skip to content

Commit

Permalink
Merge pull request #49 from trello/bp/support_ksp
Browse files Browse the repository at this point in the history
Add KSP support to Mr Clean
  • Loading branch information
bparent-atl authored Mar 7, 2024
2 parents 0eb00d8 + 9eba2c8 commit 593fcc4
Show file tree
Hide file tree
Showing 39 changed files with 1,019 additions and 734 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
# 2.0.0
* Breaking revision - KSP support and support for KAPT has been dropped
* 1.2.2 should still be used for users using KAPT as only KSP will be supported moving forward
* mr-clean-annotations has been renamed mr-clean-runtime
* mr-clean-debug-processor is used to generate the debug implementation of the root function
* mr-clean-processor will generate the sanitized version of strings

# 1.2.2
* Updates Kotlin to 1.9.0
* Updates Gradle to 8.1.0
Expand Down
49 changes: 49 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,55 @@ inline fun SensitiveData.sanitizedToString(condition: Boolean): String = "Sensit
Don't leak sensitive info ever again, trust in Mr. Clean.

# Usage
```groovy
buildscript {
repositories {
mavenCentral()
google()
}
dependencies {
classpath 'com.trello.mrclean:mr-clean-plugin:2.0.0'
}
}
```

and then apply to your modules
```groovy
apply plugin: 'com.trello.mrclean'
```

# Don't care for plugins?
The Mr. Clean plugin adds `mr-clean-debug-processor` to `debug` BuildTypes automatically and `mr-clean-processor` to all others as well as setting the `packageName` defined in your manifest. Finally it generates the root `Any?.sanitizeToString` function for you (so you don't have errors pre compile)

If you'd prefer to manually control this, you can.

```groovy
ksp {
// Anything set here will override *ALL* values across all
// Generates root function Any.sanitizeToString
arg("mrclean.rootgenerator", "true")
// Package name to generate in under generated
arg("mrclean.packagename", "true")
// Setting this will override d
arg("mrclean.debug", "true")
}
dependencies {
implmentation 'com.trello.mrclean:mr-clean-runtime:2.0.0'
// Debug by default has KSP arg "mrclean.debug" set to false, "mrclean.rootgenerator" to false
kspRelease 'com.trello.mrclean:mr-clean-processor:2.0.0'
// Debug by default has KSP arg "mrclean.debug" set to true, "mrclean.rootgenerator" to false
kspDebug 'com.trello.mrclean:mr-clean-debug-processor:2.0.0'
}
```


# Looking for KAPT support?
Use 1.2.2 as Mr. Clean is pretty stable but we are only supporting KSP moving forward

1.2.2 usage is

```groovy
buildscript {
repositories {
Expand Down
49 changes: 7 additions & 42 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,52 +1,17 @@
buildscript {
ext.versions = [
'incap' : '1.0.0',
'kotlin': '1.9.0'
]

ext.deps = [
android: [
'runtime': 'com.google.android:android:4.1.1.4',
'gradlePlugin': "com.android.tools.build:gradle:8.1.0",
],
'autoService':'com.google.auto.service:auto-service:1.1.1',
'kotlin': [
'reflect': "org.jetbrains.kotlin:kotlin-reflect:${versions.kotlin}",
'stdlib': "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${versions.kotlin}"
],
'incap': [
'runtime': "net.ltgt.gradle.incap:incap:${versions.incap}",
'processor': "net.ltgt.gradle.incap:incap-processor:${versions.incap}",
],
'junit': 'junit:junit:4.13.2',
'kotlinx': [
'metadataJvm':"org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.7.0"
],
'kotlinpoet': 'com.squareup:kotlinpoet:1.12.0'
]

repositories {
mavenCentral()
}

dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}"
classpath "com.vanniktech:gradle-maven-publish-plugin:0.18.0"
}

allprojects {
repositories {
google()
mavenCentral()
jcenter()
}

group = GROUP
version = VERSION_NAME
}
}


plugins {
id "com.github.ben-manes.versions" version "0.45.0"
alias libs.plugins.kotlin.jvm apply false
alias libs.plugins.ksp apply false
alias libs.plugins.vanniktech.publish apply false
alias libs.plugins.versions
alias(libs.plugins.com.android.application) apply false
alias(libs.plugins.org.jetbrains.kotlin.android) apply false
}

15 changes: 13 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
GROUP=com.trello.mrclean
VERSION_NAME=1.2.3-SNAPSHOT
VERSION_NAME=2.0.0-SNAPSHOT

POM_DESCRIPTION=Gradle plugin for generating release worthy toStrings
POM_INCEPTION_YEAR=2018
Expand All @@ -17,4 +17,15 @@ POM_DEVELOPER_ID=atlassian
POM_DEVELOPER_NAME=Atlassian Inc
POM_DEVELOPER_URL=https://github.com/atlassian-labs
android.defaults.buildfeatures.buildconfig=true
android.enableBuildConfigAsBytecode=true
android.enableBuildConfigAsBytecode=true

SONATYPE_HOST=DEFAULT

# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app's APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Enables namespacing of each library's R class so that its R class includes only the
# resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library
android.nonTransitiveRClass=true
43 changes: 43 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
[versions]
android-runtime = "4.1.1.4"
activity-compose = "1.8.0"
autoservice = "1.1.1"
autoserviceKsp = "1.1.0"
kotlin = "1.9.10"
ksp = "1.9.10-1.0.13"
versionsPlugin = "0.49.0"
kotlinPoet = "1.14.2"
agp = "8.1.2"
org-jetbrains-kotlin-android = "1.9.10"
junit = "4.13.2"
compileTesting = "1.5.0"
truth = "1.1.4"
kctfork = "0.2.1"
vanniktech-publish = "0.25.3"

[libraries]
autoservice-ksp = { module = "dev.zacsweers.autoservice:auto-service-ksp", version.ref = "autoserviceKsp" }
autoservice-annotations = { module = "com.google.auto.service:auto-service-annotations", version.ref = "autoservice" }
android-runtime = { module = "com.google.android:android", version.ref = "android-runtime" }
android-gradlePlugin = { module = "com.android.tools.build:gradle", version.ref = "agp" }
kotlinPoet-core = { module = "com.squareup:kotlinpoet", version.ref = "kotlinPoet" }
kotlinPoet-ksp = { module = "com.squareup:kotlinpoet-ksp", version.ref = "kotlinPoet" }
kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk7", version.ref = "kotlin" }
ksp = { module = "com.google.devtools.ksp:symbol-processing-api ", version.ref = "ksp" }
plugin-ksp = { module = "com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin", version.ref = "ksp" }
junit = { group = "junit", name = "junit", version.ref = "junit" }
truth = { module ="com.google.truth:truth", version.ref = "truth" }

compileTesting-core = { module = "com.github.tschuchortdev:kotlin-compile-testing", version.ref = "compileTesting" }
compileTesting-ksp = { module = "com.github.tschuchortdev:kotlin-compile-testing-ksp", version.ref = "compileTesting" }
zsweers-compileTesting-core = { module = "dev.zacsweers.kctfork:core", version.ref = "kctfork"}
zsweers-compileTesting-ksp= { module = "dev.zacsweers.kctfork:ksp", version.ref = "kctfork"}


[plugins]
versions = { id = "com.github.ben-manes.versions", version.ref = "versionsPlugin" }
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
vanniktech-publish = { id = "com.vanniktech.maven.publish", version.ref = "vanniktech-publish" }
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
com-android-application = { id = "com.android.application", version.ref = "agp" }
org-jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "org-jetbrains-kotlin-android" }
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
3 changes: 0 additions & 3 deletions mr-clean-annotations/gradle.properties

This file was deleted.

26 changes: 26 additions & 0 deletions mr-clean-debug-processor/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
plugins {
alias(libs.plugins.kotlin.jvm)
alias(libs.plugins.ksp)
alias(libs.plugins.vanniktech.publish)
}

dependencies {
implementation(project(":mr-clean-processor-core"))
implementation(project(":mr-clean-runtime"))
ksp(libs.autoservice.ksp)
implementation(libs.autoservice.annotations)

implementation(libs.kotlin.stdlib)

implementation(libs.ksp)

testImplementation(libs.junit)
testImplementation(libs.truth)
testImplementation(libs.zsweers.compileTesting.core)
testImplementation(libs.zsweers.compileTesting.ksp)
}

ksp {
arg("autoserviceKsp.verify", "true")
arg("autoserviceKsp.verbose", "true")
}
3 changes: 3 additions & 0 deletions mr-clean-debug-processor/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
POM_NAME=Mr. Clean Debug Processor
POM_ARTIFACT_ID=mr-clean-debug-processor
POM_PACKAGING=jar
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.trello.mrclean

import com.google.devtools.ksp.processing.CodeGenerator
import com.google.devtools.ksp.processing.KSPLogger
import com.google.devtools.ksp.processing.Resolver
import com.google.devtools.ksp.processing.SymbolProcessor
import com.google.devtools.ksp.symbol.KSAnnotated


class MrCleanDebugProcessor(
val codeGenerator: CodeGenerator,
val options: Map<String, String>,
private val logger: KSPLogger,
) : SymbolProcessor {
override fun process(resolver: Resolver): List<KSAnnotated> {
logger.info("Mr. Clean Debug Processor has $options")
val debugOptions = mutableMapOf<String, String>()
debugOptions.putAll(options)
if (options[MrCleanCoreProcessor.DEBUG_KEY] == null) {
logger.info("\"MrCleanCoreProcessor.DEBUG_KEY\" not set, defatuling to true")
debugOptions[MrCleanCoreProcessor.DEBUG_KEY] = "true"
}
if (options[MrCleanCoreProcessor.ROOT_GENERATOR_KEY] == null) {
logger.info("\"$MrCleanCoreProcessor.ROOT_GENERATOR_KEY\" not set, defaulting to true")
debugOptions[MrCleanCoreProcessor.ROOT_GENERATOR_KEY] = "true"
}
return MrCleanCoreProcessor(
codeGenerator = codeGenerator,
options = debugOptions,
logger = logger,
).process(resolver)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.trello.mrclean

import com.google.auto.service.AutoService
import com.google.devtools.ksp.processing.SymbolProcessor
import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
import com.google.devtools.ksp.processing.SymbolProcessorProvider

@AutoService(SymbolProcessorProvider::class)
class MrCleanDebugProcessorProvider : SymbolProcessorProvider {
override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor =
MrCleanDebugProcessor(
codeGenerator = environment.codeGenerator,
options = environment.options,
logger = environment.logger,
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.trello.mrclean

import com.google.common.truth.Truth.assertThat
import com.tschuchort.compiletesting.KotlinCompilation
import com.tschuchort.compiletesting.KotlinCompilation.ExitCode
import com.tschuchort.compiletesting.SourceFile
import com.tschuchort.compiletesting.kspSourcesDir
import com.tschuchort.compiletesting.symbolProcessorProviders
import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi
import org.junit.Test
import java.io.File

class MrCleanDebugProcessorTest {

@OptIn(ExperimentalCompilerApi::class)
@Test
fun `debug default cleartext private string`() {
val kotlinSource = SourceFile.kotlin(
"KClass.kt",
"""
package com.test
import com.trello.mrclean.annotations.Sanitize
@Sanitize
data class EightParam(
val one: Int,
val two: Int,
val three: Int,
val four: Int,
val five: Int,
val six: Int,
private val seven: Int,
val eight: Int,
)
""",
)
val option = mapOf(
"mrclean.packagename" to "com.test",
)
val compilation = KotlinCompilation().apply {
sources = listOf(kotlinSource)

// pass your own instance of an annotation processor
symbolProcessorProviders = listOf(TestMrCleanProcessorProvider(option))

inheritClassPath = true
messageOutputStream = System.out // see diagnostics in real time
}
val result = compilation.compile()
assertThat(result.exitCode).isEqualTo(ExitCode.OK)
val generatedSourcesDir = compilation.kspSourcesDir
val generatedFile =
File(generatedSourcesDir, "kotlin/com/test/SanitizationForTest.EightParam.kt")
assertThat(generatedFile.exists()).isTrue()
assertThat(
generatedFile.readText().trimIndent(),
).isEqualTo(
"""
package com.test
import kotlin.String
import kotlin.Suppress
@Suppress("NOTHING_TO_INLINE")
internal inline fun EightParam.sanitizedToString(): String =
""${'"'}EightParam(one = ${'$'}one, two = ${'$'}two, three = ${'$'}three, four = ${'$'}four, five = ${'$'}five, six = ${'$'}six, seven = <private>, eight = ${'$'}eight)""${'"'}
""".trimIndent().trim(),
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.trello.mrclean

import com.google.devtools.ksp.processing.SymbolProcessor
import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
import com.google.devtools.ksp.processing.SymbolProcessorProvider

class TestMrCleanProcessorProvider(val defaultOpts: Map<String, String>) : SymbolProcessorProvider {
override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor =
MrCleanDebugProcessor(
codeGenerator = environment.codeGenerator,
options = environment.options + defaultOpts,
logger = environment.logger,
)
}
Loading

0 comments on commit 593fcc4

Please sign in to comment.