In software development process, one practice became very popular, even mandatory, and that practice is continuous integration. Depending on the technology used in the project, continuous integration can be integrated in more ways.
In this article we will present a solution to continuous integration from the perspective of mobile app development for iOS platform. Our solution relies on Xcode Server and on Fabric.IO which is a online distribution framework.
Continuous integration is a practice in which the code of more software developers in a project is merged in a shared branch under a source code versioning system. This practice was first proposed by Grady Booch in 1991. The purpose of CI is to help solve the problems that may appear when the local branches that developers have, need to be merged in the main branch. It's a good practice that developers should merge the main branch into their local branch once a day, this way the probability to have merge conflicts is reduced.
Continuous Integration doesn't stop here, it covers also the unit, UI and feature tests that can be created and run each time a branch is merged in the main branch of the project. Once the tests ran successfully a build can be created and sent to QA(quality assurance) team where more tests can be done manually.
A good practice is this: if your code on your local branch has unit tests and you integrated the project main branch into your local one, you should run unit tests in order to see if your changes didn't break those tests.
There are numerous systems out there that offer continuous integration, and the most popular of them is Jenkins. Jenkins offers a lot of extensions for running scripts, connecting to git, gitlab or other source code versioning system, sending email notifications and a lot more. Beside projects created with Xcode, Jenkins can be used with projects created in other IDE as well, Visual Studio for example. To find out more about Jenkins you can read the official documentation.
Apple provides a solution for Continuous Integration in the form of Xcode server that is a part of mac OS Server.
Mac OS Server can be installed from App Store or from your developer account from Apple developer program. More details on installing OS Server can be found in the official documentation.
Once Xcode server is configured you can create a bot for your project by opening the project, and from Product menu you choose Create Bot...
You then follow the instructions of the Wizard.
Firstly, the new bot needs a name, this can be set in Name field from figure 2, then an IP address is required, the IP of the machine where mac OS runs.
After you signed in to the source code versioning system you then need to select the branch of your project that the bot will build. For our project we selected the develop branch, which is also the main branch of the project.
The next wizard screen illustrates the build configuration settings. According to Apple Documentation, the scheme aggregates multiple targets to build, a build configuration and tests to execute.
From here you check the option Shared for the scheme that the bot will build. You can see an example in the next image.
After you selected a shared scheme the action we are interested in is "perform archive", but other options are available like analyze or run a batch of tests. Cleaning refers to emptying the cache and the temporary data of the project. For the configuration option there are two values that can be set: use the scheme setting or "release", as our goal is to distribute the application to a group of testers, not to debug it. In our case, we already selected "release" in our scheme as the build configuration for archiving.
The next step is to decide when the Xcode Bot performs the build. We selected "On Commit" option, because we demand each time a feature branch is merged into the main branch, a new build be released. Even if you have more options for Schedule, after you completed the wizard a first time integration will be made, regardless of the option you have selected.
Screen from figure 8 enables you to choose the hardware devices or simulators on which the tests should be run.
For various purposes, you can define new environment variables(Fig.9), apart from those that are already created by Xcode Server(i.e. XCS_INTEGRATION_ID, XCS_BOT_NAME, etc.).
There are 4 types of triggers:
Pre-Integration Scripts
Post-Integration Scripts
Email on New Issues
We are particularly interested in the second type of triggers as we will create a script for automatically setting the build number(Fig.10). For now, it is not mandatory to create a post-integration script as we will provide more details later on.
When it comes to mobile apps the newly implemented features will go to QA team that tests them well, and this should happen as fast as possible. Here there are two choices:
sending the build manually or
Using a CI infrastructure will be faster than do it manually and what is best with CI is that each time a code merge happens in the main branch of the project the CI will make a build, run tests and can send a build to testers.
Incrementing the build number can be very easily automated using the command-line tool called agvtool. The utility is used in a shell script that is launched before every release build. The issue with auto-incrementing the build number lays in the versioning system detecting it as a change and that change has to be committed manually. In order to avoid this problem, we bring forward the idea of setting the build number according to the integration number from the Xcode Server. The value is obtained from the environment variable named "XCS_INTEGRATION_NUMBER" and it does not imply any versioning system changes.
The next script is added to the "Build Phases" of the current target in Xcode:
#!/bin/sh
if [ "${CONFIGURATION}" = "Release" ]; then
agvtool new-version -all "$XCS_INTEGRATION_NUMBER"
fi
Usually, the release notes consist of a text describing what features have been implemented or what problems have been solved and come with the application build when it is distributed to the testing team. This information is most often manually typed in by the person performing the distribution, but we consider that this task can be elegantly automated.
Our solution relies on the fact that Git is the most widely used version control system and that the development process follows the Feature Branch Workflow. The integration is triggered every time a feature branch is merged into the main branch. The release notes are extracted from the commit messages of the branch on which the development took place (also known as the feature branch) and are not to be found on the main branch. Firstly, we check whether the "hash.txt" file exists and contains any commit hash. For a better understanding, we shall abbreviate this commit hash OCH (old commit hash). Also, it is worth mentioning that HEAD refers to the last commit of the main branch. Identifying the commit messages to be added into the current release notes implies using the "git log" function with hash boundaries from the OCH to HEAD. The output of this function is written to the "release_notes.txt" file by using a format comprising only the author name and the message of each commit. Additionally, the merge commits are filtered out, as they do not contain representative information. In case of empty release notes, we simply set the release notes to "No changes since last build". Next, we save the OCH in the file "hash.txt" and perform the Fabric.IO submission.
The previously mentioned actions are packed into a lightweight shell script that is run after each successful build stage. To enable this behavior, the programmer must open the "Triggers" section of the "Edit Bot" wizard, click the "+" button and select "Post-Integration Script".
#!/bin/sh
CM_HASH=$(cat $XCS_SOURCE_DIR/hash.txt)
echo git --git-dir $XCS_SOURCE_DIR/<NAME_OF_GIT_PROJECT>/.git log --no-merges $CM_HASH..HEAD --format="%cn: %s" > RELEASE_NOTES
if [ "${RELEASE_NOTES}" = "" ]; then
RELEASE_NOTES = "No changes since last build."
fi
echo "${RELEASE_NOTES}" > $XCS_SOURCE_DIR/release_notes.txt
git --git-dir $XCS_SOURCE_DIR/<NAME_OF_GIT_PROJECT>/.git rev-parse HEAD > $XCS_SOURCE_DIR/hash.txt
"${XCS_SOURCE_DIR}/<PROJECT_CRASHLYTICS_PATH>/Crashlytics/submit" <API_KEY> <BUILD_SECRET> -groupAliases <TESTING_GROUP_NAME> -ipaPath "${XCS_PRODUCT}" -notesPath $XCS_SOURCE_DIR/release_notes.txt
Notes: XCS_SOURCE_DIR is an Xcode Server environment variable which contains the path to the source code repositories directory.
After implementing the solution described in this article, we learned how to integrate git with Xcode Server and how to send the build to QA team with the help of Fabric.IO framework.
The ease of running automated tests and having reports on errors along side with build distribution to QA team quickly, are the advantages that determines us to use continuous integration solution.
https://developer.apple.com/library/content/featuredarticles/XcodeConcepts/Concept-Schemes.html