creating gradle plugins - gr8conf us
TRANSCRIPT
Gradle Plugin Goodness
@BRWNGRLDEV
@BRWNGRLDEV
apply plugin: 'checkstyle'apply plugin: 'findbugs'apply plugin: 'pmd'task checkstyle(type: Checkstyle) { description 'Checks if the code is somewhat acceptable' group 'verification' configFile file('./qa-check/checkstyle.xml') source 'src' include '**/*.java' exclude '**/gen/**' classpath = files() ignoreFailures = false}task findbugs(type: FindBugs) { description 'Run findbugs'
apply plugin: 'checkstyle'apply plugin: 'findbugs'apply plugin: 'pmd'task checkstyle(type: Checkstyle) { description 'Checks if the code is somewhat acceptable' group 'verification' configFile file('./qa-check/checkstyle.xml') source 'src' include '**/*.java' exclude '**/gen/**' classpath = files() ignoreFailures = false}task findbugs(type: FindBugs) { description 'Run findbugs' group 'verification' classes = files("$project.buildDir/intermediates/classes") source 'src' classpath = files() effort 'max' excludeFilter file('./qa-check/findbugs-exclude.xml') reports { xml.enabled = true html.enabled = false } ignoreFailures = true}task pmd(type: Pmd) { description 'Run PMD' group 'verification' ruleSetFiles = files("./qa-check/pmd-ruleset.xml") ruleSets = [] source 'src' include '**/*.java' exclude '**/gen/**' reports { xml.enabled = false html.enabled = true } ignoreFailures = true}
apply plugin: 'info.adavis.qualitychecks'
OVERVIEW
▸Plugin Skeleton
▸Dependencies
▸Plugin.groovy
▸CustomTask.groovy
▸Publishing
@BRWNGRLDEV
PLUGIN SKELETON@BRWNGRLDEV
PLUGIN SKELETON
@BRWNGRLDEV
PLUGIN SKELETON
@BRWNGRLDEV
PLUGIN SKELETON
How Gradle finds the Plugin Implementation
@BRWNGRLDEV
PLUGIN SKELETON
implementation-class=info.adavis.qualitychecks.QualityChecksPlugin
@BRWNGRLDEV
DEPENDENCIES@BRWNGRLDEV
DEPENDENCIES
apply plugin: ‘groovy'
dependencies {
compile gradleApi()
compile localGroovy()
}
@BRWNGRLDEV
DEPENDENCIES
dependencies {
…
testCompile 'junit:junit:4.12' testCompile ('org.spockframework:spock-core:1.0-groovy-2.4') { exclude module: 'groovy-all' }
}
@BRWNGRLDEV
THE CODE@BRWNGRLDEV
BUT FIRST…@BRWNGRLDEV
GRADLE BUILD
@BRWNGRLDEV
PROJECT
TASK TASK TASK
BUILD
GRADLE BUILD
@BRWNGRLDEV
PROJECT
TASK TASK TASK
BUILD
PROJECT
TASK TASK TASK
PLUGIN.GROOVY
class CustomPlugin implements Plugin<Project> {
}
@BRWNGRLDEV
PLUGIN.GROOVY
class CustomPlugin implements Plugin<Project> {
@Override
void apply(Project project) {
}
}
@BRWNGRLDEV
QUALITYCHECKSPLUGIN.GROOVY
@BRWNGRLDEV
APPLY METHOD
@BRWNGRLDEV
void apply(Project project) { this.project = project
}
APPLY METHOD
@BRWNGRLDEV
void apply(Project project) { this.project = project project.extensions.create('qualityChecks', QualityChecksExtension)
}
APPLY METHOD
@BRWNGRLDEV
void apply(Project project) { this.project = project project.extensions.create('qualityChecks', QualityChecksExtension) createConfigFilesIfNeeded() createConfigFileTasks() createQualityChecksTasks()}
PROJECT EXTENSION
@BRWNGRLDEV
class QualityChecksExtension { String pmdConfigFile = 'quality-checks/pmd-ruleset.xml' String checkstyleConfigFile = 'quality-checks/checkstyle.xml' String findBugsExclusionFile = 'quality-checks/findbugs-exclude.xml'}
PROJECT EXTENSION
@BRWNGRLDEV
class QualityChecksExtension { String pmdConfigFile = 'quality-checks/pmd-ruleset.xml' String checkstyleConfigFile = 'quality-checks/checkstyle.xml' String findBugsExclusionFile = 'quality-checks/findbugs-exclude.xml'}
PROJECT EXTENSION
Back in the application’s build.gradle file…
@BRWNGRLDEV
qualityChecks { pmdConfigFile = ‘checks/pmd.xml’
checkstyleConfigFile = ‘checks/checkstyle.xml’ }
PROJECT EXTENSION
Back in the application’s build.gradle file…
@BRWNGRLDEV
qualityChecks { pmdConfigFile = ‘checks/pmd.xml’
checkstyleConfigFile = ‘checks/checkstyle.xml’ }
TASKS@BRWNGRLDEV
CREATING TASKS
Give it a name and a type
@BRWNGRLDEV
CREATING TASKS
@BRWNGRLDEV
▸Build on existing task
▸Extend the DefaultTask
BUILD ON EXISTING TASK
@BRWNGRLDEV
BUILD ON EXISTING
@BRWNGRLDEV
EXTEND DEFAULT TASK
@BRWNGRLDEV
CUSTOMTASK.GROOVY
class CustomTask extends DefaultTask {
CustomTask() { group: ‘verification’
}
}
@BRWNGRLDEV
CUSTOMTASK.GROOVY
class CustomTask extends DefaultTask {
CustomTask() { group: ‘verification’
}
}
@BRWNGRLDEV
CUSTOMTASK.GROOVY
CustomTask() { group: ‘verification’
onlyIf { // skip under certain conditions }
}
@BRWNGRLDEV
CUSTOMTASK.GROOVY
@TaskAction def defaultAction() {
description: ‘What my task does’
}
@BRWNGRLDEV
CUSTOMTASK.GROOVY
@BRWNGRLDEV
@TaskAction def defaultAction() {
description: ‘What my task does’
<do your cool stuff here> }
GRADLE TASK DOCUMENTATION
@BRWNGRLDEV
SO FAR…
▸Plugin Skeleton
▸Dependencies
▸Plugin.groovy
▸CustomTask.groovy
@BRWNGRLDEV
PUBLISHING@BRWNGRLDEV
PUBLISHING
@BRWNGRLDEV
PUBLISHING
buildscript { …
dependencies { classpath "com.gradle.publish:plugin-publish-plugin:0.9.4" } }
apply plugin: 'com.gradle.plugin-publish'
@BRWNGRLDEV
PUBLISHING
version = "0.1.3" group = "info.adavis"
@BRWNGRLDEV
PUBLISHING
pluginBundle { website = 'https://github.com/adavis/quality-checks' vcsUrl = 'https://github.com/adavis/quality-checks.git' description = 'Gradle Plugin for…’ tags = ['Checkstyle', 'FindBugs', 'PMD']
}
@BRWNGRLDEV
PUBLISHING
pluginBundle { …
plugins { qualityChecksPlugin { id = 'info.adavis.qualitychecks' displayName = 'Quality Checks Plugin' } }
@BRWNGRLDEV
@BRWNGRLDEV
@BRWNGRLDEV
WE’RE DONE…
WRONG!@BRWNGRLDEV
WE’RE DONE…
TESTS@BRWNGRLDEV
TESTING - WITH JUNIT
@Beforevoid setUp() { projectDir = temporaryFolder.root projectDir.mkdirs()
}
@BRWNGRLDEV
TESTING - WITH JUNIT
@Beforevoid setUp() { projectDir = temporaryFolder.root projectDir.mkdirs() project = ProjectBuilder.builder().withProjectDir(projectDir).build() task = project.tasks.create('writeConfigFile', WriteConfigFileTask)}
@BRWNGRLDEV
TESTING - WITH JUNIT
@Test void shouldBeAbleToCreateTask() { assertTrue(task instanceof WriteConfigFileTask) }
@BRWNGRLDEV
TESTING - WITH JUNIT
@Test void pluginShouldBeApplied() { project.apply(plugin: QualityChecksPlugin)
assertNotNull(project.tasks.findByName(‘mytask’)) }
@BRWNGRLDEV
SPOCK@BRWNGRLDEV
TESTING - WITH SPOCK
def createCheckstyleTask() { given: "we have a project" def project = ProjectBuilder.builder().build()
}
TESTING - WITH SPOCKdef createCheckstyleTask() { and: "we apply the extension" project.extensions.create('qualityChecks', QualityChecksExtension) and: "we supply an existing checkstyle config file" project.qualityChecks.checkstyleConfigFile = File.createTempFile('temp', '.xml').path
}
TESTING - WITH SPOCK
def createCheckstyleTask() {
when: "we create a checkstyle task" def checkstyleTask = project.tasks.create('checkstyle', CheckstyleTask)
}
TESTING - WITH SPOCKdef createCheckstyleTask() {
then: "it should not replace our previous file" checkstyleTask.configFile.name.startsWith('temp')}
TESTING - WITH SPOCK
@BRWNGRLDEV
TESTING - REPORT
@BRWNGRLDEV
TESTING - SPOCK-REPORT
dependencies {
…
testCompile( 'com.athaydes:spock-reports:1.2.12' ) { transitive = false }
}
@BRWNGRLDEV
TESTING - SPOCK-REPORT
@BRWNGRLDEV
@BRWNGRLDEV
BONUS: README
@BRWNGRLDEV
BONUS: README
@BRWNGRLDEV
BONUS: README
@BRWNGRLDEV
SUMMARY
▸Helps avoid copy/paste horror
▸Simple project structure
▸Extending DefaultTask
▸Testing techniques
▸Easy to publish
@BRWNGRLDEV