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

Kotlin で書く Gradle Custom Tasks

Kotlin で書く Gradle Custom Tasks

Keishin Yokomaku

November 27, 2019
Tweet

More Decks by Keishin Yokomaku

Other Decks in Programming

Transcript

  1. Kotlin Ͱॻ͘

    Gradle Custom Tasks
    KeithYokoma (Keishin Yokomaku) / shuuu-mai

    View Slide

  2. Kotlin Ͱॻ͘ Gradle Custom Tasks
    About Me
    ▸ Keishin Yokomaku
    ▸ @KeithYokoma: GitHub / Twitter / Qiita / Stack Overflow
    ▸ Merpay, Inc. / Engineer
    ▸ Fun: Gymnastics / Cycling / Photography / Motorsport / Camping
    shuuu-mai
    2

    View Slide

  3. Kotlin Ͱॻ͘ Gradle Custom Tasks
    Gradle Tasks
    3
    $ ./gradlew assembleDebug testDebugUnitTest ktlintDebug
    shuuu-mai

    View Slide

  4. Kotlin Ͱॻ͘ Gradle Custom Tasks
    Gradle Tasks
    4
    $ ./gradlew assembleDebug testDebugUnitTest ktlintDebug
    shuuu-mai

    View Slide

  5. Kotlin Ͱॻ͘ Gradle Custom Tasks
    Gradle Tasks
    5
    $ ./gradlew assembleDebug testDebugUnitTest ktlintDebug
    main sourceSet Λ debug variant ͰίϯύΠϧ͠ apk Λੜ੒͢Δ
    shuuu-mai

    View Slide

  6. Kotlin Ͱॻ͘ Gradle Custom Tasks
    Gradle Tasks
    6
    $ ./gradlew assembleDebug testDebugUnitTest ktlintDebug
    test sourceSet ʹ͋Δ Unit Test Λ debug variant Ͱ࣮ߦ͢Δ
    shuuu-mai

    View Slide

  7. Kotlin Ͱॻ͘ Gradle Custom Tasks
    Gradle Tasks
    7
    $ ./gradlew assembleDebug testDebugUnitTest ktlintDebug
    ktlint Λ debug variant Ͱ࣮ߦ͢Δ
    shuuu-mai

    View Slide

  8. Kotlin Ͱॻ͘ Gradle Custom Tasks
    General use of Gradle Tasks
    ▸ Build setup
    ▸ clean, init, etc…
    ▸ Compile the source and generate artifacts
    ▸ assemble, jar, etc…
    ▸ Verification
    ▸ test, lint, etc…
    8
    shuuu-mai

    View Slide

  9. Kotlin Ͱॻ͘ Gradle Custom Tasks
    What we can do with Gradle Custom Tasks?
    ▸ Whatever you want!
    ▸ Access to Web APIs
    ▸ Git operations
    ▸ File I/O
    9
    shuuu-mai

    View Slide

  10. Kotlin Ͱॻ͘ Gradle Custom Tasks
    What we can do with Gradle Custom Tasks?
    ▸ Whatever you want!
    ▸ Access to Web APIs
    ▸ Git operations
    ▸ File I/O
    10
    shuuu-mai
    = Automation!

    View Slide

  11. Kotlin Ͱॻ͘ Gradle Custom Tasks
    Case Study: What we do with Gradle Custom Tasks
    1. Keep the master up-to-date with the latest release branch
    ‣ Daily feature development: master
    ‣ Bug-fixes: release/*
    ‣ release/* may conflict with master…
    shuuu-mai
    11
    master
    release/*


    View Slide

  12. Kotlin Ͱॻ͘ Gradle Custom Tasks
    Case Study: What we do with Gradle Custom Tasks
    1. Keep the master up-to-date with the latest release branch
    ‣ Daily feature development: master
    ‣ Bug-fixes: release/*
    ‣ release/* may conflict with master… Resolve it as early as possible!
    shuuu-mai
    12
    master
    release/*


    View Slide

  13. Kotlin Ͱॻ͘ Gradle Custom Tasks
    Declare a new Gradle Task
    ▸ In the build script
    ▸ In buildSrc
    ▸ Standalone project
    13
    shuuu-mai

    View Slide

  14. Kotlin Ͱॻ͘ Gradle Custom Tasks
    Declare a new Gradle Task
    ▸ In the build script: one-off task for a single build script
    ▸ In buildSrc
    ▸ Standalone project
    14
    shuuu-mai

    View Slide

  15. Kotlin Ͱॻ͘ Gradle Custom Tasks
    Declare a new Gradle Task
    ▸ In the build script: one-off task for a single build script
    ▸ In buildSrc: Commonly-used task among multiple build scripts
    ▸ Standalone project
    15
    shuuu-mai

    View Slide

  16. Kotlin Ͱॻ͘ Gradle Custom Tasks
    Declare a new Gradle Task
    ▸ In the build script: one-off task for a single build script
    ▸ In buildSrc: Commonly-used task among multiple build scripts
    ▸ Standalone project: Generates sharable jar artifact of Gradle Tasks
    16
    shuuu-mai

    View Slide

  17. Kotlin Ͱॻ͘ Gradle Custom Tasks
    Gradle Custom Tasks with buildSrc
    ▸ Create buildSrc directory under the project root
    ▸ src for Gradle Custom Tasks source code
    ▸ build.gradle.kts for buildSrc configuration
    ▸ dependencies of Gradle Custom Tasks
    17
    shuuu-mai

    View Slide

  18. Kotlin Ͱॻ͘ Gradle Custom Tasks
    buildSrc/build.gradle.kts
    repositories {
    jcenter()
    }
    plugins {
    `kotlin-dsl`
    `java-gradle-plugin`
    }
    dependencies {
    implementation(gradleApi())
    }
    shuuu-mai

    View Slide

  19. Kotlin Ͱॻ͘ Gradle Custom Tasks
    Gradle Custom Tasks: Getting Started
    ▸ Gradle Custom Task class
    ▸ Must be open class
    ▸ Extends DefaultTask
    ▸ Task function is annotated with @TaskAction
    19
    shuuu-mai

    View Slide

  20. Kotlin Ͱॻ͘ Gradle Custom Tasks
    Gradle Custom Tasks: Getting Started
    import org.gradle.api.DefaultTask
    import org.gradle.api.tasks.TaskAction
    open class MyCustomTask : DefaultTask() {
    @TaskAction
    fun sayHello() {
    println("Hello")
    }
    }
    shuuu-mai

    View Slide

  21. Kotlin Ͱॻ͘ Gradle Custom Tasks
    Gradle Custom Tasks: Getting Started
    import org.gradle.api.DefaultTask
    import org.gradle.api.tasks.TaskAction
    open class MyCustomTask : DefaultTask() {
    @TaskAction
    fun sayHello() {
    println("Hello")
    }
    }
    shuuu-mai

    View Slide

  22. Kotlin Ͱॻ͘ Gradle Custom Tasks
    Gradle Custom Tasks: Getting Started
    import org.gradle.api.DefaultTask
    import org.gradle.api.tasks.TaskAction
    open class MyCustomTask : DefaultTask() {
    @TaskAction
    fun sayHello() {
    println("Hello")
    }
    }
    shuuu-mai

    View Slide

  23. Kotlin Ͱॻ͘ Gradle Custom Tasks
    Gradle Custom Tasks: Getting Started
    import org.gradle.api.DefaultTask
    import org.gradle.api.tasks.TaskAction
    open class MyCustomTask : DefaultTask() {
    @TaskAction
    fun sayHello() {
    println("Hello")
    }
    }
    shuuu-mai

    View Slide

  24. Kotlin Ͱॻ͘ Gradle Custom Tasks
    Gradle Custom Tasks: Task registration in the root build.grdle.kts
    24
    shuuu-mai
    tasks.register("myCustomTask") {
    group = "Sample Group"
    description = "Print Hello"
    }
    // $ ./gradlew tasks
    // Sample Group tasks
    // ------------------
    // myCustomTask - Print Hello

    View Slide

  25. Kotlin Ͱॻ͘ Gradle Custom Tasks
    Gradle Custom Tasks: Task registration in the root build.grdle.kts
    25
    shuuu-mai
    tasks.register("myCustomTask") {
    group = "Sample Group"
    description = "Print Hello"
    }
    // $ ./gradlew tasks
    // Sample Group tasks
    // ------------------
    // myCustomTask - Print Hello

    View Slide

  26. Kotlin Ͱॻ͘ Gradle Custom Tasks
    Gradle Custom Tasks: Task registration in the root build.grdle.kts
    26
    shuuu-mai
    tasks.register("myCustomTask") {
    group = "Sample Group"
    description = "Print Hello"
    }
    // $ ./gradlew tasks
    // Sample Group tasks
    // ------------------
    // myCustomTask - Print Hello

    View Slide

  27. Kotlin Ͱॻ͘ Gradle Custom Tasks
    Gradle Custom Tasks: Task registration in the root build.grdle.kts
    27
    shuuu-mai
    tasks.register("myCustomTask") {
    group = "Sample Group"
    description = "Print Hello"
    }
    // $ ./gradlew myCustomTask
    // > Task: myCustomTask
    // Hello

    View Slide

  28. Kotlin Ͱॻ͘ Gradle Custom Tasks
    Gradle Custom Tasks: Options
    ▸ Gradle Custom Task class
    ▸ lateinit variable with @Input
    ▸ Get a CLI option from properties field in the project root build.gradle.kts
    28
    shuuu-mai

    View Slide

  29. Kotlin Ͱॻ͘ Gradle Custom Tasks
    Gradle Custom Tasks: Declare input variable
    import org.gradle.api.DefaultTask
    import org.gradle.api.tasks.TaskAction
    open class MyCustomTask : DefaultTask() {
    @Input lateinit var personName: String
    @TaskAction
    fun sayHello() {
    println("Hello, $personName")
    }
    }
    shuuu-mai

    View Slide

  30. Kotlin Ͱॻ͘ Gradle Custom Tasks
    Gradle Custom Tasks: Read options
    30
    shuuu-mai
    tasks.register("myCustomTask") {
    group = "Sample Group"
    description = "Print Hello"
    personName = properties["personName"] as String? ?: "noname"
    }
    // $ ./gradlew myCustomTask -PpersonName=keithyokoma
    // > Task: myCustomTask
    // Hello, keithyokoma

    View Slide

  31. Kotlin Ͱॻ͘ Gradle Custom Tasks
    Case Study: Daily release branch merge into master
    1. Workflow
    ‣ Pull all the updates
    ‣ Checkout a wokring branch for merging from a release branch
    ‣ Create a merge commit, push and open a pull request
    shuuu-mai
    31

    View Slide

  32. Kotlin Ͱॻ͘ Gradle Custom Tasks
    Case Study: Daily release branch merge into master
    2. What we need
    ‣ GitHub API token
    ‣ A library to execute Git operations and GitHub API calls
    ‣ e.g. JGit for Git operations
    ‣ e.g. github-api for GitHub API calls
    ‣ e.g. jsemver for comparing semantic version
    shuuu-mai
    32

    View Slide

  33. Kotlin Ͱॻ͘ Gradle Custom Tasks
    Case Study: Daily release branch merge into master
    3. What the command looks like?
    shuuu-mai
    33
    ./gradlew mergeReleaseToMaster -PgithubToken=$GITHUB_API_TOKEN

    View Slide

  34. Kotlin Ͱॻ͘ Gradle Custom Tasks
    Gradle Custom Tasks: Prepare Git
    open class DailyMergeTask : DefaultTask() {
    @Input lateinit var token: String
    @TaskAction
    fun merge() {
    val git = FileRepositoryBuilder()
    .setGitDir(File("${project.rootDir}/.git"))
    .build()
    .let(::Git)
    }
    }
    shuuu-mai

    View Slide

  35. Kotlin Ͱॻ͘ Gradle Custom Tasks
    Gradle Custom Tasks: Prepare Git
    open class DailyMergeTask : DefaultTask() {
    @Input lateinit var token: String
    @TaskAction
    fun merge() {
    // …
    val provider = UsernamePasswordCredentialsProvider(
    "token",
    token
    )
    }
    }
    shuuu-mai

    View Slide

  36. Kotlin Ͱॻ͘ Gradle Custom Tasks
    Gradle Custom Tasks: Get the latest releae branch
    open class DailyMergeTask : DefaultTask() {
    @Input lateinit var token: String
    @TaskAction
    fun merge() {
    // …
    // mostly equivalent: $ git branch -a --sort=committerdate | grep "release/"
    val branch = git.lsRemote()
    .setCredentialProvider(provider)
    .call()
    .filter { ref ->
    ref.name.contains("release/")
    }.sortedBy { ref ->
    val versionName = ref.name.replace("refs/heads/release/", "")
    Version.valueOf(versionName)
    }.last() // alternative: maxBy {}
    }
    }
    shuuu-mai

    View Slide

  37. Kotlin Ͱॻ͘ Gradle Custom Tasks
    Gradle Custom Tasks: Create a working branch
    open class DailyMergeTask : DefaultTask() {
    @Input lateinit var token: String
    @TaskAction
    fun merge() {
    // …
    // equivalent: $ git checkout -b ci/$version_to_master
    val version =
    releaseBranch.name.replace("refs/heads/release/", "")
    val workingBranch = "ci/$version_to_master"
    // Ext method to create a new branch (delete existing one if any) and checkout
    git.createAndCheckout(workingBranch)
    }
    }
    shuuu-mai

    View Slide

  38. Kotlin Ͱॻ͘ Gradle Custom Tasks
    Gradle Custom Tasks: Checkout release branch
    open class DailyMergeTask : DefaultTask() {
    @Input lateinit var token: String
    @TaskAction
    fun merge() {
    // …
    // equivalent: $ git fetch origin && git branch -f release/$version origin/release/$version
    git.fetch()
    .setCredentialProvider(provider)
    .call()
    val releaseBranch = branch.name.replace("refs/heads/", "")
    val releaseBranchRef = git.branchCreate()
    .setUpstreamMode(CreateBranchCommand.SetupUpstreamMode.SET_UPSTREAM)
    .setName(branchName)
    .setStartPoint("origin/$releaseBranch")
    .setForce(true)
    .call()
    }
    }
    shuuu-mai

    View Slide

  39. Kotlin Ͱॻ͘ Gradle Custom Tasks
    Gradle Custom Tasks: Create a merge commit on the working branch
    open class DailyMergeTask : DefaultTask() {
    @Input lateinit var token: String
    @TaskAction
    fun merge() {
    // …
    // equivalent: $ git merge —no-ff release/$version
    val mergeResult = git.merge()
    .include(releaseBranchRef)
    .setFastForward(MergeCommand.FastForwardMode.NO_FF)
    .setMessage("CI Merge $releaseBranch")
    .call()
    }
    }
    shuuu-mai

    View Slide

  40. Kotlin Ͱॻ͘ Gradle Custom Tasks
    Gradle Custom Tasks: Check if any conflicts
    open class DailyMergeTask : DefaultTask() {
    @Input lateinit var token: String
    @TaskAction
    fun merge() {
    // …
    val status = mergeResult.mergeStatus
    if (!status.isSuccessful && status == CONFLICTING) {
    throw IllegalStateException(mergeResult.message)
    }
    }
    }
    shuuu-mai

    View Slide

  41. Kotlin Ͱॻ͘ Gradle Custom Tasks
    Gradle Custom Tasks: Push if succeeds
    open class DailyMergeTask : DefaultTask() {
    @Input lateinit var token: String
    @TaskAction
    fun merge() {
    // …
    // equivalent: $ git push --force-with-rease origin ci/$version_to_master
    git.push()
    .setForce(true)
    .setCredentialProvider(provider)
    .call()
    }
    }
    shuuu-mai

    View Slide

  42. Kotlin Ͱॻ͘ Gradle Custom Tasks
    Gradle Custom Tasks: Push if succeeds
    open class DailyMergeTask : DefaultTask() {
    @Input lateinit var token: String
    @TaskAction
    fun merge() {
    // …
    // equivalent: $ hub pull-request
    GitHub.connectUsingOAuth(token)
    .getRepository("user_name/repository_name")
    .createPullRequest(
    "[CI] Daily merge from $releaseBranch to master",
    workingBranch,
    "master",
    "Automatically created pull request!"
    )
    }
    }
    shuuu-mai

    View Slide

  43. Kotlin Ͱॻ͘ Gradle Custom Tasks
    Wrap up: General use of Gradle Tasks
    ▸ Automate the stuff you are doing in daily basis with Gradle Custom Tasks.
    ▸ A Gradle Custom Task can handle File and/or Network I/O.
    ▸ No need to consider some kind of AsyncTask.
    ▸ And… you can write unit tests on Gradle Custom Tasks. :)
    43
    shuuu-mai

    View Slide

  44. Kotlin Ͱॻ͘

    Gradle Custom Tasks
    KeithYokoma (Keishin Yokomaku) / shuuu-mai

    View Slide