{"id":466,"date":"2019-01-04T14:40:11","date_gmt":"2019-01-04T11:40:11","guid":{"rendered":"https:\/\/artem.services\/?p=466"},"modified":"2019-02-05T12:39:31","modified_gmt":"2019-02-05T09:39:31","slug":"jenkins-fastlane-build-ios-and-android-apps","status":"publish","type":"post","link":"https:\/\/artem.services\/?p=466","title":{"rendered":"Jenkins &#8212; Fastlane build iOS and Android apps"},"content":{"rendered":"<p>\u041f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 <strong>Jenkins<\/strong> \u0438 <strong>Fastlane<\/strong> \u0431\u0443\u0434\u0435\u043c \u0441\u043e\u0431\u0438\u0440\u0430\u0442\u044c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u0430 <strong>iOS<\/strong> \u0438 <strong>Android<\/strong>, \u0431\u0443\u0434\u0435\u043c \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0430\u0440\u0442\u0435\u0444\u0430\u043a\u0442\u044b \u0432 <strong>Slack<\/strong>, \u0438 \u0442\u0430\u043a \u0436\u0435 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0434\u043b\u044f <strong>iOS<\/strong> \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0432 <strong>Testflight<\/strong>. \u0421\u0431\u043e\u0440\u043a\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u0430 \u0441 \u0432\u0435\u0442\u043e\u043a <strong>develop<\/strong> \u0438 <strong>release<\/strong>,\u00a0 \u0438 \u0441\u0447\u0438\u0442\u044b\u0432\u0430\u0435\u0442 \u0441 \u043d\u0438\u0445 \u0432\u0435\u0440\u0441\u0438\u044e \u0440\u0435\u043b\u0438\u0437\u0430 (<strong>major<\/strong> \u0438 <strong>minor<\/strong>), \u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442 \u043d\u043e\u043c\u0435\u0440 \u0441\u0431\u043e\u0440\u043a\u0438.<\/p>\n<p>\u041a \u043f\u0440\u0438\u043c\u0435\u0440\u0443: \u0432\u0435\u0442\u043a\u0430 &#8212; <strong>release\/1.0<\/strong> \u0438 \u043d\u043e\u043c\u0435\u0440 <strong>Jenkins<\/strong> \u0441\u0431\u043e\u0440\u043a\u0438 <strong>25<\/strong>, \u0442\u043e \u0432\u0435\u0440\u0441\u0438\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0431\u0443\u0434\u0435\u0442 &#8212; <strong>1.0.25<\/strong><\/p>\n<p>\u0414\u043b\u044f \u0441\u0431\u043e\u0440\u043a\u0438 \u0431\u0443\u0434\u0435\u0442 \u043d\u0443\u0436\u043d\u044b:<\/p>\n<ul>\n<li><a href=\"https:\/\/artem.services\/?p=453\" target=\"_blank\" rel=\"noopener\">\u041e\u0431\u0440\u0430\u0437 Docker&#8217;\u0430 c Android SDK<\/a><\/li>\n<li><a href=\"https:\/\/artem.services\/?p=433\" target=\"_blank\" rel=\"noopener\">\u0421\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u044b \u0434\u043b\u044f iOS &#8212; AdHoc \u0438 AppStore<\/a><\/li>\n<li><a href=\"https:\/\/artem.services\/?p=368\" target=\"_blank\" rel=\"noopener\">Android Release Key<\/a><\/li>\n<\/ul>\n<p>\u0414\u043b\u044f \u0443\u0434\u043e\u0431\u0441\u0442\u0432\u0430 \u043c\u043e\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0432\u0435\u0437\u0434\u0435 \u043d\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f: &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>\u041d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0432 \u043f\u0430\u043f\u043a\u0435 <strong>ios<\/strong><\/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>\u0412\u0441\u0435 \u0444\u0430\u0439\u043b\u044b \u043d\u0430\u0445\u043e\u0434\u044f\u0442\u0441\u044f \u0432 \u043f\u0430\u043f\u043a\u0435 <strong>fastlane<\/strong><\/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>\u041f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 Jenkins \u0438 Fastlane \u0431\u0443\u0434\u0435\u043c \u0441\u043e\u0431\u0438\u0440\u0430\u0442\u044c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u0430 iOS \u0438 Android, \u0431\u0443\u0434\u0435\u043c \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0430\u0440\u0442\u0435\u0444\u0430\u043a\u0442\u044b \u0432 Slack, \u0438 \u0442\u0430\u043a \u0436\u0435 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0434\u043b\u044f iOS \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0432 Testflight. \u0421\u0431\u043e\u0440\u043a\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u0430 \u0441 \u0432\u0435\u0442\u043e\u043a develop \u0438 release,\u00a0 \u0438 \u0441\u0447\u0438\u0442\u044b\u0432\u0430\u0435\u0442 \u0441 \u043d\u0438\u0445 \u0432\u0435\u0440\u0441\u0438\u044e \u0440\u0435\u043b\u0438\u0437\u0430 (major \u0438 minor), \u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442 \u043d\u043e\u043c\u0435\u0440 \u0441\u0431\u043e\u0440\u043a\u0438. \u041a \u043f\u0440\u0438\u043c\u0435\u0440\u0443: \u0432\u0435\u0442\u043a\u0430 &#8212; release\/1.0 \u0438 \u043d\u043e\u043c\u0435\u0440 Jenkins &hellip; <a href=\"https:\/\/artem.services\/?p=466\" 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":[37],"tags":[94,126,118,119,125,66],"_links":{"self":[{"href":"https:\/\/artem.services\/index.php?rest_route=\/wp\/v2\/posts\/466"}],"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=466"}],"version-history":[{"count":16,"href":"https:\/\/artem.services\/index.php?rest_route=\/wp\/v2\/posts\/466\/revisions"}],"predecessor-version":[{"id":585,"href":"https:\/\/artem.services\/index.php?rest_route=\/wp\/v2\/posts\/466\/revisions\/585"}],"wp:attachment":[{"href":"https:\/\/artem.services\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=466"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/artem.services\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=466"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/artem.services\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=466"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}