{"id":947,"date":"2019-01-04T14:40:11","date_gmt":"2019-01-04T11:40:11","guid":{"rendered":"https:\/\/artem.services\/?p=466"},"modified":"2019-03-17T17:54:35","modified_gmt":"2019-03-17T14:54:35","slug":"jenkins-fastlane-build-ios-and-android-apps-2","status":"publish","type":"post","link":"https:\/\/artem.services\/?p=947&lang=en","title":{"rendered":"Jenkins &#8212; Fastlane build iOS and Android apps"},"content":{"rendered":"<p><span class=\"tlid-translation translation\" lang=\"en\"><span class=\"\" title=\"\">Using <strong>Jenkins<\/strong> and <strong>Fastlane,<\/strong> we will build applications on <strong>iOS<\/strong> and <strong>Android<\/strong>, send artifacts to <strong>Slack<\/strong>, and also automatically send an <strong>iOS<\/strong> application to <strong>Testflight<\/strong>.<\/span> <span class=\"\" title=\"\">The build is configured from the <strong>develop<\/strong> and <strong>release<\/strong> branches, and reads the release version (<strong>major<\/strong> and <strong>minor<\/strong>) from them, and adds the build number. <\/span><\/span><\/p>\n<p><span class=\"tlid-translation translation\" lang=\"en\"><span class=\"\" title=\"\">For example: the branch is <strong>release\/1.0<\/strong> and the <strong>Jenkins<\/strong> number of build <strong>25<\/strong>, then the application version will be <strong>1.0.25<\/strong><\/span><\/span><\/p>\n<p><span class=\"tlid-translation translation\" lang=\"en\"><span class=\"\" title=\"\">For the building will be needed:<\/span><\/span><\/p>\n<ul>\n<li><a href=\"https:\/\/artem.services\/?p=453\" target=\"_blank\" rel=\"noopener noreferrer\">Docker image with Android SDK<\/a><\/li>\n<li><a href=\"https:\/\/artem.services\/?p=433\" target=\"_blank\" rel=\"noopener noreferrer\">Certificates for iOS &#8212; AdHoc and AppStore<\/a><\/li>\n<li><a href=\"https:\/\/artem.services\/?p=368\" target=\"_blank\" rel=\"noopener noreferrer\">Android Release Key<\/a><\/li>\n<\/ul>\n<p><span class=\"tlid-translation translation\" lang=\"en\"><span class=\"\" title=\"\">For convenience, my application is everywhere called:<\/span><\/span> &quot;<strong>my_mobile_app<\/strong>&quot;<\/p>\n<p><!--more--><\/p>\n<h3>jenkinsfile:<\/h3>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\npipeline {\r\n    agent none\r\n    parameters {\r\n        booleanParam(name: 'del_cache', defaultValue: false, description: 'Toggle this value to build with cache clearing.')\r\n    }\r\n    environment {\r\n        REPO = 'https:\/\/artem@bitbucket.org\/artem\/my_mobile_app.git'\r\n        REPO_CRED = 'artem-jenkins-bitbucket'\r\n        IOS_KEYCHAIN = 'my_mobile_app-db'\r\n        SLACK_TOKEN = 'my-slack-token'\r\n        SLACK_CHANNEL = 'artem-debug'\r\n    }\r\n    options {\r\n        ansiColor('xterm')\r\n        timeout(time: 60, unit:'MINUTES')\r\n        timestamps()\r\n    }    \r\n    stages {       \r\n        stage('Parallel Stage test') {\r\n            parallel {\r\n                stage('Android.') {\r\n                    agent any\r\n                    stages {\r\n                        stage('Android. Define versions') {\r\n                            steps {\r\n                                script {\r\n                                    BR_VER = sh(script: &quot;echo ${BRANCH_NAME} | cut -d '\/' -f 2 | tr -d \\&quot;\\n\\&quot;&quot;, returnStdout: true)\r\n                                    VERS_FULL = &quot;${BR_VER}.${BUILD_NUMBER}&quot;                           \r\n                                }\r\n                            }\r\n                        }\r\n                        stage ('Clearing cache.') {\r\n                            when { expression { return params.del_cache } }\r\n                            steps {\r\n                                deleteDir()\r\n                            }\r\n                        }                                                \r\n                        stage('Android. Get code') {\r\n                            steps {\r\n                                echo &quot;Get code for android.&quot;\r\n                                git branch: &quot;${BRANCH_NAME}&quot;, credentialsId: &quot;${REPO_CRED}&quot;, url: &quot;${REPO}&quot;\r\n                            }\r\n                        }\r\n                        stage ('Android. Build') {\r\n                            agent {\r\n                                docker {\r\n                                    image 'repo.artem.services\/android_studio\/base-image-jenkins:latest'\r\n                                    args &quot;--name my_mobile_app-worker-${BUILD_NUMBER} \\\r\n                                    -e RELEASE_KEYSTORE_FILE=\\&quot;my_mobile_app-release-keystore\\&quot; \\\r\n                                    -e RELEASE_KEYSTORE_PASSWORD=\\&quot;MY_PASSWORD\\&quot; \\\r\n                                    -e RELEASE_KEY_ALIAS=\\&quot;my_mobile_app\\&quot; \\\r\n                                    -e RELEASE_KEY_PASSWORD=\\&quot;MY_PASSWORD\\&quot;&quot;\r\n                                    reuseNode true\r\n                                }\r\n                            }\r\n                            environment {\r\n                                APP_VERSION = &quot;${VERS_FULL}&quot;\r\n                                BUILD_NUMBER = &quot;${BUILD_NUMBER}&quot;\r\n                                HOME = &quot;${WORKSPACE}&quot;\r\n                            }\r\n                            steps {\r\n                                script {\r\n                                    sh 'sed -i \\&quot;s\/versionCode 1\/versionCode 1${BUILD_NUMBER}\/\\&quot; ${WORKSPACE}\/android\/app\/build.gradle'\r\n                                    sh 'sed -i \\&quot;s\/versionName \\\\&quot;1.0\\\\&quot;\/versionName \\\\&quot;${APP_VERSION}\\\\&quot;\/\\&quot; ${WORKSPACE}\/android\/app\/build.gradle' \r\n                                    sh &quot;yarn &amp;&amp; yarn cache clean &amp;&amp; bundle install --path=vendor\/bundle &amp;&amp; \\\r\n                                        env &amp;&amp; bundle exec fastlane android release&quot;\r\n                                }\r\n                            }\r\n                        }\r\n                        stage ('Android. Upload apk to slack and make artifacts') {\r\n                            steps {\r\n                                script {\r\n                                    sh &quot;mv -f ${WORKSPACE}\/android\/app\/build\/outputs\/apk\/release\/app-release.apk ${WORKSPACE}\/android\/app\/build\/outputs\/apk\/release\/my_mobile_app_${VERS_FULL}.apk&quot;\r\n                                    sh &quot;curl -F \\&quot;file=@${WORKSPACE}\/android\/app\/build\/outputs\/apk\/release\/my_mobile_app_${VERS_FULL}.apk\\&quot; -F \\&quot;initial_comment=Android: my_mobile_app_adhoc_${VERS_FULL}.apk file from \\&quot;${BRANCH_NAME}\\&quot; branch\\&quot; -F \\&quot;filetype=auto\\&quot; -F \\&quot;channels=${SLACK_CHANNEL}\\&quot; -H \\&quot;Authorization: Bearer ${SLACK_TOKEN}\\&quot; https:\/\/slack.com\/api\/files.upload&quot;\r\n                                }\r\n                            }\r\n                            post {\r\n                                always {\r\n                                    archiveArtifacts artifacts: &quot;android\/app\/build\/outputs\/apk\/release\/my_mobile_app_${VERS_FULL}.apk&quot;, onlyIfSuccessful: true\r\n                                }\r\n                            }                            \r\n                        }                         \r\n                    }                   \r\n                }\r\n                stage('iOS.') {\r\n                    agent {node 'ios'}\r\n                    stages {\r\n                        stage('iOS. Define versions') {\r\n                            steps {\r\n                                script {\r\n                                    BR_VER = sh(script: &quot;echo ${BRANCH_NAME} | cut -d '\/' -f 2 | tr -d \\&quot;\\n\\&quot;&quot;, returnStdout: true)\r\n                                    VERS_FULL = &quot;${BR_VER}.${BUILD_NUMBER}&quot;\r\n                                }\r\n                            }\r\n                        }\r\n                        stage ('Clearing cache.') {\r\n                            when { expression { return params.del_cache } }\r\n                            steps {\r\n                                deleteDir()\r\n                            }\r\n                        }\r\n                        stage('IOS. Get code') {\r\n                            steps {\r\n                                git branch: &quot;${BRANCH_NAME}&quot;, credentialsId: &quot;${REPO_CRED}&quot;, url: &quot;${REPO}&quot;\r\n                            }\r\n                        }\r\n                        stage('iOS. Build') {\r\n                        environment {\r\n                            APP_VERSION = &quot;${VERS_FULL}&quot;\r\n                            MATCH_GIT_URL = &quot;git@bitbucket.org:artem\/certificates.git&quot;\r\n                        }\r\n                            steps {\r\n                                \/\/ Delete keychain if it exist:\r\n                                sh &quot;rm -rf .\/ios\/Podfile.lock | true&quot;\r\n                                sh &quot;if git diff HEAD^ HEAD fastlane\/devices.txt | grep -q 'fastlane\/devices.txt'; then bundle exec fastlane run \\\r\n                                create_keychain name:'my_mobile_appkey' password:'MY_PASSWORD' \\\r\n                                default_keychain:true unlock:true timeout:false &amp;&amp; \\\r\n                                bundle exec fastlane run match force:true keychain_name:my_mobile_appkey type:adhoc; fi&quot;\r\n                                sh &quot;\/usr\/bin\/security delete-keychain ${IOS_KEYCHAIN} | true&quot;\r\n                                sh &quot;yarn &amp;&amp; bundle install&quot;\r\n                                script {\r\n                                    sh &quot;cp $HOME\/jenkins\/files_dotenv\/.my_mobile_app.dotenv $WORKSPACE\/.env&quot;\r\n                                    if (BRANCH_NAME ==~ &quot;release\/.*&quot;) {\r\n                                        \/\/ Build adhock\r\n                                        sh &quot;bundle exec fastlane ios adhoc&quot;\r\n                                        sh &quot;mv -f ios_build\/my_mobile_app_adhoc.ipa ios_build\/my_mobile_app_adhoc_${VERS_FULL}.ipa&quot;                                        \r\n                                        sh &quot;curl -F \\&quot;file=@ios_build\/my_mobile_app_adhoc_${VERS_FULL}.ipa\\&quot; -F \\&quot;initial_comment=iOS: my_mobile_app_adhoc_${VERS_FULL}.ipa file from \\&quot;${BRANCH_NAME}\\&quot; branch\\&quot; -F \\&quot;filetype=auto\\&quot; -F \\&quot;channels=${SLACK_CHANNEL}\\&quot; -H \\&quot;Authorization: Bearer ${SLACK_TOKEN}\\&quot; https:\/\/slack.com\/api\/files.upload&quot;\r\n                                        \/\/ Build appstore                                        \r\n                                        sh &quot;bundle exec fastlane ios release&quot;\r\n                                        sh &quot;mv -f ios_build\/my_mobile_app_appstore.ipa ios_build\/my_mobile_app_appstore_${VERS_FULL}.ipa&quot;                                       \r\n                                    }\r\n                                    else if (BRANCH_NAME ==~ &quot;develop\/.*&quot;) {\r\n                                        sh &quot;bundle exec fastlane ios adhoc&quot;\r\n                                        sh &quot;mv -f ios_build\/my_mobile_app_adhoc.ipa ios_build\/my_mobile_app_adhoc_${VERS_FULL}.ipa&quot;\r\n                                        sh &quot;curl -F \\&quot;file=@ios_build\/my_mobile_app_adhoc_${VERS_FULL}.ipa\\&quot; -F \\&quot;initial_comment=iOS: my_mobile_app_adhoc_${VERS_FULL}.ipa file from \\&quot;${BRANCH_NAME}\\&quot; branch\\&quot; -F \\&quot;filetype=auto\\&quot; -F \\&quot;channels=${SLACK_CHANNEL}\\&quot; -H \\&quot;Authorization: Bearer ${SLACK_TOKEN}\\&quot; https:\/\/slack.com\/api\/files.upload&quot;\r\n                                    }\r\n                                }\r\n                            }\r\n                        }\r\n                        stage ('iOS. Upload ipa to slack and make artifacts') {\r\n                            steps {\r\n                                echo &quot;Making Artifacts...&quot;\r\n                            }\r\n                            post {\r\n                                always {\r\n                                    archiveArtifacts artifacts: &quot;ios_build\/*.ipa&quot;, onlyIfSuccessful: false\r\n                                    sh &quot;rm -rf $WORKSPACE\/ios_build&quot;\r\n                                    sh &quot;rm -f ${WORKSPACE}\/.env&quot;\r\n                                }\r\n                            }\r\n                        }\r\n                    }\r\n                }\r\n            }\r\n        }\r\n    }\r\n    post {\r\n        success {\r\n            slackSend channel: &quot;${SLACK_CHANNEL}&quot;, color: 'good', message: &quot;Job: ${JOB_NAME}${BUILD_NUMBER} build was successful.&quot;\r\n        }\r\n        failure {\r\n            slackSend channel: &quot;${SLACK_CHANNEL}&quot;, color: 'danger', message: &quot;Job: ${JOB_NAME}${BUILD_NUMBER} was finished with some error. It may occurs because of the build was rollbacked by docker swarm, or because of other error (watch the Jenkins Console Output): ${JOB_URL}${BUILD_ID}\/consoleFull&quot;\r\n        }\r\n        unstable {\r\n            slackSend channel: &quot;${SLACK_CHANNEL}&quot;, color: 'warning', message: &quot;Job: ${JOB_NAME}${BUILD_NUMBER} was finished with some error. Please watch the Jenkins Console Output: ${JOB_URL}${BUILD_ID}\/console.&quot;\r\n        }\r\n    }  \r\n}\r\n<\/pre>\n<h4>Gemfile<\/h4>\n<blockquote><p>\u041d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0432 \u043a\u043e\u0440\u043d\u0435<\/p><\/blockquote>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\nsource 'https:\/\/rubygems.org'\r\ngem 'fastlane'\r\ngem 'cocoapods'\r\ngem 'dotenv'\r\n<\/pre>\n<h1>Android<\/h1>\n<h4>build.grandge<\/h4>\n<blockquote><p>\u041d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0432 <strong>android\/app<\/strong><\/p><\/blockquote>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\napply plugin: &quot;com.android.application&quot;\r\n\r\nimport com.android.build.OutputFile\r\n\r\nproject.ext.react = [\r\n    entryFile: &quot;index.js&quot;\r\n]\r\n\r\napply from: &quot;..\/..\/node_modules\/react-native\/react.gradle&quot;\r\n\r\ndef enableSeparateBuildPerCPUArchitecture = false\r\n\r\ndef enableProguardInReleaseBuilds = false\r\n\r\nandroid {\r\n    compileSdkVersion 26\r\n    buildToolsVersion '27.0.3'\r\n\r\n    defaultConfig {\r\n        applicationId &quot;com.mymobileapp&quot;\r\n        minSdkVersion 16\r\n        targetSdkVersion 26\r\n        versionCode 1\r\n        versionName &quot;1.0&quot;\r\n        ndk {\r\n            abiFilters &quot;armeabi-v7a&quot;, &quot;x86&quot;\r\n        }\r\n    }\r\n    signingConfigs {\r\n        release {\r\n            storeFile file(String.valueOf(System.getenv(&quot;RELEASE_KEYSTORE_FILE&quot;)))\r\n            storePassword System.getenv(&quot;RELEASE_KEYSTORE_PASSWORD&quot;)\r\n            keyAlias System.getenv(&quot;RELEASE_KEY_ALIAS&quot;)\r\n            keyPassword System.getenv(&quot;RELEASE_KEY_PASSWORD&quot;)\r\n        }\r\n        debug {\r\n            storeFile file(String.valueOf(System.getenv(&quot;DEBUG_KEYSTORE_FILE&quot;)))\r\n            storePassword System.getenv(&quot;DEBUG_KEYSTORE_PASSWORD&quot;)\r\n            keyAlias System.getenv(&quot;DEBUG_KEY_ALIAS&quot;)\r\n            keyPassword System.getenv(&quot;DEBUG_KEY_PASSWORD&quot;)\r\n        }\r\n      }\r\n    lintOptions {\r\n        checkReleaseBuilds false\r\n        abortOnError false\r\n    }\r\n    splits {\r\n        abi {\r\n            reset()\r\n            enable enableSeparateBuildPerCPUArchitecture\r\n            universalApk false \r\n            include &quot;armeabi-v7a&quot;, &quot;x86&quot;\r\n        }\r\n    }\r\n\r\n    buildTypes {\r\n        release {\r\n            minifyEnabled enableProguardInReleaseBuilds\r\n            proguardFiles getDefaultProguardFile(&quot;proguard-android.txt&quot;), &quot;proguard-rules.pro&quot;\r\n            signingConfig signingConfigs.release\r\n        }\r\n    }\r\n    applicationVariants.all { variant -&amp;amp;gt;\r\n        variant.outputs.each { output -&amp;amp;gt;\r\n            def versionCodes = [&quot;armeabi-v7a&quot;:1, &quot;x86&quot;:2]\r\n            def abi = output.getFilter(OutputFile.ABI)\r\n            if (abi != null) { \r\n                output.versionCodeOverride =\r\n                        versionCodes.get(abi) * 1048576 + defaultConfig.versionCode\r\n            }\r\n        }\r\n    }\r\n}\r\n\r\ndependencies {\r\n    compile project(':react-native-mixpanel')\r\n    compile project(':react-native-vector-icons')\r\n    compile project(':react-native-svg')\r\n    compile project(':react-native-splash-screen')\r\n    compile fileTree(dir: &quot;libs&quot;, include: [&quot;*.jar&quot;])\r\n    compile &quot;com.android.support:appcompat-v7:26.0.1&quot;\r\n    compile &quot;com.facebook.react:react-native:+&quot; \r\n    compile project(':react-native-push-notification')\r\n}\r\n\r\ntask copyDownloadableDepsToLibs(type: Copy) {\r\n    from configurations.compile\r\n    into 'libs'\r\n}\r\n\r\napply from: &quot;..\/..\/node_modules\/react-native-vector-icons\/fonts.gradle&quot;\r\n\r\n<\/pre>\n<h1>iOS<\/h1>\n<h4>Podfile<\/h4>\n<blockquote><p><span class=\"tlid-translation translation\" lang=\"en\"><span class=\"\" title=\"\">Located in the<\/span><\/span><strong><span class=\"tlid-translation translation\" lang=\"en\"><span class=\"\" title=\"\"> ios<\/span><\/span><\/strong><span class=\"tlid-translation translation\" lang=\"en\"><span class=\"\" title=\"\"> folder<\/span><\/span><\/p><\/blockquote>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\nplatform :ios, '9.0'\r\n\r\ntarget 'My_Mobile_App' do\r\n  pod 'Mixpanel'\r\nend\r\n<\/pre>\n<h1>Fastlane<\/h1>\n<blockquote><p><span class=\"tlid-translation translation\" lang=\"en\"><span class=\"\" title=\"\">All files are in the <\/span><\/span><strong><span class=\"tlid-translation translation\" lang=\"en\"><span class=\"\" title=\"\">fastlane<\/span><\/span><\/strong><span class=\"tlid-translation translation\" lang=\"en\"><span class=\"\" title=\"\"> folder<\/span><\/span><\/p><\/blockquote>\n<h4>Matchfile<\/h4>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\ngit_url(&quot;git@bitbucket.org:artem\/certificates.git&quot;)\r\ngit_branch(&quot;my_mobile_app&quot;)\r\n<\/pre>\n<h4>devices.txt<\/h4>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\nDevice ID\tDevice Name\r\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\t&quot;Artem iPhone&quot;\r\n<\/pre>\n<h4>Appfile<\/h4>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\napp_identifier &quot;com.mymobileapp&quot; # The bundle identifier of your app\r\n\r\napple_id &quot;artem@artem.services&quot;\r\n\r\nteam_id &quot;ABCDEFGHIJ&quot;\r\n<\/pre>\n<h4>Fastfile<\/h4>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\ndefault_platform(:ios)\r\n\r\njenkinsBuildNumber = ENV[&quot;BUILD_NUMBER&quot;]\r\n$buildNumber = &quot;10&quot; + jenkinsBuildNumber\r\n$versionNumber = ENV[&quot;APP_VERSION&quot;]\r\n  \r\nplatform :ios do\r\n  before_all do\r\n    puts 'ios: before_all'\r\n    cocoapods({\r\n      podfile: &quot;.\/ios\/Podfile&quot;\r\n    })\r\n    create_keychain(\r\n      name: &quot;my_mobile_app&quot;,\r\n      password: &quot;MY_PASSWORD&quot;,\r\n      default_keychain: true,\r\n      unlock: true,\r\n      timeout: false\r\n    )\r\n  end\r\n  #  Develop section\r\n  lane :adhoc do\r\n     register_devices(devices_file: &quot;.\/fastlane\/devices.txt&quot;)\r\n     match(git_url: &quot;git@bitbucket.org:artem\/certificates.git&quot;,\r\n      git_branch: &quot;my_mobile_app&quot;,\r\n      type: &quot;adhoc&quot;, \r\n      force_for_new_devices: true,\r\n      keychain_name: &quot;my_mobile_app&quot;,\r\n      keychain_password: &quot;MY_PASSWORD&quot;)\r\n  \r\n    increment_build_number(\r\n      xcodeproj:&quot;.\/ios\/my_mobile_app.xcodeproj&quot;,\r\n      build_number: $buildNumber\r\n    )\r\n    increment_version_number(\r\n      xcodeproj:&quot;.\/ios\/my_mobile_app.xcodeproj&quot;,\r\n      version_number: $versionNumber\r\n    )\r\n    automatic_code_signing(\r\n      path: &quot;.\/ios\/my_mobile_app.xcodeproj&quot;,\r\n      use_automatic_signing: false\r\n    )\r\n    gym({\r\n      xcargs: &quot;PROVISIONING_PROFILE_SPECIFIER='match AdHoc com.my_mobile_app' -UseNewBuildSystem='NO'&quot;,\r\n      codesigning_identity: &quot;iPhone Distribution: Artem Services (ABCDEFGHIJ)&quot;,\r\n      workspace: &quot;.\/ios\/my_mobile_app.xcworkspace&quot;,\r\n      scheme: &quot;my_mobile_app&quot;,\r\n      configuration: &quot;Release&quot;,\r\n      clean: true,\r\n      silent: true,\r\n      output_directory: &quot;.\/ios_build&quot;,\r\n      output_name: &quot;my_mobile_app_adhoc.ipa&quot;\r\n    })\r\n  end\r\n\r\n  lane :release do\r\n    register_devices(devices_file: &quot;.\/fastlane\/devices.txt&quot;)\r\n    match(git_url: &quot;git@bitbucket.org:artem\/certificates.git&quot;,\r\n      git_branch: &quot;my_mobile_app&quot;,\r\n      type: &quot;appstore&quot;, \r\n      force_for_new_devices: true,\r\n      keychain_name: &quot;my_mobile_app&quot;,\r\n      keychain_password: &quot;MY_PASSWORD&quot;)\r\n \r\n    increment_build_number(\r\n      xcodeproj:&quot;.\/ios\/my_mobile_app.xcodeproj&quot;,\r\n      build_number: $buildNumber\r\n    )\r\n    increment_version_number(\r\n      xcodeproj:&quot;.\/ios\/my_mobile_app.xcodeproj&quot;,\r\n      version_number: $versionNumber\r\n    )\r\n    gym({\r\n      xcargs: &quot;PROVISIONING_PROFILE_SPECIFIER='match AppStore com.my_mobile_app' -UseNewBuildSystem='NO'&quot;,\r\n      codesigning_identity: &quot;iPhone Distribution: Artem Services (ABCDEFGHIJ)&quot;,\r\n      workspace: &quot;.\/ios\/my_mobile_app.xcworkspace&quot;,\r\n      scheme: &quot;my_mobile_app&quot;,\r\n      configuration: &quot;Release&quot;,\r\n      clean: true,\r\n      silent: true,\r\n      output_directory: &quot;.\/ios_build\/&quot;,\r\n      output_name: &quot;my_mobile_app_appstore.ipa&quot;\r\n    })\r\n    upload_to_testflight(\r\n      ipa: &quot;.\/ios_build\/my_mobile_app_appstore.ipa&quot;\r\n    )\r\n  end\r\n\r\n   after_all do |lane|\r\n    puts 'ios: after_all'\r\n    delete_keychain(\r\n      name: &quot;my_mobile_app&quot;\r\n    )\r\n  end\r\n\r\n  error do |lane, exception|\r\n    delete_keychain(\r\n      name: &quot;my_mobile_app&quot;\r\n    )\r\n  end\r\nend\r\n\r\nplatform :android do\r\n  before_all do\r\n    puts 'android: before_all'\r\n  end\r\n\r\n  lane :release do\r\n    gradle(\r\n      task: &quot;clean&quot;,\r\n      project_dir: &quot;android\/&quot;\r\n     )\r\n    gradle(\r\n      task: &quot;assemble&quot;,\r\n      build_type: &quot;Release&quot;,\r\n      project_dir: &quot;android\/&quot;,\r\n      flags: &quot;--no-daemon --max-workers 1&quot;,\r\n      properties: {\r\n        &quot;versionCode&quot; =&gt; $buildNumber,\r\n        &quot;versionName&quot; =&gt; $versionNumber,\r\n        &quot;android.injected.signing.store.password&quot; =&gt; &quot;MY_PASSWORD&quot;,\r\n        &quot;android.injected.signing.key.alias&quot; =&gt; &quot;my_mobile_app&quot;,\r\n        &quot;android.injected.signing.key.password&quot; =&gt; &quot;MY_PASSWORD&quot;,\r\n      }\r\n    )\r\n  end\r\n\r\n  after_all do |lane|\r\n    puts 'android: after_all'\r\n  end\r\nend\r\n<\/pre>\n<h4>jenkins\/files_dotenv\/.my_mobile_app.dotenv<\/h4>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\nFASTLANE_PASSWORD='PASSWORD_FROM_MY_APPLE_ID_ACCOUNT'\r\nMATCH_PASSWORD='PASSWORD_FOR_DECRYPT_APPLE_CERTIFICATES'\r\n<\/pre>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Using Jenkins and Fastlane, we will build applications on iOS and Android, send artifacts to Slack, and also automatically send an iOS application to Testflight. The build is configured from the develop and release branches, and reads the release version (major and minor) from them, and adds the build number. For example: the branch is &hellip; <a href=\"https:\/\/artem.services\/?p=947&#038;lang=en\" class=\"more-link\">\u041f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u044c \u0447\u0438\u0442\u0430\u0442\u044c<span class=\"screen-reader-text\"> &quot;Jenkins &#8212; Fastlane build iOS and Android apps&quot;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[613],"tags":[695,697,363,673,617,491],"_links":{"self":[{"href":"https:\/\/artem.services\/index.php?rest_route=\/wp\/v2\/posts\/947"}],"collection":[{"href":"https:\/\/artem.services\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/artem.services\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/artem.services\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/artem.services\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=947"}],"version-history":[{"count":2,"href":"https:\/\/artem.services\/index.php?rest_route=\/wp\/v2\/posts\/947\/revisions"}],"predecessor-version":[{"id":951,"href":"https:\/\/artem.services\/index.php?rest_route=\/wp\/v2\/posts\/947\/revisions\/951"}],"wp:attachment":[{"href":"https:\/\/artem.services\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=947"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/artem.services\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=947"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/artem.services\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=947"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}