Skip to content

Commit 8abeb2c

Browse files
committed
Attach check task to lifecycle tasks depending on configuration
1 parent f9ba76e commit 8abeb2c

9 files changed

Lines changed: 133 additions & 33 deletions

File tree

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
plugins {
2-
alias(libs.plugins.jetbrains.kotlin.jvm) apply false
2+
alias(libs.plugins.kotlin.jvm) apply false
33
alias(libs.plugins.kover) apply false
44
alias(libs.plugins.maven.publish) apply false
55
}

gradle.properties

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,6 @@
1-
# Project-wide Gradle settings.
2-
# IDE (e.g. Android Studio) users:
3-
# Gradle settings configured through the IDE *will override*
4-
# any settings specified in this file.
5-
# For more details on how to configure your build environment visit
6-
# http://www.gradle.org/docs/current/userguide/build_environment.html
7-
# Specifies the JVM arguments used for the daemon process.
8-
# The setting is particularly useful for tweaking memory settings.
9-
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10-
# When configured, Gradle will run in incubating parallel mode.
11-
# This option should only be used with decoupled projects. For more details, visit
12-
# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
13-
# org.gradle.parallel=true
14-
# AndroidX package structure to make it clearer which packages are bundled with the
15-
# Android operating system, and which are packaged with your app's APK
16-
# https://developer.android.com/topic/libraries/support-library/androidx-rn
1+
org.gradle.jvmargs=-Xmx4096m -Dfile.encoding=UTF-8 -XX:MetaspaceSize=1g
172
android.useAndroidX=true
18-
# Kotlin code style for this project: "official" or "obsolete":
193
kotlin.code.style=official
20-
# Enables namespacing of each library's R class so that its R class includes only the
21-
# resources declared in the library itself and none from the library's dependencies,
22-
# thereby reducing the size of the R class for that library
234
android.nonTransitiveRClass=true
245

256
POM_NAME=ProjectGuard Plugin

gradle/libs.versions.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,12 @@ kotlin-stdlib = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib", versio
2121
runner = { group = "androidx.test", name = "runner", version.ref = "runner" }
2222
core = { group = "androidx.test", name = "core", version.ref = "core" }
2323
ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
24+
gradle-android = { group = "com.android.tools.build", name = "gradle", version.ref = "agp" }
2425

2526
[plugins]
2627
android-application = { id = "com.android.application", version.ref = "agp" }
2728
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
28-
jetbrains-kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
29+
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
2930
maven-publish = { id = "com.vanniktech.maven.publish", version = "0.36.0" }
3031
projectguard = { id = "com.rubensousa.projectguard", version = "unspecified" }
3132
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }

