Upgrade to Pro — share decks privately, control downloads, hide ads and more …

チーム開発でのKotlin Multiplatformリポジトリ構成とVersion管理とCI/CD

チーム開発でのKotlin Multiplatformリポジトリ構成とVersion管理とCI/CD

Bd6c9292707f5d8c5b107b2c697276df?s=128

Marco Valentino

April 06, 2021
Tweet

Transcript

  1. Kotlin Multiplatform: Structure, Versioning and Ci/Cd

  2. Table of Contents Character Introduction Project Structures How should the

    players interact? Who are the key players? Versioning CI/CD Pipeline How to automate everything? How to keep track of the players? 01 02 03 04
  3. Character Introduction 01

  4. Marco Valentino! • Graduated with Master’s Degree from University of

    Illinois Urbana Champaign • Joined teamLab in April 2019 doing iOS (0.5 years) and Android (1.5 years) development • Started working with Kotlin Multiplatform since early 2020
  5. Characters iOS Kotlin Multiplatform Android iOS KMP Android

  6. Characters iOS Android [iOS Project] [Android Project] [Server] UI Domain

    Data Data Domain UI
  7. Characters iOS KMP Android [iOS Project] [Android Project] [Server] UI

    Domain Data UI [KMP Project]
  8. Project Structure 02

  9. Mono-Repository iOS KMP Android [Repository]

  10. Mono-Repository iOS Android [Repository] KMP

  11. Mono-Repository iOS KMP Android [Repository]

  12. Multi-Repository iOS KMP Android [Repository] [Repository] [Repository]

  13. Multi-Repository iOS Android [Repository] [Repository] [Repository] KMP

  14. Multi-Repository iOS Android [Repository] [Repository] [Repository] KMP

  15. Multi-Repository iOS KMP Android [Repository] [Repository] [Repository]

  16. Multi-Repository iOS KMP Android [Repository] [Repository] [Repository]

  17. Multi-Repository iOS KMP Android [Repository] [Repository] [Repository] [Cocoapods] [Maven]

  18. Multi-Repository iOS KMP Android [Repository] [Repository] [Repository] [Cocoapods] [Maven]

  19. Multi-Repository iOS KMP Android [Repository] [Repository] [Repository] [Cocoapods] [Maven] v

    0.1 v 0.2
  20. Multi-Repository iOS KMP Android [Repository] [Repository] [Repository] [Cocoapods] [Maven] v

    0.1 v 0.2
  21. Versioning 03

  22. - Delegate to Github versioning for releases and pull requests

    - Use tags for releases - Use commit hashes for snapshots Versioning
  23. - Delegate to Github versioning for releases and pull requests

    - Use tags for releases - Use commit hashes for snapshots Versioning git fetch --tags --quiet && git describe --tags --always --first-parent releases: 0.1.0 snapshots: 0.1.0-1-gf4e3f96 [sh]
  24. latest tag - Delegate to Github versioning for releases and

    pull requests - Use tags for releases - Use commit hashes for snapshots Versioning git fetch --tags --quiet && git describe --tags --always --first-parent releases: 0.1.0 snapshots: 0.1.0-1-gf4e3f96 # of commits since tag commit hash [sh]
  25. - Delegate to Github versioning for releases and pull requests

    - Use tags for releases - Use commit hashes for snapshots Versioning environment { VERSION = sh([script:’git fetch --tags --quiet && git describe --tags --always --first-parent’, returnStdout:true]).trim() ... } [CI/CD: Jenkinsfile] allprojects { version = System.getenv(“VERSION”) ... } [KMP: root/build.gradle]
  26. CI/CD Pipeline 04

  27. Goals for Ci/Cd Versioning System Push Artifacts to a Repository

    Maven for Android, Cocoapods for iOS Releases for tags, Snapshots for Pull Requests Parallel Builds Run platform specific tasks on different nodes 01 02 03 Hosted on private Github repository Do not use other package managers (Github Packages, Bintray, ect.) 04
  28. Ci/Cd Pipeline Setup Lint Test Publish Assemble Report

  29. Ci/Cd Pipeline Setup Lint Test Publish Assemble Report

  30. Create place to store artifacts locally /artifacts/maven for Android /artifacts/cocoapods

    for iOS Add gradle tasks to create necessary artifacts 01 02 Artifacts Overview
  31. Artifacts plugins { apply(“maven-publish”) ... } ... publishing { repositories

    { maven(“${project.rootDir}/artifacts/maven”) } } [KMP: build.gradle] Android ./gradlew publishKotlinMultiplatformPublicationToMavenRepository ./gradlew publishAndroidDebugPublicationToMavenRepository ./gradlew publishAndroidReleasePublicationToMavenRepository [sh]
  32. Create ‘iosmodule’ module Add gradle tasks to create universal framework

    Setup Cocoapods Repo Add gradle tasks to create podspec 01 02 03 04 Artifacts iOS
  33. Create ‘iosmodule’ module Add gradle tasks to create universal framework

    Setup Cocoapods Repo Add gradle tasks to create podspec 01 02 03 04 Artifacts iOS
  34. Artifacts kotlin { ... configure(listOf(iosArm64(), iosX64()) { binaries.framework { ...

    cinterop settings ... export(“::module1”) export(“::module2”) ... } } ... } [KMP: iosmodule/build.gradle] iOS - Creating an ‘iosmodule’ module
  35. Artifacts kotlin { ... sourceSets { val commonMain by getting

    { dependencies { api(“::module1”) api(“::module2”) ... } } ... } ... } [KMP: iosmodule/build.gradle] iOS - Creating an ‘iosmodule’ module
  36. Create ‘iosmodule’ module Add gradle tasks to create universal framework

    Setup Cocoapods Repo Add gradle tasks to create podspec 01 02 03 04 Artifacts iOS
  37. Artifacts kotlin { val frameworkName = “common” val artifactsDir =

    rootDir.resolve(“artifacts/cocoapods”) val debugFramework = tasks.create("debugFramework", FatFrameworkTask::class) { baseName = frameworkName destinationDir = artifactsDir.resolve("debug") from( iosArm64.binaries.getFramework("DEBUG"), iosX64.binaries.getFramework("DEBUG") ) } } [KMP: iosmodule/build.gradle] iOS - Create gradle task to build framework
  38. Create ‘iosmodule’ module Add gradle tasks to create universal framework

    Setup Cocoapods Repo Add gradle tasks to create podspec 01 02 03 04 Artifacts iOS
  39. Artifacts iOS - Setup Cocoapods Repo https://guides.cocoapods.org/making/private-cocoapods.html

  40. Create ‘iosmodule’ module Add gradle tasks to create universal framework

    Setup Cocoapods Repo Add gradle tasks to create podspec 01 02 03 04 Artifacts iOS
  41. Artifacts iOS - Generate Podspec Pod::Spec.new do |spec| spec.name =

    "<ProjectName>" spec.version = "<Version>" spec.license = "<License>" spec.homepage = "<Homepage>" spec.authors = "<Author>" spec.summary = "<Summary>" spec.source = { :git => 'git@github.com:<Username>/<ProjectName>Spec.git, :branch => '<Branch>' } spec.platform = :ios, "<x.x>" spec.vendored_frameworks = 'cocoapods/debug/<FrameworkName>.framework' spec.static_framework = true spec.libraries = "c++", "sqlite3", ... spec.module_name = "#{spec.name}_umbrella" spec.pod_target_xcconfig = { 'KOTLIN_TARGET[sdk=iphonesimulator*]' => 'ios_x64', 'KOTLIN_TARGET[sdk=iphoneos*]' => 'ios_arm' ... } end [Podspec]
  42. Artifacts iOS - Generate Podspec val generatePodspec = tasks.create("generatePodspec") {

    val podspecText = “”” Pod::Spec.new do |spec| spec.name = "<ProjectName>" spec.version = "$version" spec.license = "<License>" spec.homepage = "<Homepage>" spec.authors = "<Author>" spec.summary = "<Summary>" spec.source = { :git => 'git@github.com:<Username>/<ProjectName>Spec.git’, :branch => '$version' } spec.platform = :ios, "<x.x>" spec.vendored_frameworks = 'cocoapods/debug/$frameworkName.framework' spec.static_framework = true spec.libraries = "c++", "sqlite3", ... spec.module_name = "#{spec.name}_umbrella" spec.pod_target_xcconfig = { 'KOTLIN_TARGET[sdk=iphonesimulator*]' => 'ios_x64', 'KOTLIN_TARGET[sdk=iphoneos*]' => 'ios_arm' ... } end “””.trimIndent() artifactsDir.resolve("<ProjectName>.podspec”).writeText(podspecText) } [KMP: iosmodule/build.gradle]
  43. Artifacts iOS - Generate Podspec val generatePodspec = tasks.create("generatePodspec") {

    val podspecText = “”” Pod::Spec.new do |spec| spec.name = "<ProjectName>" spec.version = "$version" spec.license = "<License>" spec.homepage = "<Homepage>" spec.authors = "<Author>" spec.summary = "<Summary>" spec.source = { :git => 'git@github.com:<Username>/<ProjectName>Spec.git’, :branch => '$version' } spec.platform = :ios, "<x.x>" spec.vendored_frameworks = 'cocoapods/debug/$frameworkName.framework' spec.static_framework = true spec.libraries = "c++", "sqlite3", ... spec.module_name = "#{spec.name}_umbrella" spec.pod_target_xcconfig = { 'KOTLIN_TARGET[sdk=iphonesimulator*]' => 'ios_x64', 'KOTLIN_TARGET[sdk=iphoneos*]' => 'ios_arm' ... } end “””.trimIndent() artifactsDir.resolve("<ProjectName>.podspec”).writeText(podspecText) } [KMP: iosmodule/build.gradle]
  44. Artifacts iOS - Generate Podspec val generatePodspec = tasks.create("generatePodspec") {

    val podspecText = “”” Pod::Spec.new do |spec| spec.name = "<ProjectName>" spec.version = "$version" spec.license = "<License>" spec.homepage = "<Homepage>" spec.authors = "<Author>" spec.summary = "<Summary>" spec.source = { :git => 'git@github.com:<Username>/<ProjectName>Spec.git’, :branch => '$version' } spec.platform = :ios, "<x.x>" spec.vendored_frameworks = 'cocoapods/debug/$frameworkName.framework' spec.static_framework = true spec.libraries = "c++", "sqlite3", ... spec.module_name = "#{spec.name}_umbrella" spec.pod_target_xcconfig = { 'KOTLIN_TARGET[sdk=iphonesimulator*]' => 'ios_x64', 'KOTLIN_TARGET[sdk=iphoneos*]' => 'ios_arm' ... } end “””.trimIndent() artifactsDir.resolve("<ProjectName>.podspec”).writeText(podspecText) } [KMP: iosmodule/build.gradle]
  45. Artifacts stage(‘Artifacts’) { parallel { stage(‘Android’) { steps { node(label:

    'android') { sh "./gradlew publishKotlinMultiplatformPublicationToMavenRepository" sh "./gradlew publishAndroidDebugPublicationToMavenRepository" sh "./gradlew publishAndroidReleasePublicationToMavenRepository" stash(name: 'androidArtifacts', includes: '**/artifacts/maven/**') } stage(‘Ios’) { steps { node(label: 'ios') { sh "./gradlew universalFramework” sh "./gradlew generateCocoapodsFile” stash(name: 'iosArtifacts', includes: '**/artifacts/cocoapods/**') } } } } } [CI/CD: Jenkinsfile] KMP
  46. Push artifacts to GitHub 01 Publish Overview Run Cocoapod commands

    02
  47. Push artifacts to GitHub 01 Publish Overview Run Cocoapod commands

    02
  48. Publish plugins { id("org.ajoberstar.git-publish") version "3.0.0" } extensions.getByType<org.ajoberstar.gradle.git.publish.GitPublishExtension>().run { repoUri.set(<ARTIFACTS

    URL>) branch.set("$version") commitMessage.set("Release $version") contents { from("artifacts") } } [KMP: build.gradle.kts] - Use Gradle-Git-Publish plugin (https://github.com/ajoberstar/gradle-git-publish)
  49. Push artifacts to GitHub 01 Publish Overview Run Cocoapod commands

    02
  50. Publish pod repo remove <PROJECT NAME> || true pod repo

    add <PROJECT NAME> <PODSPEC URL> pod repo push <PROJECT NAME> artifacts/cocoapods/<PROJECT NAME>.podspec [sh] - Add Cocoapods commands (https://guides.cocoapods.org/making/private-cocoapods.html)
  51. Publish stage(‘Publish’) { unstash 'androidArtifacts' unstash 'iosArtifacts' sh ``` ./gradle

    gitPublishPush pod repo remove <PROJECT NAME> || true pod repo add <PROJECT NAME> <PODSPEC URL> pod repo push <PROJECT NAME> artifacts/cocoapods/<PROJECT NAME>.podspec ``` } [CI/CD: Jenkinsfile]
  52. Publish

  53. Thanks for listening!

  54. CREDITS: This presentation template was created by Slidesgo, including icons

    by Flaticon, infographics & images by Freepik Credits!