Skip to content

Commit

Permalink
Merge pull request #6 from simonschiller/feature/task-configuration
Browse files Browse the repository at this point in the history
Allow configuration of tasks
  • Loading branch information
simonschiller authored Jul 1, 2020
2 parents 706d60d + 7477806 commit d2c142a
Show file tree
Hide file tree
Showing 8 changed files with 127 additions and 12 deletions.
20 changes: 17 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,29 @@ When you deliberately want to add new permissions to the app, you need to recrea

#### Strict mode

By default, PermissionCheck only reports issues that would cause the app to require more permissions than specified in the baseline. In case a permission is removed or the max SDK of a permission is decreased, the tasks would not fail. To also detect these cases, you can use the `--strict` command line option.
By default, PermissionCheck only reports issues that would cause the app to require more permissions than specified in the baseline. In case a permission is removed or the max SDK of a permission is decreased, the tasks would not fail. To also detect these cases, you can enable strict mode in the task configuration (see next section) or use the `--strict` command line option.

#### Configuring the tasks

For greater customization, you can change the default behaviour of the tasks using the Gradle DSL.

```groovy
permissionCheck {
// Location of the baseline file, defaults to "$projectDir/permission-baseline.xml"
baselineFile.set(layout.projectDirectory.file("baselines/permissions.xml"))
// Always perform strict checking, defaults to false
strict.set(true)
}
```

## Adding the plugin to your project

To add the PermissionCheck plugin to your project, you have to add this block of code to your `build.gradle`.

```groovy
plugins {
id "io.github.simonschiller.permissioncheck" version "1.1.0"
id "io.github.simonschiller.permissioncheck" version "1.2.0"
}
```

