Slide 1

Slide 1 text

Get Things Done with Gradle Custom Tasks Keishin Yokomaku (@KeithYokoma) Shibuya.apk #36

Slide 2

Slide 2 text

Get Things Done with Gradle Custom Tasks About Me ▸ Keishin Yokomaku ▸ @KeithYokoma: GitHub / Twitter / Qiita / Stack Overflow ▸ Merpay, Inc. / Engineer ▸ Fun: Gymnastics / Cycling / Photography / Motorsport / Camping Shibuya.apk #36 2

Slide 3

Slide 3 text

Get Things Done with Gradle Custom Tasks Daily workflow 1. Merpay: ⌨ Code and Verify 2. Merpay: Git Push 3. Merpay: Build and Deploy the artifact 4. Mercari: Update the Merpay artifact for integration Shibuya.apk #36 3

Slide 4

Slide 4 text

Get Things Done with Gradle Custom Tasks Daily workflow 1. Feature Development ▸ Merging feature branches into master 2. QA ▸ Merging bugfix branches into release/* Shibuya.apk #36 4 master release/*

Slide 5

Slide 5 text

Get Things Done with Gradle Custom Tasks Continuous Integration Shibuya.apk #36 5

Slide 6

Slide 6 text

Get Things Done with Gradle Custom Tasks Continuous Integration ▸ CircleCI 2.1 ▸ Docker: the first-class citizen ▸ Configuration as a Code ▸ Flexible workflow definitions ▸ Git triggers ▸ Nightly Shibuya.apk #36 6

Slide 7

Slide 7 text

Get Things Done with Gradle Custom Tasks Continuous Integration ▸ CircleCI 2.1 - Workflow with git triggers ▸ Mainstream: master and release/* ▸ Branches: feature / topic branches ▸ Tags: git tags Shibuya.apk #36 7

Slide 8

Slide 8 text

Get Things Done with Gradle Custom Tasks Continuous Integration ▸ CircleCI 2.1 - Nightly Workflow ▸ Protobuf update: 12:00 and 19:00 JST every weekdays ▸ Release branch merge: 12:00 and 19:00 JST every weekdays Shibuya.apk #36 8

Slide 9

Slide 9 text

Get Things Done with Gradle Custom Tasks Mainstream Workflow ▸ Install dependencies ▸ Test ▸ Assemble / Collect test coverage ▸ Deploy the artifacts Shibuya.apk #36 9

Slide 10

Slide 10 text

Get Things Done with Gradle Custom Tasks Branches Workflow ▸ Install dependencies ▸ Assemble and test debug and release variants ▸ Collect test coverage / Static code analysis Shibuya.apk #36 10

Slide 11

Slide 11 text

Get Things Done with Gradle Custom Tasks Tags Workflow ▸ Install dependencies ▸ Test ▸ Assemble ▸ Deploy ▸ Execute a Gradle Custom Task: Open a pull request in Mercari repository Shibuya.apk #36 11

Slide 12

Slide 12 text

Get Things Done with Gradle Custom Tasks Protobuf Update Workflow ▸ Execute a Gradle Custom Task: Overwrite the version with the latest one Shibuya.apk #36 12

Slide 13

Slide 13 text

Get Things Done with Gradle Custom Tasks Release Branch Merge Workflow ▸ Execute a Gradle Custom Task: git merge and push to create a pull request Shibuya.apk #36 13

Slide 14

Slide 14 text

Get Things Done with Gradle Custom Tasks Gradle Custom Tasks ▸ A step for a workflow ▸ Open a pull request in Mercari repository ▸ Overwrite the version with the latest one ▸ git merge and push to create a pull request ▸ Static code analysis result aggregation Shibuya.apk #36 14

Slide 15

Slide 15 text

Get Things Done with Gradle Custom Tasks Steps to make Gradle Custom Tasks work 1. Put a custom task implementation under buildSrc 2. Register the custom task in build.gradle of project root 3. Run the Gradle command! Shibuya.apk #36 15

Slide 16

Slide 16 text

Get Things Done with Gradle Custom Tasks Prep for buildSrc ▸ Project Root/buildSrc ▸ build.gradle.kts ▸ src ▸ main ▸ java ▸ com.your.package Shibuya.apk #36 16

Slide 17

Slide 17 text

Get Things Done with Gradle Custom Tasks Prep for buildSrc // build.gradle.kts under buildSrc directory repositories { jcenter() } plugins { `kotlin-dsl` `java-gradle-plugin` } dependencies { implementation(gradleApi()) } 17 Shibuya.apk #36

Slide 18

Slide 18 text

Get Things Done with Gradle Custom Tasks Implement your Gradle custom task in Kotlin open class MyCustomTask : DefaultTask() { @Input lateinit var parameter: String @TaskAction fun execute() { println(parameter) } } 18 Shibuya.apk #36

Slide 19

Slide 19 text

Get Things Done with Gradle Custom Tasks Class needs to be open open class MyCustomTask : DefaultTask() { @Input lateinit var parameter: String @TaskAction fun execute() { println(parameter) } } 19 Shibuya.apk #36

Slide 20

Slide 20 text

Get Things Done with Gradle Custom Tasks Extends DefaultTask open class MyCustomTask : DefaultTask() { @Input lateinit var parameter: String @TaskAction fun execute() { println(parameter) } } 20 Shibuya.apk #36

Slide 21

Slide 21 text

Get Things Done with Gradle Custom Tasks Task parameter with @Input annotation open class MyCustomTask : DefaultTask() { @Input lateinit var parameter: String @TaskAction fun execute() { println(parameter) } } 21 Shibuya.apk #36

Slide 22

Slide 22 text

Get Things Done with Gradle Custom Tasks Task body with @TaskAction open class MyCustomTask : DefaultTask() { @Input lateinit var parameter: String @TaskAction fun execute() { println(parameter) } } 22 Shibuya.apk #36

Slide 23

Slide 23 text

Get Things Done with Gradle Custom Tasks Registering the custom task // build.gradle.kts in the project root tasks.register(“myTask”) { parameter = “Hello, World!” } 23 Shibuya.apk #36

Slide 24

Slide 24 text

Get Things Done with Gradle Custom Tasks Have a name of your custom task: “myTask” // build.gradle.kts in the project root tasks.register(“myTask”) { parameter = “Hello, World!” } 24 Shibuya.apk #36

Slide 25

Slide 25 text

Get Things Done with Gradle Custom Tasks Set the parameter values // build.gradle.kts in the project root tasks.register(“myTask”) { parameter = “Hello, World!” } 25 Shibuya.apk #36

Slide 26

Slide 26 text

Get Things Done with Gradle Custom Tasks Check your task $ ./gradlew tasks …… Android Tasks ————— …… Build Tasks ————— …… BUILD SUCCESSFUL in 7s 1 actionable task: 1 executed 26 Shibuya.apk #36

Slide 27

Slide 27 text

Get Things Done with Gradle Custom Tasks Registering the custom task // build.gradle.kts in the project root tasks.register(“myTask”) { group = “Greeting” description = “Print a greeting message” parameter = “Hello, World!” } 27 Shibuya.apk #36

Slide 28

Slide 28 text

Get Things Done with Gradle Custom Tasks Check your task $ ./gradlew tasks …… Android Tasks ————— …… Greeting Tasks ————— myTask - Print a greeting message …… BUILD SUCCESSFUL in 7s 1 actionable task: 1 executed 28 Shibuya.apk #36

Slide 29

Slide 29 text

Get Things Done with Gradle Custom Tasks Run! $ ./gradlew myTask > Task: myTask Hello, World! BUILD SUCCESSFUL in 1s 1 actionable task: 1 executed 29 Shibuya.apk #36

Slide 30

Slide 30 text

Get Things Done with Gradle Custom Tasks Example: aggregateLintReport ▸ Premise: ▸ Multiple Android modules inside the project ▸ Requirements: ▸ Collect all the Lint report and unify them ▸ Usage: ▸ $ ./gradlew lint aggregateLintReport Shibuya.apk #36 30

Slide 31

Slide 31 text

Get Things Done with Gradle Custom Tasks Example: aggregateLintReport ▸ Dependencies: ▸ Writers in java.io package ▸ XML utilities in groovy.util package Shibuya.apk #36 31

Slide 32

Slide 32 text

Get Things Done with Gradle Custom Tasks Example: aggregateLintReport open class AndroidLintReportAggregation : DefaultTask() { @TaskAction fun aggregate() { // 1. Collect all Lint report files // 2. Create a file for aggregation // 3. Copy nodes in each file to aggregated file // 4. Print the aggregated file path } } 32 Shibuya.apk #36

Slide 33

Slide 33 text

Get Things Done with Gradle Custom Tasks Example: aggregateLintReport @TaskAction fun aggregate() { // 1. Collect all Lint report files val reports = project.subprojects.map { subproject -> File(“${subproject.buildDir.absolutePath}/reports”) }.map { dir -> dir.walk().find { file -> file.name == “lint-results.xml” } }.filterNotNull() } 33 Shibuya.apk #36

Slide 34

Slide 34 text

Get Things Done with Gradle Custom Tasks Example: aggregateLintReport @TaskAction fun aggregate() { // 2. Create a file for aggregation val aggregatedFile = File(“${project.rootDir}/result.xml”) if (aggregatedFile.exists().not()) { aggregatedFile.createNewFile())( } } 34 Shibuya.apk #36

Slide 35

Slide 35 text

Get Things Done with Gradle Custom Tasks Example: aggregateLintReport @TaskAction fun aggregate() { // 3. Copy nodes in each file to aggregated file val printer = XmlNodePrinter( PrintWriter(FileWriter(aggregatedFile)) ).apply { write(“\n”) } printer.isExpandEmptyElements = true } 35 Shibuya.apk #36

Slide 36

Slide 36 text

Get Things Done with Gradle Custom Tasks Example: aggregateLintReport @TaskAction fun aggregate() { // 3. Copy nodes in each file to aggregated file val rootNode = Node( null, “issues”, mapOf(“format” to “5”, “by” to “lint 3.4.0”) ) } 36 Shibuya.apk #36

Slide 37

Slide 37 text

Get Things Done with Gradle Custom Tasks Example: aggregateLintReport @TaskAction fun aggregate() { // 3. Copy nodes in each file to aggregated file val rootNode = // …… reports.forEach { report -> val elements = XmlParser().parse(report).children() if (elements.isEmpty()) { return@forEach } } } 37 Shibuya.apk #36

Slide 38

Slide 38 text

Get Things Done with Gradle Custom Tasks Example: aggregateLintReport @TaskAction fun aggregate() { // 3. Copy nodes in each file to aggregated file val rootNode = // …… reports.forEach { report -> val elements = // …… elements.map { element -> if (element !is Node) { return@map } rootNode.append(element) } } } 38 Shibuya.apk #36

Slide 39

Slide 39 text

Get Things Done with Gradle Custom Tasks Example: aggregateLintReport @TaskAction fun aggregate() { // 3. Copy nodes in each file to aggregated file val rootNode = // …… // …… printer.print(rootNode) } 39 Shibuya.apk #36

Slide 40

Slide 40 text

Get Things Done with Gradle Custom Tasks Example: aggregateLintReport @TaskAction fun aggregate() { // 4. Print the aggregated file path println(“Aggregated to ${aggregatedFile.absolutePath}”) } 40 Shibuya.apk #36

Slide 41

Slide 41 text

Get Things Done with Gradle Custom Tasks Example: aggregateLintReport - What’s good? ▸ No change on static code analysis configuration when you have a new module ▸ e.g. Danger to report the Lint issues Shibuya.apk #36 41 android_lint.report_file = “./reports.xml” android_lint.filtering = true android_lint.lint(inline_mode: true)

Slide 42

Slide 42 text

Get Things Done with Gradle Custom Tasks Steps to make Gradle Custom Tasks work 1. Put a custom task implementation under buildSrc 2. Register the custom task in build.gradle of project root 3. Run the Gradle command! Shibuya.apk #36 42

Slide 43

Slide 43 text

Get Things Done with Gradle Custom Tasks Some Discussions ▸ Is it OK to do some Network I/O? ▸ Definitely! ▸ You can enhance your task with Web APIs (e.g. GitHub API, CircleCI API) ▸ Can I use Git commands from the task? ▸ Yes! ▸ Use org.eclipse.jgit library to make it easier Shibuya.apk #36 43

Slide 44

Slide 44 text

Get Things Done with Gradle Custom Tasks Keishin Yokomaku (@KeithYokoma) Shibuya.apk #36