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

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

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

Marco Valentino

April 06, 2021
Tweet

More Decks by Marco Valentino

Other Decks in Programming

Transcript

  1. 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
  2. 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
  3. - Delegate to Github versioning for releases and pull requests

    - Use tags for releases - Use commit hashes for snapshots Versioning
  4. - 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]
  5. 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]
  6. - 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]
  7. 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
  8. 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
  9. Artifacts plugins { apply(“maven-publish”) ... } ... publishing { repositories

    { maven(“${project.rootDir}/artifacts/maven”) } } [KMP: build.gradle] Android ./gradlew publishKotlinMultiplatformPublicationToMavenRepository ./gradlew publishAndroidDebugPublicationToMavenRepository ./gradlew publishAndroidReleasePublicationToMavenRepository [sh]
  10. 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
  11. 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
  12. Artifacts kotlin { ... configure(listOf(iosArm64(), iosX64()) { binaries.framework { ...

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

    { dependencies { api(“::module1”) api(“::module2”) ... } } ... } ... } [KMP: iosmodule/build.gradle] iOS - Creating an ‘iosmodule’ module
  14. 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
  15. 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
  16. 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
  17. 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
  18. 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 => '[email protected]:<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]
  19. 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 => '[email protected]:<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]
  20. 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 => '[email protected]:<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]
  21. 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 => '[email protected]:<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]
  22. 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
  23. 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)
  24. 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)
  25. 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]
  26. CREDITS: This presentation template was created by Slidesgo, including icons

    by Flaticon, infographics & images by Freepik Credits!