Expand All @@ -40,7 +54,7 @@ buildscript {
}
}
dependencies {
classpath "io.github.simonschiller:permissioncheck:1.1.0"
classpath "io.github.simonschiller:permissioncheck:1.2.0"
}
}
```
Expand Down
2 changes: 1 addition & 1 deletion buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ plugins {
}

group = "io.github.simonschiller"
version = "1.1.0" // Also update the version in the README
version = "1.2.0" // Also update the version in the README

repositories {
google()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package io.github.simonschiller.permissioncheck

import org.gradle.api.file.ProjectLayout
import org.gradle.api.file.RegularFile
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.Property
import org.gradle.kotlin.dsl.property

@Suppress("UnstableApiUsage")
open class PermissionCheckExtension(objects: ObjectFactory, layout: ProjectLayout) {

/** Location of the baseline file, should be somewhere in your project directory. */
val baselineFile: Property<RegularFile> = objects.fileProperty()

/** When enabled, removed permissions and decreased max SDK versions will also be detected. */
val strict: Property<Boolean> = objects.property()

// Setup default values
init {
baselineFile.convention(layout.projectDirectory.file("permission-baseline.xml"))
strict.convention(false)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ import org.gradle.api.provider.Provider
import org.gradle.language.base.plugins.LifecycleBasePlugin
import java.util.*

@Suppress("UnstableApiUsage")
class PermissionCheckPlugin : Plugin<Project> {

override fun apply(project: Project) {
val extension = project.extensions.create("permissionCheck", PermissionCheckExtension::class.java)

// Register tasks once the Android app plugin is available
project.plugins.configureEach {
Expand All @@ -23,23 +25,23 @@ class PermissionCheckPlugin : Plugin<Project> {

val appExtension = project.extensions.getByType(AppExtension::class.java)
appExtension.applicationVariants.configureEach {
registerTask(project, this)
registerTask(project, this, extension)
}
}
}

private fun registerTask(project: Project, variant: ApplicationVariant) {
private fun registerTask(project: Project, variant: ApplicationVariant, extension: PermissionCheckExtension) {
val taskName = "check${variant.name.capitalize(Locale.ROOT)}Permissions"

val task = project.tasks.register(taskName, PermissionCheckTask::class.java) {
group = LifecycleBasePlugin.VERIFICATION_GROUP
description = "Checks ${variant.name} permissions for regressions"

variantName.set(variant.name)
mergedManifest.set(getMergedManifestFile(variant)) // Takes care of task dependencies
baseline.set(project.layout.projectDirectory.file("permission-baseline.xml"))
mergedManifest.set(getMergedManifestFile(variant)) // Takes care of task dependencies automatically
baseline.set(extension.baselineFile)
recreate.set(false)
strict.set(false)
strict.set(extension.strict)
}

// Execute the task as part of the standard Gradle check task
Expand All @@ -51,7 +53,8 @@ class PermissionCheckPlugin : Plugin<Project> {
// Extracts the location of the merged manifest
private fun getMergedManifestFile(variant: ApplicationVariant): Provider<RegularFile> {
val output = variant.outputs.single { it.outputType == VariantOutput.MAIN }
val outputDirectory = output.processManifestProvider.get().manifestOutputDirectory
return outputDirectory.file("AndroidManifest.xml")
return output.processManifestProvider.flatMap { task ->
task.manifestOutputDirectory.file("AndroidManifest.xml")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ class PermissionCheckTaskIntegrationTest {
}

@Test
fun `Task fails if there are strict mode violations and strict mode is enabled`() {
fun `Task fails if there are strict mode violations and strict mode is enabled via command line`() {
baselineFile.writeText("""
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<baseline>
Expand All @@ -174,6 +174,36 @@ class PermissionCheckTaskIntegrationTest {
assertTrue(buildResult.output.contains("Found 2 violation(s)"))
}

@Test
fun `Task fails if there are strict mode violations and strict mode is enabled via configuration`() {
val buildGradleFile = androidProject.rootDir.resolve("build.gradle")
buildGradleFile.appendText("""
permissionCheck {
strict.set(true)
}
""".trimIndent())

baselineFile.writeText("""
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<baseline>
<variant name="debug">
<uses-permission name="android.permission.INTERNET"/>
<uses-permission name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission maxSdkVersion="24" name="android.permission.CAMERA"/>
<uses-permission-sdk-23 name="android.permission.ACCESS_NETWORK_STATE"/>
</variant>
</baseline>
""".trimIndent())

val buildResult = GradleRunner.create()
.withProjectDir(androidProject.rootDir)
.withPluginClasspath()
.withArguments("checkDebugPermissions")
.buildAndFail()

assertTrue(buildResult.output.contains("Found 2 violation(s)"))
}

@Test
fun `All variants are checked as part of the default check task`() {
baselineFile.writeText("""
Expand Down Expand Up @@ -201,4 +231,43 @@ class PermissionCheckTaskIntegrationTest {
assertTrue(buildResult.tasks.any { it.path == ":checkDebugPermissions" })
assertTrue(buildResult.tasks.any { it.path == ":checkReleasePermissions" })
}

@Test
fun `Configuring a different baseline location works correctly` () {
val buildGradleFile = androidProject.rootDir.resolve("build.gradle")
buildGradleFile.appendText("""
permissionCheck {
baselineFile.set(layout.projectDirectory.file("baselines/test-baseline.xml"))
}
""".trimIndent())

val buildResult = GradleRunner.create()
.withProjectDir(androidProject.rootDir)
.withPluginClasspath()
.withArguments("checkDebugPermissions")
.buildAndFail()

val baselineFile = androidProject.rootDir.resolve("baselines").resolve("test-baseline.xml")
assertTrue(buildResult.output.contains(baselineFile.path))
assertTrue(baselineFile.exists())
assertFalse(androidProject.rootDir.resolve("permission-baseline.xml").exists())
}

@Test
fun `Task fails if the specified baseline is not an XML file`() {
val buildGradleFile = androidProject.rootDir.resolve("build.gradle")
buildGradleFile.appendText("""
permissionCheck {
baselineFile.set(layout.projectDirectory.file("invalid-baseline.json"))
}
""".trimIndent())

val buildResult = GradleRunner.create()
.withProjectDir(androidProject.rootDir)
.withPluginClasspath()
.withArguments("checkDebugPermissions")
.buildAndFail()

assertTrue(buildResult.output.contains("The permission baseline has to be a .xml file"))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ class AndroidProjectExtension : BeforeEachCallback, AfterEachCallback {
check("")
}
}
""".trimIndent())
}

Expand Down
5 changes: 5 additions & 0 deletions sample/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,8 @@ android {
dependencies {
implementation(project(":sample:library"))
}

permissionCheck {
baselineFile.set(layout.projectDirectory.file("sample-baseline.xml"))
strict.set(true)
}
File renamed without changes.

0 comments on commit d2c142a

Please sign in to comment.