projectguard/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ dependencies {
4242
implementation(libs.kotlin.serialization.json)
4343
implementation(libs.jackson.yaml)
4444
implementation(libs.jackson.kotlin)
45+
implementation(libs.gradle.android)
4546
testImplementation(gradleTestKit())
4647
testImplementation(libs.kotlin.test)
4748
testImplementation(libs.truth)

projectguard/src/main/kotlin/com/rubensousa/projectguard/plugin/ProjectGuardPlugin.kt

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package com.rubensousa.projectguard.plugin
1818

19+
import com.android.build.api.variant.AndroidComponentsExtension
1920
import com.rubensousa.projectguard.plugin.internal.DependencyGraphBuilder
2021
import com.rubensousa.projectguard.plugin.internal.task.TaskAggregateDependencyDump
2122
import com.rubensousa.projectguard.plugin.internal.task.TaskAggregateRestrictionDump
@@ -53,6 +54,13 @@ class ProjectGuardPlugin : Plugin<Project> {
5354
private val dependenciesFilePath = "reports/$pluginId/dependencies.json"
5455
private val jsonReportFilePath = "reports/$pluginId/report.json"
5556
private val graphBuilder = DependencyGraphBuilder()
57+
private val androidPluginIds = listOf(
58+
"com.android.test",
59+
"com.android.application",
60+
"com.android.library",
61+
"com.android.dynamic-feature",
62+
"com.android.kotlin.multiplatform.library"
63+
)
5664

5765
override fun apply(target: Project) {
5866
val rootProject = target.rootProject
@@ -71,6 +79,8 @@ class ProjectGuardPlugin : Plugin<Project> {
7179
individualModuleTasks.add(moduleTasks)
7280
setupModuleTasks(
7381
aggregationTasks = aggregationTasks,
82+
project = targetProject,
83+
extension = extension,
7484
moduleTasks = moduleTasks
7585
)
7686
}
@@ -93,6 +103,8 @@ class ProjectGuardPlugin : Plugin<Project> {
93103
}
94104

95105
private fun setupModuleTasks(
106+
project: Project,
107+
extension: ProjectGuardExtension,
96108
aggregationTasks: AggregationTasks,
97109
moduleTasks: ModuleTasks,
98110
) {
@@ -109,6 +121,43 @@ class ProjectGuardPlugin : Plugin<Project> {
109121
outputDir.set(project.layout.buildDirectory.dir(htmlAggregateReportFilePath))
110122
reportFilePath.set(getProjectReportFilePath(project))
111123
}
124+
125+
project.afterEvaluate {
126+
val options = extension.getSpec().options
127+
options.lifecycleTask?.let { lifecycleTask ->
128+
if (lifecycleTask == LifecycleTask.ASSEMBLE) {
129+
attachToAndroidAssembleTasks(project, moduleTasks.check)
130+
project.tasks.findByName("assemble")?.dependsOn(moduleTasks.check)
131+
} else {
132+
project.tasks.findByName("check")?.dependsOn(moduleTasks.check)
133+
}
134+
}
135+
}
136+
}
137+
138+
private fun attachToAndroidAssembleTasks(
139+
project: Project,
140+
checkTask: TaskProvider<TaskCheck>,
141+
) {
142+
androidPluginIds.forEach { pluginId ->
143+
if (project.plugins.hasPlugin(pluginId)) {
144+
val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java)
145+
val variantTasks = mutableListOf<String>()
146+
androidComponents.onVariants { variant ->
147+
val variantName = capitalizeVariantName(variant.name)
148+
variantTasks.add("assemble$variantName")
149+
}
150+
project.afterEvaluate {
151+
variantTasks.forEach { variantTask ->
152+
project.tasks.findByName(variantTask)?.dependsOn(checkTask)
153+
}
154+
}
155+
}
156+
}
157+
}
158+
159+
private fun capitalizeVariantName(name: String): String {
160+
return name.substring(0, 1).uppercase() + name.substring(1, name.length)
112161
}
113162

114163
private fun setupAggregationTasks(

projectguard/src/test/kotlin/com/rubensousa/projectguard/plugin/GroovyIntegrationTest.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ class GroovyIntegrationTest {
7171
pluginRunner.addDependency(from = module, to = dependency)
7272

7373
// then
74-
pluginRunner.assertCheckFails(module)
74+
pluginRunner.assertProjectGuardCheckFails(module)
7575
pluginRunner.assertTaskOutputContains(reason)
7676
}
7777

@@ -96,7 +96,7 @@ class GroovyIntegrationTest {
9696
pluginRunner.addDependency(from = module, to = dependency)
9797

9898
// then
99-
pluginRunner.assertCheckSucceeds(module)
99+
pluginRunner.assertProjectGuardCheckSucceeds(module)
100100
pluginRunner.assertTaskOutputContains("No fatal matches found")
101101
}
102102

projectguard/src/test/kotlin/com/rubensousa/projectguard/plugin/PluginIntegrationTest.kt

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class PluginIntegrationTest {
3535
rootBuildFile = temporaryFolder.newFile("build.gradle.kts")
3636
rootBuildFile.writeText(
3737
"""
38+
import com.rubensousa.projectguard.plugin.LifecycleTask
3839
plugins {
3940
id("com.rubensousa.projectguard") apply true
4041
}
@@ -69,7 +70,7 @@ class PluginIntegrationTest {
6970
pluginRunner.addDependency(from = "libraryA", to = "libraryB")
7071

7172
// then
72-
pluginRunner.assertCheckFails("consumer")
73+
pluginRunner.assertProjectGuardCheckFails("consumer")
7374
}
7475

7576
@Test
@@ -90,7 +91,7 @@ class PluginIntegrationTest {
9091
pluginRunner.addDependency(from = "consumer", to = "library")
9192

9293
// then
93-
pluginRunner.assertCheckFails("consumer")
94+
pluginRunner.assertProjectGuardCheckFails("consumer")
9495
}
9596

9697
@Test
@@ -111,7 +112,53 @@ class PluginIntegrationTest {
111112
pluginRunner.addDependency(from = "consumer", to = "library")
112113

113114
// then
114-
pluginRunner.assertCheckSucceeds("consumer")
115+
pluginRunner.assertProjectGuardCheckSucceeds("consumer")
116+
}
117+
118+
@Test
119+
fun `assemble fails if there are restrictions and plugin is configured to attach to lifecycle task`() {
120+
pluginRunner.createModule("consumer")
121+
pluginRunner.createModule("library")
122+
123+
rootBuildFile.appendText(
124+
"""
125+
projectGuard {
126+
options {
127+
lifecycleTask = LifecycleTask.ASSEMBLE
128+
}
129+
restrictDependency(":library")
130+
}
131+
""".trimIndent()
132+
)
133+
134+
// when
135+
pluginRunner.addDependency(from = "consumer", to = "library")
136+
137+
// then
138+
pluginRunner.assertAssembleTaskFails("consumer")
139+
}
140+
141+
@Test
142+
fun `check fails if there are restrictions and plugin is configured to attach to lifecycle task`() {
143+
pluginRunner.createModule("consumer")
144+
pluginRunner.createModule("library")
145+
146+
rootBuildFile.appendText(
147+
"""
148+
projectGuard {
149+
options {
150+
lifecycleTask = LifecycleTask.CHECK
151+
}
152+
restrictDependency(":library")
153+
}
154+
""".trimIndent()
155+
)
156+
157+
// when
158+
pluginRunner.addDependency(from = "consumer", to = "library")
159+
160+
// then
161+
pluginRunner.assertCheckTaskFails("consumer")
115162
}
116163

117164
}

projectguard/src/test/kotlin/com/rubensousa/projectguard/plugin/PluginRunner.kt

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,20 +41,32 @@ class PluginRunner(
4141
settingsFile.appendText("\ninclude(\":$name\")")
4242
}
4343

44-
fun assertCheckFails(module: String) {
45-
val task = createCheckTask(module)
44+
fun assertProjectGuardCheckFails(module: String) {
45+
val task = getProjectGuardCheckTask(module)
4646
val result = gradleRunner.withArguments(task).buildAndFail()
4747
assertThat(result.task(task)!!.outcome).isEqualTo(TaskOutcome.FAILED)
4848
lastResult = result
4949
}
5050

51-
fun assertCheckSucceeds(module: String) {
52-
val task = createCheckTask(module)
51+
fun assertProjectGuardCheckSucceeds(module: String) {
52+
val task = getProjectGuardCheckTask(module)
5353
val result = gradleRunner.withArguments(task).build()
5454
assertThat(result.task(task)!!.outcome).isEqualTo(TaskOutcome.SUCCESS)
5555
lastResult = result
5656
}
5757

58+
fun assertAssembleTaskFails(module: String) {
59+
val result = gradleRunner.withArguments(":$module:assemble").buildAndFail()
60+
assertThat(result.task(getProjectGuardCheckTask(module))!!.outcome).isEqualTo(TaskOutcome.FAILED)
61+
lastResult = result
62+
}
63+
64+
fun assertCheckTaskFails(module: String) {
65+
val result = gradleRunner.withArguments(":$module:check").buildAndFail()
66+
assertThat(result.task(getProjectGuardCheckTask(module))!!.outcome).isEqualTo(TaskOutcome.FAILED)
67+
lastResult = result
68+
}
69+
5870
fun assertTaskOutputContains(message: String) {
5971
assertThat(lastResult!!.output).contains(message)
6072
}
@@ -75,7 +87,7 @@ class PluginRunner(
7587
)
7688
}
7789

78-
private fun createCheckTask(module: String): String {
90+
private fun getProjectGuardCheckTask(module: String): String {
7991
return ":$module:projectGuardCheck"
8092
}
8193

sample/build.gradle.kts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
import com.rubensousa.projectguard.plugin.LifecycleTask
2+
13
// Top-level build file where you can add configuration options common to all sub-projects/modules.
24
plugins {
35
alias(libs.plugins.android.application) apply false
46
alias(libs.plugins.kotlin.android) apply false
5-
alias(libs.plugins.jetbrains.kotlin.jvm) apply false
7+
alias(libs.plugins.kotlin.jvm) apply false
68
alias(libs.plugins.maven.publish) apply false
79
alias(libs.plugins.android.library) apply false
810
alias(libs.plugins.projectguard) apply true
@@ -12,6 +14,13 @@ plugins {
1214
}
1315

1416
projectGuard {
17+
/**
18+
* Fail the project build without running the dedicated `projectGuardCheck`
19+
*/
20+
options {
21+
lifecycleTask = LifecycleTask.ASSEMBLE
22+
}
23+
1524
report {
1625
showLibrariesInGraph = true
1726
}

0 commit comments

Comments
 (0)