Kotlin で書く Gradle Custom Tasks

Kotlin で書く Gradle Custom Tasks

Bbe9718bebdafbdc8dabbe3cadf1bc46?s=128

Keishin Yokomaku

November 27, 2019
Tweet

Transcript

  1. Kotlin Ͱॻ͘
 Gradle Custom Tasks KeithYokoma (Keishin Yokomaku) / shuuu-mai

  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
  3. Kotlin Ͱॻ͘ Gradle Custom Tasks Gradle Tasks 3 $ ./gradlew

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

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

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

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

    assembleDebug testDebugUnitTest ktlintDebug ktlint Λ debug variant Ͱ࣮ߦ͢Δ shuuu-mai
  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
  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
  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!
  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/*
  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/*
  13. Kotlin Ͱॻ͘ Gradle Custom Tasks Declare a new Gradle Task

    ▸ In the build script ▸ In buildSrc ▸ Standalone project 13 shuuu-mai
  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
  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
  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
  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
  18. Kotlin Ͱॻ͘ Gradle Custom Tasks buildSrc/build.gradle.kts repositories { jcenter() }

    plugins { `kotlin-dsl` `java-gradle-plugin` } dependencies { implementation(gradleApi()) } shuuu-mai
  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
  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
  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
  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
  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
  24. Kotlin Ͱॻ͘ Gradle Custom Tasks Gradle Custom Tasks: Task registration

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

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

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

    in the root build.grdle.kts 27 shuuu-mai tasks.register<MyCustomTask>("myCustomTask") { group = "Sample Group" description = "Print Hello" } // $ ./gradlew myCustomTask // > Task: myCustomTask // Hello
  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
  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
  30. Kotlin Ͱॻ͘ Gradle Custom Tasks Gradle Custom Tasks: Read options

    30 shuuu-mai tasks.register<MyCustomTask>("myCustomTask") { group = "Sample Group" description = "Print Hello" personName = properties["personName"] as String? ?: "noname" } // $ ./gradlew myCustomTask -PpersonName=keithyokoma // > Task: myCustomTask // Hello, keithyokoma
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  44. Kotlin Ͱॻ͘
 Gradle Custom Tasks KeithYokoma (Keishin Yokomaku) / shuuu-mai