Continuous Integration with Jenkins

In september 2016 a blogpost on jenkins.io announced Blue Ocean and with it declarative pipelines and with that the build and deployment process can be managed from within the repository. In the meantim the plugins are well hung and additional plugins have been developed.

Travis uses .travis.yml, GitLab uses .gitlab-ci.yml, BITRISE uses bitrise.yml and Drone uses .drone.yml. Finally jenkins followed using the Jenkinsfile. Not only is it possible to manage the process in the CVS, it also allows fast and easy copying among projects.

Team Projects

Once Jenkins is setup the first question is what kind of project one should choose. For iOS development the freestyle project was the way to go. With the update two new plugins came along. The Bitbucket Branch Source Plugin and the GitHub Branch Source Plugin which allow adding team folders.

Bitbucket Team/Project

Once configured, the jenkins server regularly scans all the teams repositories for branches with a Jenkinsfile and configures projects for the repositories and jobs for the branches. New projects don’t have to be configured within Jenkins. It’s sufficient to add a Jenkinsfile in the base folder of the project. Jenkins also configures a job for every pull request and runs every commit both in the context of the pull request and in the context of the branch. To avoid that you can use the options Discover branches or Automatic branch project triggering.

Discover branches Automatic branch project triggering

Blue Ocean

Blue Ocean is the new Jenkins interface, that runs in addition to the classic Jenkins interface. Day to day tasks can be done in the new interface but specific configuring tasks are only possible in the old one.

All jobs for a specific branch are shown together with their status.

List of all Jobs in Blue Ocean

Jenkinsfile

The Build Pipeline is configured in a Jenkinsfile in the root folder of the project. To quickly find a build error, it is helpfull to define different stages, which cover different scopes. For example checkout, prepare, tests, codestyle, deployment and archive.

All tasks (aside of configurations) have to be executed on a node. A node can be the Jenkins server itself but to be able to scale it is helpfull to add additional nodes. Every node can be assigned different tags that describe satisfied requirements. You can either chose macos and linux or be more specific using xcode9, nodejs, ruby2.1.6 and so on.

An example Jenkinsfile could look like this:

#!groovy

// Defines the number of builds and artifacts to store.
properties([buildDiscarder(logRotator(artifactDaysToKeepStr: '30', artifactNumToKeepStr: '50', daysToKeepStr: '30', numToKeepStr: '50'))])

// These stages require a node with the taks xcode8 and fastlane
node('xcode8 && fastlane') {

  // Activates the Ansi Color Plugin for the enclosed stages
  wrap([$class: 'AnsiColorBuildWrapper', 'colorMapName': 'XTerm', 'defaultFg': 1, 'defaultBg': 2]) {

    stage('Checkout') {
      checkout scm
    }

    stage('Bundle install') {
      sh 'bundle update'
      sh 'bundle install'
    }

    stage('Tests') {
      sh "killall Simulator || true"
      sh "SNAPSHOT_FORCE_DELETE=yes snapshot reset_simulators"
      sh 'fastlane test sdk:"9.3" device:"iPhone 5s"'
      sh 'fastlane test sdk:"10.2" device:"iPhone 5s"'
      publishHTML([allowMissing: true, alwaysLinkToLastBuild: false, keepAll: false, reportDir: 'build/reports/', reportFiles: 'report.html', reportName: 'HTML Test Report'])
      junit allowEmptyResults: true, testResults: 'build/reports/report.junit'
      archiveArtifacts allowEmptyArchive: true, artifacts: 'build/reports/*', onlyIfSuccessful: true
    }
  }
}

The Ansi Color Plugin provides support for ANSI Escape Characters and optimizes the representation of console output.

Every build can be inspected and lists all stages. It will show test results, archived artifacts and provides ways to restart a build if necessary.

View of a single build in Blue Ocean