Gradle Plugin Best Practices by Example

74
Unless otherwise indicated, these slides are © 2013-2015 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ SPRINGONE2GX WASHINGTON, DC Gradle plugin best practices by example By Benjamin Muschko Gradle Inc.

Transcript of Gradle Plugin Best Practices by Example

Page 1: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

SPRINGONE2GXWASHINGTON, DC

Gradle plugin best practices by example

By Benjamin Muschko Gradle Inc.

Page 2: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

The dark past of build logic

2

Unstructured spaghetti code

Copy & paste of code snippets

The build tool tells you how to structure code

Build can only be understood by build guru™

Testing through manual execution

Page 3: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Build logic “-ilities”

Typical non-functional software requirements also apply to build code…

• Reusability

• Testability

• Maintainability

• Extensibility

• Configurability

3

Page 4: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Reusability

Avoid copy/paste! Allow code to be shared among independent projects.

4

Page 5: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Testability

Build code is no different from application code. Test it on multiple levels!

5

Page 6: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Maintainability

Avoid the big ball of mud! Cohesion and separation of concerns are important.

6

Page 7: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Extensibility

Extend Gradle's build language by your own declarative & expressive language constructs.

7

Page 8: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Configurability

Don't box in your users! Implement convention over configuration with ease.

8

Page 9: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

The end goal

9

Maintainable, reusable & tested code

Consumers only configure the code

Complex implement. details are hidden

Page 10: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

How to get there?

Concepts that help implement these requirements…

• Good software engineering practices

• Custom tasks

• Plugins

• Gradle's extension mechanisms

• Testing capabilities

10

Page 11: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Techniques covered in talk

• Declarative vs. imperative logic

• Convention over configuration

• Capabilities vs. conventions

• Gradle's extension mechanisms

• Testing plugin logic

• Publishing the plugin artifacts

• Writing and generating documentation

• Setting up Continuous Integration/Delivery

11

Page 12: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Case study: Gradle Docker plugin

Serves as showcase plugin project.

• Plugin for managing Docker images and containers through Docker remote API

• API communication via Docker Java library

• Written in Groovy

