$30 off During Our Annual Pro Sale. View Details »

Get Things Done with Gradle Custom Tasks

Get Things Done with Gradle Custom Tasks

Get the job done automatically with Gradle Custom Tasks on the CI.

Keishin Yokomaku

August 01, 2019
Tweet

More Decks by Keishin Yokomaku

Other Decks in Technology

Transcript

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

    View Slide

  2. 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

    View Slide

  3. 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

    View Slide

  4. 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/*

    View Slide

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

    View Slide

  6. 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

    View Slide

  7. 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

    View Slide

  8. 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

    View Slide

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

    View Slide

  10. 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

    View Slide

  11. 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

    View Slide

  12. 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

    View Slide

  13. 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

    View Slide

  14. 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

    View Slide

  15. 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

    View Slide

  16. 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

    View Slide

  17. 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

    View Slide

  18. 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

    View Slide

  19. 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

    View Slide

  20. 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

    View Slide

  21. 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

    View Slide

  22. 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

    View Slide

  23. 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

    View Slide

  24. 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

    View Slide

  25. 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

    View Slide

  26. 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

    View Slide

  27. 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

    View Slide

  28. 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

    View Slide

  29. 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

    View Slide

  30. 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

    View Slide

  31. 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

    View Slide

  32. 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

    View Slide

  33. 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

    View Slide

  34. 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

    View Slide

  35. 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

    View Slide

  36. 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

    View Slide

  37. 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

    View Slide

  38. 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

    View Slide

  39. 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

    View Slide

  40. 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

    View Slide

  41. 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)

    View Slide

  42. 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

    View Slide

  43. 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

    View Slide

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

    View Slide