$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 About Me ▸ Keishin Yokomaku

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

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

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

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

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

    assembleDebug testDebugUnitTest ktlintDebug ktlint Λ debug variant Ͱ࣮ߦ͢Δ shuuu-mai
  7. 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
  8. 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
  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 10 shuuu-mai = Automation!
  10. 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/*
  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… Resolve it as early as possible! shuuu-mai 12 master release/*
  12. Kotlin Ͱॻ͘ Gradle Custom Tasks Declare a new Gradle Task

    ▸ In the build script ▸ In buildSrc ▸ Standalone project 13 shuuu-mai
  13. 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
  14. 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
  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: Generates sharable jar artifact of Gradle Tasks 16 shuuu-mai
  16. 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
  17. Kotlin Ͱॻ͘ Gradle Custom Tasks buildSrc/build.gradle.kts repositories { jcenter() }

    plugins { `kotlin-dsl` `java-gradle-plugin` } dependencies { implementation(gradleApi()) } shuuu-mai
  18. 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
  19. 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
  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: 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
  24. 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
  25. 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
  26. 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
  27. 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
  28. 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
  29. 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
  30. 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
  31. 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
  32. 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
  33. 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
  34. 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
  35. 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
  36. 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
  37. 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
  38. 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
  39. 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
  40. 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
  41. 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
  42. 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