Integrating CI/CD for MULTIPLE environments with Jenkins & Fastlane (Part 2/2)

Eleni Papanikolopoulou
4 min readSep 19, 2020

--

Configure Jenkins for different environments 🖇

If we consider the goal that we defined in the previous part, we have managed to create a Jenkins job in order to upload our application to Testflight for different feature branches. Therefore, what is left is the ability to do this for different environments for example a staging environment and a test production environment. Different environments are equivalent to different Xcode configurations (i.e different custom schemes) with specific build settings each. If we take a look to the three files above, MyScipt.groovy , Deploy.groovy and Fastfile, we will notice that there are some properties that are configuration specific which are:

  1. The app identifier
  2. The configuration which corresponds to the different scheme created in Xcode
  3. The provisioning profile name

These properties are used in various places, mostly inside the Fastlane lanes. Consequently, in order to achieve our goal we should pass those properties as parameters in the lanes and use different values for each configuration. The syntax to have parameters in a lane is the following:

lane :build do |options|

and then a parameter can be obtained as follows:

parameter = options[:parameter_name]

So in our case, we need three different parameters as described above.

Now let’s say we have an environment called Staging and another one called TestProduction. We will need to implement two different jobs in Jenkins, identical to the job defined above, and connect it to two different scripts let’s say Stg.groovy and TestProduction.groovy. Those scripts will be identical apart from the following three different parameters:

And the way we will use get those inside the build lane is:

lane :build do |options|bundle_id = options[:bundle_id]
configuration = options[:configuration]
provisioning_profile = options[:provisioning_profile]
.
.
.
end

So the deploy function inside the Deploy.script will now get those parameters in the method signature:

def deployWith(bundle_id, configuration, provisioning_profile) {.
.
.
}

Now the stages that use these parameters are the Run Tests stage, the Build stage and the Upload to Testflight stage, so these are the stages that need parameterization. The others (Checkout repo, Install dependencies, Reset simulators, and Cleanup at the end) will remain the same.

Configure the Run Tests stage

So we implement the Run tests stage by passing the configuration as a parameter in order to be used inside the corresponding lane as follows:

stage('Run Tests') {
sh 'bundle exec fastlane test configuration:$configuration'
}

Now the difficult part is to configure the test lane inside the Fastfile to use this parameter. The way to obtain the parameters is the following:

lane :a_lane do |options|
....
bundle_id = options[:bundle_id]
configuration = options[:configuration]
provisioning_profile = options[:provisioning_profile]
...
end

So the test lane, which needs to use only the configuration parameter, will now be as follows:

We can notice that in the scheme field we use the configuration parameter since the scheme is different for the different environments.

Configure the Build stage

The Build stage needs all three parameters. So we implement the Fastlane build command by passing those three parameters. The Build stage now inside the deployWith() function will be as follows:

This means that the build lane will be exactly the same as before apart from the commands that use those three parameters:

It’s easy to notice that wherever the bundle_id, configuration and provisioning_profiles values are used we use the parameter values instead of the hardcoded values.

Configure the Upload To Testflight stage

So we use the upload_to_testflight Fastlane command by passing the bundle_id as parameter. The Upload to TestFlight stage now inside the deployWith() function of the Deploy.groovy file will be as follows:

Consequently the corresponding lane inside the Fastfile will now use the bundle_id as follows:

And that’s it! We have managed to use the same Deploy.groovy and Fastfile for different configurations. But how are we going to separate those parameters for the different configurations?

In Jenkins, instead of having one job for all configurations, we can now create different jobs for each configuration which will be identical apart from the script they use. Therefore, as I mentioned earlier, we create a Stg job and we use the following Stg.groovy script:

Same for the TestProduction configuration we create a new identical job and we use the following TestProduction.groovy script:

Notice that all of them use the same Deploy.groovy script and the same Fastfile which are the following:

End Goal Achieved 👏

As I mentioned in a previous section, our end goal was to be able to upload to Testflight, automatically with the click of one button:

a) for multiple feature branches

b) for multiple configurations corresponding to a different environment

We have managed to achieve the afore mentioned, by having different jobs in Jenkins, which can be configured with a different branch. Each job corresponds to a different Xcode configuration and in extension, to a different environment, all of the them using the same 2 main files (Deploy.groovy and Fastlane) and different initial script files (Stg.groovy and TestProduction.groovy).

Of course, this can extend for multiple environments with the implementation of a simple initial script with only a few lines of code!

Thank you for bearing with me 😊 and I hope this proves helpful 🙏!

Special thanks to Pavlos-Petros Tournaris and George Tsifrikas for their precious feedback.

Twitter: @elenipapanikolo

--

--