{"id":237978,"date":"2018-06-18T06:03:00","date_gmt":"2018-06-18T10:03:00","guid":{"rendered":"https:\/\/wordpress-756359-3782526.cloudwaysapps.com\/?p=237978"},"modified":"2025-05-30T07:45:00","modified_gmt":"2025-05-30T07:45:00","slug":"2021-06-18-lanes","status":"publish","type":"post","link":"https:\/\/www.travis-ci.com\/blog\/2021-06-18-lanes\/","title":{"rendered":"Changing lanes with Travis CI + Fastlane"},"content":{"rendered":"\n<p>Fastlane is one of those iOS development gems, that sometimes not everyone talks about because it\u2019s so helpful. Luckily I\u2019ll tell you all about it with a lengthy demo, code samples, and my GitHub repository with a working Fastlane\/Travis CI project.<\/p>\n\n\n\n<p>Fastlane is a Google project that I\u2019ve always found really useful. After doing a whole day\u2019s research, I was able to successfully use Fastlane. This tool is still very useful in my opinion when it comes to actually splitting tests. Some things I saw people forget, is the simple start. So in that case let\u2019s press continue, and get started!<\/p>\n\n\n\n<p>If you want to <a href=\"https:\/\/github.com\/Montana\/travis-fastlane\">checkout my repo<\/a> before you start, feel free!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"getting-in-motion\">Getting in motion<\/h2>\n\n\n\n<p>When using Fastlanes you should really remember if you\u2019re going to pass parameters from the command line to your lane, use the following syntax:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>fastlane &#91;lane] key:value key2:value2\nfastlane deploy submit:false build_number:24<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"passing-values-between-lanes\">Passing values between lanes<\/h2>\n\n\n\n<p>In theory, you can have as many&nbsp;<code>lanes<\/code>&nbsp;as you want, so you can invoke variant&nbsp;<code>lanes<\/code>. Example being:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>before_each do |lane, options|\n  # lane number (Montana likes # 3) \nend<\/code><\/pre>\n\n\n\n<p>Now within lanes, you can&nbsp;<em>INVOKE<\/em>&nbsp;other lanes for various other reasons, in other words, each lane can have different functions essentially:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>error do |lane, exception, options|\n  if options&#91;:debug]\n    puts \"This is a cool lane because you're in it with Montana\"\n  end\nend<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"switching-lanes\">Switching lanes<\/h2>\n\n\n\n<p>If you\u2019ve used&nbsp;<code>Fastlane<\/code>&nbsp;at length, you\u2019ll know switching&nbsp;<code>lanes<\/code>&nbsp;is usually called&nbsp;<code>lane hopping<\/code>. You can lane hop feasibly, without using too much CPU:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>lane :deploy do |options|\n  # lane 3 \n  build(release: true) # don't forget this part! - Montana\n  Montana Mendy\n  # invoking lane 4 with a conditional \nend<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"querying-lanes\">Querying lanes<\/h2>\n\n\n\n<p>Just like anything you query, you can retrieve the return value in this case it\u2019s through a&nbsp;<code>lane callback<\/code>. In Ruby, the last line of the lane definition is the return value. Here is an example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>lane :deploy do |options|\n  value = calculate(value: 3)\n  puts value # =&gt; 5\nend\n\nlane :calculate do |options|\n  # ...\n  2 + options&#91;:value] # This is always the return value line. - Montana \nend<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"executing-lanes-early\">Executing lanes early<\/h2>\n\n\n\n<p>You in theory never want to reach the end of a&nbsp;<code>lane<\/code>, and if you do &#8211; you\u2019re not going to be executing much longer:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>lane :build do |options|\n  if cached_build_available?\n    UI.important 'Montana cached this Lane!'\n    next # Montana is skipping doing the rest of this lane\n  end\n  match\n  gym\nend\n\nprivate_lane :cached_build_available? do |options|\n  # ...\n  true\nend<\/code><\/pre>\n\n\n\n<p>So while executing&nbsp;<code>lanes<\/code>&nbsp;&#8211; there are conditionals you can use, such as&nbsp;<code>next<\/code>&nbsp;is used during a&nbsp;<code>lane<\/code>&nbsp;and&nbsp;<code>switch<\/code>,&nbsp;<code>control<\/code>&nbsp;returns to the previous lane that was executing, so&nbsp;<code>lanes<\/code>&nbsp;in theory can revert back and forth.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>lane :first_lane do |options|\n  puts \"If you run: `fastlane first_lane`\"\n  puts \"You'll see this!\"\n  second_lane\n  puts \"Montana is in this lane!\"\nend\n\nprivate_lane :second_lane do |options|\n  next\n  puts \"Hidden!\"\nend<\/code><\/pre>\n\n\n\n<p>When you stop executing a&nbsp;<code>lane<\/code>&nbsp;early with next (<code>lane switching<\/code>), any&nbsp;<code>after_each<\/code>&nbsp;and&nbsp;<code>after_all<\/code>&nbsp;blocks you have will still trigger as usual&nbsp;<code>:+1:<\/code>. This would include being called (via a&nbsp;<code>lane callback<\/code>) before each lane you\u2019ve switched to.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>before_each do |lane, options|\n  # ...\nend<\/code><\/pre>\n\n\n\n<p>So this is a good reminder from myself, that when doing&nbsp;<code>after_each<\/code>&nbsp;blocks are called (again, by the&nbsp;<code>lane callback<\/code>) after any lane is called. This would include being called after each lane you\u2019ve switched to. Just like&nbsp;<code>after_all<\/code>,&nbsp;<code>after_each<\/code>&nbsp;is not called if an error occurs. The error block should be used in this case.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"feasibility-of-lanes\">Feasibility of lanes<\/h2>\n\n\n\n<p>With all these scenarios,&nbsp;<code>before_each<\/code>&nbsp;and&nbsp;<code>after_each<\/code>&nbsp;would be called 4 times, by the&nbsp;<code>lane callback<\/code>. Remember before the deploy lane, before the switch to archive, sign, and upload, and after each of these lanes as well, a lot of users forget.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"lane-context\">Lane context<\/h2>\n\n\n\n<p>Now&nbsp;<code>Fastlane<\/code>&nbsp;has different actions that can communicate with each other using a shared hash, in other words, communicating with other&nbsp;<code>lanes<\/code>. You can access this in your (lanes, actions, plugins etc.):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>lane_context&#91;SharedValues::BUILD_NUMBER]                # Generated by `increment_build_number`\nlane_context&#91;SharedValues::VERSION_NUMBER]              # Generated by `increment_version_number`\nlane_context&#91;SharedValues::SNAPSHOT_SCREENSHOTS_PATH]   # Generated by _snapshot_\nlane_context&#91;SharedValues::PRODUCE_APPLE_ID]            # The Apple ID of the newly created app\nlane_context&#91;SharedValues::IPA_OUTPUT_PATH]             # Generated by _gym_\nlane_context&#91;SharedValues::DSYM_OUTPUT_PATH]            # Generated by _gym_\nlane_context&#91;SharedValues::SIGH_PROFILE_PATH]           # Generated by _sigh_\nlane_context&#91;SharedValues::SIGH_UDID]                   # The UDID of the generated provisioning profile\nlane_context&#91;SharedValues::HOCKEY_DOWNLOAD_LINK]        # Generated by `hockey`\nlane_context&#91;SharedValues::GRADLE_APK_OUTPUT_PATH]      # Generated by `gradle`\nlane_context&#91;SharedValues::GRADLE_ALL_APK_OUTPUT_PATHS] # Generated by `gradle`\nlane_context&#91;SharedValues::GRADLE_FLAVOR]               # Generated by `gradle`\nlane_context&#91;SharedValues::GRADLE_BUILD_TYPE]           # Generated by `gradle`<\/code><\/pre>\n\n\n\n<p>To feasibly get information about available lane variables, available lanes to switch on, run&nbsp;<code>fastlane action [action_name]<\/code>&nbsp;or look at the generated table in the action documentation.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"lane-properties\">Lane properties<\/h2>\n\n\n\n<p>It can be useful to dynamically access some properties of the current lane. These are available in the&nbsp;<code>lane_context<\/code>&nbsp;as well:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>lane_context&#91;SharedValues::PLATFORM_NAME]        # Platform name, e.g. `:ios`, `:android` or empty (for root level lanes)\nlane_context&#91;SharedValues::LANE_NAME]            # The name of the current lane preceded by the platform name (stays the same when switching lanes)\nlane_context&#91;SharedValues::DEFAULT_PLATFORM]     # Default platform<\/code><\/pre>\n\n\n\n<p>They are also available as environment variables:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ENV&#91;\"FASTLANE_PLATFORM_NAME\"]\nENV&#91;\"FASTLANE_LANE_NAME\"]<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"private-lanes\">Private lanes<\/h2>\n\n\n\n<p>Sometimes you might have a lane that is used from different lanes, or a lane from a&nbsp;<code>hopped lane<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>lane :production do\n  # ...\n  build(release: true)\n  appstore # Deploy to the Montana's brain\n  # ...\nend<\/code><\/pre>\n\n\n\n<p>-&gt;<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>lane :beta do\n  # ...\n  build(release: false)\n  crashlytics # Distribute to testers\n  # ...\nend<\/code><\/pre>\n\n\n\n<p>-&gt;<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>lane :build do |options|\n  # ...\n  Montana likes beer\n  # ...\nend\nIt probably doesn't make sense to execute the build lane directly using fastlane build. You can hide this lane using<\/code><\/pre>\n\n\n\n<p>-&gt;<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>private_lane :build do |options|\n  # ...\nend<\/code><\/pre>\n\n\n\n<p>This will hide the lane from:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>fastlane lanes\nfastlane list\nfastlane docs<\/code><\/pre>\n\n\n\n<p>And also, you can\u2019t call the private lane using fastlane build. The resulting private lane can only be called from another lane using the lane switching technology, or the&nbsp;<code>lane callback<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"control-configuration-by-lane-and-by-platform\">Control configuration by lane and by platform<\/h2>\n\n\n\n<p>In general, configuration files take only the first value given for a particular configuration item. That means that for an Appfile like the following:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>app_identifier \"com.used.id\"\napp_identifier \"com.ignored.id\"<\/code><\/pre>\n\n\n\n<p>So,&nbsp;<code>the app_identfier<\/code>&nbsp;will be \u201ccom.used.id\u201d and the second value will be ignored. The for_lane and for_platform configuration blocks provide a limited exception to this rule.<\/p>\n\n\n\n<p>All configuration files (Appfile, Matchfile, Screengrabfile, etc.) can use&nbsp;<code>for_lane<\/code>&nbsp;and&nbsp;<code>for_platform blocks<\/code>&nbsp;to control (and override) configuration values for those circumstances.<\/p>\n\n\n\n<p>So&nbsp;<code>for_lane<\/code>&nbsp;blocks will be called when the name of lane invoked on the command line matches the one specified by the block. So, given a Screengrabfile like:<\/p>\n\n\n\n<p>For the locales (language lane):&nbsp;<code>locales ['en-US', 'fr-FR', 'ja-JP']<\/code><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>for_lane :screenshots_english_only do\n  locales &#91;'en-US']\nend<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>for_lane :screenshots_french_only do\n  locales &#91;'fr-FR']\nend<\/code><\/pre>\n\n\n\n<p>So as you can see, the locales will have the values [\u2018en-US\u2019, \u2018fr-FR\u2019, \u2018ja-JP\u2019] by default, but will only have one value when running the fastlane&nbsp;<code>screenshots_english_only<\/code>&nbsp;or&nbsp;<code>fastlane screenshots_french_only<\/code>.<\/p>\n\n\n\n<p>Then,&nbsp;<code>for_platform<\/code>&nbsp;gives you similar control based on the platform for which you have invoked fastlane. So, for an Appfile configured like:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>app_identifier \"com.default.id\"\n\nfor_lane :enterprise do\n  app_identifier \"com.forlane.enterprise\"\nend<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>for_platform :mac do\n  app_identifier \"com.forplatform.mac\"\n\n  for_lane :release do\n    app_identifier \"com.forplatform.mac.forlane.release\"\n  end\nend<\/code><\/pre>\n\n\n\n<p>So now you can expect the&nbsp;<code>app_identifier<\/code>&nbsp;to equal \u201ccom.forplatform.mac.forlane.release\u201d when invoking Fastlane mac release.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"is-it-plausible-to-change-the-fastlane-execution-folder-while-inside-a-lane\">Is it plausible to change the fastlane execution folder while inside a lane?<\/h2>\n\n\n\n<p>Yes, you can do this. The way I\u2019ve classically done this is make two fastlane lanes, one for the old location, one for the new, so then your new script looks somethng like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cd old-location\nfastlane old_lane\ncp -r old-location new-location\ncd new-location\nfastlane new_lane<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"setting-fastlane-up-in-ios-for-different-export-methods-and-provision-profiles\">Setting fastlane up in iOS for different export methods and provision profiles?<\/h2>\n\n\n\n<p>So my guess is that anyway your project is scoped you have more than 1&nbsp;<code>bundle<\/code>&nbsp;identifier to the different apps in question.<\/p>\n\n\n\n<p>Remember with Fastlane you can setup a new target and just create the same lane with a different scheme for that target (or build a function and send the correct scheme as a parameter). You will also have to create a different&nbsp;<code>bundle id<\/code>.<\/p>\n\n\n\n<p>If you don\u2019t wish to create a different target and you are you using Automatic signing on your project, (some SSO), you will have to change it to manual and specify the provisioning profile there.<\/p>\n\n\n\n<p>Please make sure there\u2019s nothing that could conflict with Travis or Fastlane, so it should look like something like this:<\/p>\n\n\n\n<p>Prod<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>  build_app(\n  workspace: \"XXXX.xcworkspace\",\n  scheme: \"XXXXX\",\n  ......\n  export_options: {\n    method: \"app-store\",\n    signingStyle: 'manual',\n    provisioningProfiles: {\n      \"bundle id\": \"Prod profile full name\",\n    }\n  })<\/code><\/pre>\n\n\n\n<p>Ad-hoc<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code> build_app(\n  workspace: \"XXXX.xcworkspace\",\n  scheme: \"XXXXX\",\n  ......\n  export_options: {\n    method: \"ad-hoc\",\n    signingStyle: 'manual',\n    provisioningProfiles: {\n      \"bundle id\": \"Montana's project\",\n    }\n  })<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"can-fastlane-skip-before_all-or-after_all-in-certain-lanes-in-travis\">Can fastlane skip&nbsp;<code>before_all<\/code>&nbsp;or&nbsp;<code>after_all<\/code>&nbsp;in certain lanes in Travis?<\/h2>\n\n\n\n<p>You certainly can with Travis and Fastlane. One way to accomplish this with certain&nbsp;<code>lanes<\/code>&nbsp;would be the following:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>before_all do |lane|\n  if lane == :test\n    puts \"Do something for test\"\n  else\n    puts \"Montana\"\n  end\nend<\/code><\/pre>\n\n\n\n<p>Additionally:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>lanes = &#91;:test, :foo, :bar]\nlanes.include?(:test) # =&gt; true\nlanes.include?(:baz) # =&gt; false<\/code><\/pre>\n\n\n\n<p>So the final output would look like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>before_all do |lane|\n  lanes_to_say_foo = &#91;:test, :build, :other]\n  if lanes_to_say_foo.include?(lane)\n    puts \"Montana\"\n  end\nend<\/code><\/pre>\n\n\n\n<p>Happy building! Any other questions please feel free to contact&nbsp;<a href=\"mailto:montana@travis-ci.com\">montana@travis-ci.com<\/a>, and I\u2019ll be as quick as I can in the Fastlane to answer your question.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Fastlane is one of those iOS development gems, that sometimes not everyone talks about because it\u2019s so helpful. Luckily I\u2019ll tell you all about it with a lengthy demo, code samples, and my GitHub repository with a working Fastlane\/Travis CI project. Fastlane is a Google project that I\u2019ve always found really useful. After doing a [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":237982,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_breakdance_hide_in_design_set":false,"_breakdance_tags":"","footnotes":""},"categories":[16],"tags":[7,19,20,5],"class_list":["post-237978","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-news","tag-community","tag-feature","tag-infrastructure","tag-news"],"_links":{"self":[{"href":"https:\/\/www.travis-ci.com\/wp-json\/wp\/v2\/posts\/237978","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.travis-ci.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.travis-ci.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.travis-ci.com\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.travis-ci.com\/wp-json\/wp\/v2\/comments?post=237978"}],"version-history":[{"count":1,"href":"https:\/\/www.travis-ci.com\/wp-json\/wp\/v2\/posts\/237978\/revisions"}],"predecessor-version":[{"id":241353,"href":"https:\/\/www.travis-ci.com\/wp-json\/wp\/v2\/posts\/237978\/revisions\/241353"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.travis-ci.com\/wp-json\/"}],"wp:attachment":[{"href":"https:\/\/www.travis-ci.com\/wp-json\/wp\/v2\/media?parent=237978"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.travis-ci.com\/wp-json\/wp\/v2\/categories?post=237978"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.travis-ci.com\/wp-json\/wp\/v2\/tags?post=237978"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}