• Source code available on GitHub (https://github.com/bmuschko/gradle-docker-plugin)

12

Page 13: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Always check in the Wrapper

Project becomes instantly buildable for every developer and on the CI machine.

13

Check in Wrapper files into VCS repository.

Page 14: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Hiding complex, imperative logic

Concept applies to tasks and plugins.

• Reusable and configurable

• Easy to structure, refactor and test

• Avoid global properties and methods

14

Page 15: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Task type > ad-hoc task

Prefer implementing task types to implementing ad-hoc tasks.

15

task createDockerfile(type: Dockerfile) { destFile = file("$buildDir/mydockerfile/Dockerfile") from 'ubuntu:12.04' maintainer 'Benjamin Muschko "[email protected]"' }

task buildImage(type: DockerBuildImage) { dependsOn createDockerfile inputDir = createDockerfile.destFile.parentFile tag = 'bmuschko/myimage' }

Page 16: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Binary plugin > script plugin

Use script plugins to organize build logic based on functional boundaries in project.

16

apply from: "$rootDir/gradle/dependencies.gradle" apply from: "$rootDir/gradle/test-setup.gradle" apply from: "$rootDir/gradle/integration-test.gradle" apply from: "$rootDir/gradle/functional-test.gradle" apply from: "$rootDir/gradle/additional-artifacts.gradle" apply from: "$rootDir/gradle/publishing.gradle" apply from: "$rootDir/gradle/documentation.gradle" apply from: "$rootDir/gradle/release.gradle"

Page 17: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Convention over configuration

Provide sensible defaults and standards. Expose a way to re-configure them to user’s needs.

Examples:

• src/main/java for Java-based applications

• Project name derived from directory name

• Output directory is build

17

Page 18: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Convention

Plugin consumers will be most comfortable with using convention plugins. Pick sensible defaults for the user.

Good indicator:

The less a consumer has to re-configure defaults the better.

18

Page 19: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Configuration

Real-world project might need to re-configure the defaults. Make it convenient to change defaults.

Reason:

Their view of the world might look different than yours.

19

Page 20: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Users only declare the “what”

Expose a custom DSL from your binary plugin to configure runtime behavior. The “how” is the responsibility of the plugin implementation.

20

apply plugin: 'com.bmuschko.docker-remote-api'

docker { url = 'https://192.168.59.103:2376' certPath = new File(System.properties['user.home'], '.boot2docker/certs/boot2docker-vm') }

Page 21: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Example: Providing default dependencies

Plugins often rely on external libraries. Automatically resolve default version, but make it configurable.

21

Page 22: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Customizing Docker Java library

Introduce custom configuration in plugin.

22

project.configurations.create('docker') .setVisible(false) .setTransitive(true) .setDescription('The Docker Java libraries

to be used.') }

Setting dependency from consuming build.

dependencies { docker 'com.github.docker-

java:docker-java:2.0.0' }

Page 23: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Providing default (but customizable) dependency

Only use defaults, if no dependencies are assigned to custom configuration.

23

Configuration config = project.configurations.create('docker')

project.tasks.withType(AbstractDockerRemoteApiTask) { config.defaultDependencies { deps -> deps.add(project.dependencies .create('com.github.docker-java:docker-java:2.1.0')) deps.add(project.dependencies .create('org.slf4j:slf4j-simple:1.7.5')) } conventionMapping.classpath = { config } } }

Page 24: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Capabilities vs. conventions

Separating general-purpose functionality from pre-configured, opinionated functionality.

Finding the right balance between both aspects is key.

24

Page 25: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Capabilities

• Un-opinionated functionality

• Provide general-purpose concepts

Examples:

• Custom task types without creating an actual task instance

• Add new concepts without configuring them (e.g. source sets, build types and flavors)

25

Page 26: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Conventions

• Opinionated functionality

• Instantiate and/or pre-configure concepts

Examples:

• Standardized directory layouts

• Enhanced tasks created by a plugin

• Adding default values to extensions

• Declaring task dependencies to form a lifecycle

26

Page 27: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Plugin composition

Plugins can build upon other plugins. This is a common pattern.

27

A base plugin provides generic capabilities

Another plugin builds on the base, adding opinionated conventions

Page 28: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Example: Docker plugins

Allows users to pick functionality they need in their projects.

28

Plugin that adds task types for interacting with Docker remote API

Plugin for creating & pushing Docker image for Java applications

Page 29: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Example: Docker Java application plugin

Applying plugin by identifier or type.

29

import org.gradle.api.Plugin import org.gradle.api.Project

class DockerJavaApplicationPlugin implements Plugin<Project> { @Override void apply(Project project) { project.apply(plugin: DockerRemoteApiPlugin) } }

Page 30: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Build for further extension

• Anticipate more specific extensions to your plugin

• Implemented through plugin composition

• Base plugin act as enablers for custom convention plugins

• Enterprise build convention plugins re-configure your plugins

30

Page 31: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Put yourself in the shoes of consumer

Do not automatically apply plugins in your plugin!

Reason:

• Makes your plugin highly opinionated.

31

Page 32: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Reacting to plugins

React to plugins that might be applied in consuming build script. Only then logic is executed.

The same concept applies to tasks.

32

Page 33: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Using configuration rules

33

project.plugins.withType(IdeaPlugin) { // configure Idea plugin in the

context of your plugin }

Reacting to task types

project.tasks.withType(War) { // configure all War tasks in the

context of your plugin }

Reacting to plugins

Page 34: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Testing build logic

34

Build logic needs to be testable on multiple levels.

Page 35: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Implementing test types with Gradle

35

Unit testing:

No Gradle tooling needed

Integration testing:

Use ProjectBuilder to create pseudo Project instance

Functional testing:

Nebula Test, Gradle TestKit

Page 36: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Example: Writing unit test with Spock

36

package com.bmuschko.gradle.docker.tasks.image import spock.lang.Specification import static com.bmuschko.gradle.docker.tasks.image.Dockerfile.*

class DockerfileTest extends Specification { def "Instruction String representation is built correctly"() { expect: instructionInstance.keyword == keyword instructionInstance.build() == builtInstruction where: instructionInstance | keyword | builtInstruction new FromInstruction('ubuntu:14.04') | 'FROM' | 'FROM ubuntu:14.04' new FromInstruction({ 'ubuntu:14.04' }) | 'FROM' | 'FROM ubuntu:14.04' } }

Page 37: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Separating test type source code

37

Default unit test directory

Custom integration test directory

Custom functional test directory

Page 38: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Example: Integration test source set and task

38

sourceSets { integrationTest { groovy.srcDir file('src/integTest/groovy') resources.srcDir file('src/integTest/resources') compileClasspath += sourceSets.main.output + configurations.testRuntime runtimeClasspath += output + compileClasspath } }

task integrationTest(type: Test) { description = 'Runs the integration tests.' group = 'verification' testClassesDir = sourceSets.integrationTest.output.classesDir classpath = sourceSets.integrationTest.runtimeClasspath mustRunAfter test }

Page 39: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Example: Writing integration test with Spock

39

import org.gradle.api.Project import org.gradle.testfixtures.ProjectBuilder

class DockerJavaApplicationPluginIntegrationTest extends Specification { @Rule TemporaryFolder temporaryFolder = new TemporaryFolder() Project project = ProjectBuilder.builder().withProjectDir(temporaryFolder.root).build()

def "Creates tasks out-of-the-box when application plugin is applied"() { when: project.apply(plugin: DockerJavaApplicationPlugin) project.apply(plugin: 'application') then: project.tasks.findByName(DockerJavaApplicationPlugin.DOCKERFILE_TASK_NAME) } }

Page 40: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Functional testing

40

Testing the build logic from the end user’s perspective.

• Exercising build logic as part of a programmatically executed build.

• Make assertions about build outcome.

Page 41: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Using the Gradle TestKit

41

Functional testing support in Gradle core.

• Uses Tooling API as test execution harness.

• Agnostic of test framework.

• Assertions made based on build output, build logging or test of tasks + their result.

Page 42: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

TestKit usage

42

Declaring TestKit dependency

Declaring the Spock dependency

dependencies { testCompile gradleTestKit() }

dependencies { testCompile

'org.spockframework:spock-core:1.0-groovy-2.3'

}

Page 43: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Example: Writing functional tests with TestKit

43

def "can successfully create Dockerfile"() { given: buildFile << """ import com.bmuschko.gradle.docker.tasks.image.Dockerfile task dockerfile(type: Dockerfile) { from 'ubuntu:14.04' maintainer 'Benjamin Muschko "[email protected]"' } “”"

when: def result = GradleRunner.create().withProjectDir(testProjectDir.root).withArguments('dockerfile').build() then: result.task(":dockerfile").outcome == SUCCESS testProjectDir.file('Dockerfile').exists() }

Page 44: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

DEMO

44

Functional testing with TestKit

Page 45: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Roadmap for TestKit

45

Gradle 2.6

Mechanics for executing tests

Functional tests can query build result

Gradle 2.7

Isolation of test environment, dedicated daemons

Gradle 2.8

Convenient injection of code under test

Page 46: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Cross-version compatibility tests

46

Forward and backward compatibility independent of Gradle version used to build plugin artifact.

2.4 2.5 2.62.32.2

 Version  used  to  build  plugin

Page 47: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Implementing compatibility tests

47

No built-in support in Gradle yet. On the roadmap for TestKit.

Intermediate options:

Custom implementation using Tooling API.

Community plugins like https://github.com/ysb33r/gradleTest

Page 48: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Importance of documentation

48

Plugins are not self-documenting.

Thorough documentation is crucial for plugin consumers.

Page 49: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Answer questions for consumers

49

How can I use the plugin and configure it?

What tasks & extensions are provided?

What impact does the plugin have on my project?

Page 50: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Impact on consumers

50

De-mystify added functionality.

Give consumers the feeling that they are under control.

Page 51: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

What should be documented?

51

• Purpose of plugin, the repository that hosts the plugin

• Plugins: Identifier, type and description bundled in a JAR

• Enhanced tasks: their name, type and dependencies

• Custom task types: Javadocs, Groovydocs

• Conventions, custom extensions (e.g. DSLs)

• Extension properties/methods

• Usage examples in textual form

• A good set of functional tests that demonstrate the use of the plugin

Page 52: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Example: Plugin description & usage

52

Page 53: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Example: Extension usage and properties

53

Page 54: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Example: Textual usage & functional tests

54

Page 55: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Example: Linking custom tasks to API docs

55

Page 56: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Example: Publishing API docs to GitHub pages

56

Javadocs/Groovydocs are essential to allow users discover API classes exposed by plugin.

• Create new branch gh-pages

• Remove all files from working tree and index

• Push new branch

Available under

http(s)://<username>.github.io/<projectname>

Page 57: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Example: Publishing Javadocs to GitHub pages

57

Use gradle-git plugin to push to automatically publish Javadocs.

apply plugin: 'org.ajoberstar.github-pages'

githubPages { repoUri = '[email protected]:bmuschko/gradle-docker-plugin.git' pages { from(javadoc.outputs.files) { into 'docs/javadoc' } } }

Page 58: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

DEMO

58

Publishing changed API documentation

Page 59: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Publishing the plugin binaries

59

Make artifact(s) available to consumers by uploading them to one or many binary repositories.

Page 60: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Provide appropriate metadata

60

apply plugin: 'maven-publish'

publishing { publications { mavenJava(MavenPublication) { pom.withXml { def root = asNode() root.appendNode('name', 'Gradle Docker plugin') root.appendNode('description', 'Gradle plugin for managing Docker images & containers.') root.appendNode('url', 'https://github.com/bmuschko/gradle-docker-plugin') root.appendNode('inceptionYear', '2014') } } } }

Page 61: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Publishing doesn’t stop with plugin JAR

61

task sourcesJar(type: Jar) { classifier 'sources' from sourceSets.main.allSource }

task groovydocJar(type: Jar, dependsOn: groovydoc) { classifier 'groovydoc' from groovydoc.destinationDir }

task javadocJar(type: Jar, dependsOn: javadoc) { classifier 'javadoc' from javadoc.destinationDir }

Page 62: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Share Open Source plugins on the portal

62

https://plugins.gradle.org/

Page 63: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Publishing to the Gradle plugin portal

63

Requires use of the “Plugin Publishing Plugin”buildscript { repositories { maven { url 'https://plugins.gradle.org/m2/' } } dependencies { classpath 'com.gradle.publish:plugin-publish-plugin:0.9.1' } }

apply plugin: 'com.gradle.plugin-publish'

Page 64: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Declaring required portal plugin metadata

64

Requires use of the “Plugin Publishing Plugin”pluginBundle { website = 'https://github.com/bmuschko/gradle-docker-plugin' vcsUrl = 'https://github.com/bmuschko/gradle-docker-plugin.git' description = 'Gradle plugin for managing Docker images and containers.' tags = ['gradle', 'docker', 'container', 'image', 'lightweight', 'vm', 'linux'] plugins { dockerRemoteApi { id = 'com.bmuschko.docker-remote-api' displayName = 'Provides custom tasks for interacting with Docker remote API.' } } }

Page 65: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Continuous Integration

65

A plugin might start small but will grow in complexity over time. Avoid integration hell!

• Fast feedback with every code integration

• Always execute (an assorted set of) tests

• Use CI cloud-based product or host in-house

Page 66: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Adding a CI badge

66

Give consumers and contributor a chance to see the build status.

Page 67: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Working towards Continuous Delivery

67

Make sure your plugin is always production ready.

• End goal: avoid manual steps

• Build and visualize suitable delivery pipeline

• Go the extra mile - model push-button release capabilities

Page 68: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Example: Potential release steps

68

 PubliAssemble artifact(s)

  Tag VCS repository

   Publish artifact(s)

Plugin JAR Sources JAR

Docs JAR

Create tag Push tag

Create metadata Upload artifact(s) Update API docs

Page 69: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

DEMO

69

Implementing a plugin release process

Page 70: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Example: Build pipeline with Snap CI

70

Build, test and deploy in the cloud.

• Free of charge for public Github repos

• Model build pipelines with automatic and/or manual execution steps

• Execute build for pull requests

Page 71: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Example: Snap CI pipeline for Docker plugin

71

Compilation      Unit  tests

Integration            tests

Functional            tests

Release

https://snap-ci.com/bmuschko/gradle-docker-plugin/branch/master

Page 72: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

DEMO

72

Showcasing build pipeline on Snap CI

Page 73: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Other aspects

73

• Be mindful of the performance impact your plugin might have on consuming build

• Avoid using internal Gradle APIs as much as possible

Page 74: Gradle Plugin Best Practices by Example

Unless otherwise indicated, these s l ides are © 2013-2015 Pivotal Software, Inc. and l icensed under a Creat ive Commons Attr ibut ion-NonCommercial l icense: ht tp: / /creat ivecommons.org/ l icenses/by-nc/3.0/

Thank You!

74

Please ask questions…

https://www.github.com/bmuschko

@bmuschko