Continuous Integration — GitLab — iOS — Part 2

Rakesh Chander
4 min readOct 23, 2020

We have covered SetUp of CI in gitlab along with Build & Test jobs in my medium story “Continuous Integration — GitLab — iOS — Part 1”. Go through this for all preliminary details.

In this story we’ll cover up Lint & SONAR (Static Code Analysis) integration in CI pipelines.

Its always best practices to lint code before taking up Merge Requests. Linting can also be automated via GitLab Pipeline and Reviewer / Developer both are always sure that code pushed is not having any lint issues.

In general, we use SwiftLint in iOS Projects for linting. SwiftLint YML file is added to project root and rules are defined as per project needs. Developers start getting warnings / errors from SwiftLint in their local machines itself which they can fix on the go. But they can still push code, here comes the CI part which will make sure that all lint issues are fixed, otherwise pipeline will start failing.

SwiftLint has added more beauty to this in such a way that we can define customised location from where swiftlint.yml to be picked for CI — making sure that no rules are being changed by developers locally in the project. At gitlab runner machine we can place yml file and specify that location in gitlab yml for job execution.

Reviewers can keep on adding more rules as and whne required at runner machine only, to automate their review rules — frankly speaking, I have offloaded most of my review checks to SwiftLint by adding rules. You can check SwiftLint - Advanced story for such rules.

LINT — Run SwiftLint

We should check for lint issues even before the build validation to make sure no lint level mistakes are there to compromise with Best Practices.

I reommend creating a separate pre_build.sh file, which you can use for any pre-build activity required. Place this file in runner machine at some defined location. Refer below for SwiftLint scripts in pre_build.sh -

export PATH=/usr/local/bin:$PATHLINT=$(which swiftlint)if [[ -e "${LINT}" ]]; thenecho "SwiftLint Start..."echo $(pwd)elseecho "SwiftLint does not exist, download from https://github.com/realm/SwiftLint"exit 1fiRESULT=$($LINT lint --quiet --strict --config $HOME/Documents/Pre-Build/.swiftlint.yml)if [ "$RESULT" == '' ]; thenprintf "SwiftLint Finished.\n"elseprintf "SwiftLint Failed. Please check below:\n"while read -r line; doFILEPATH=$(echo $line | cut -d : -f 1)L=$(echo $line | cut -d : -f 2)C=$(echo $line | cut -d : -f 3)TYPE=$(echo $line | cut -d : -f 4 | cut -c 2-)MESSAGE=$(echo $line | cut -d : -f 5 | cut -c 2-)DESCRIPTION=$(echo $line | cut -d : -f 6 | cut -c 2-)printf "\n $TYPE\n"printf "    $FILEPATH:$L:$C\n"printf "    $MESSAGE - $DESCRIPTION\n"done <<< "$RESULT"echo "PIPELINE ABORTED. Please fix them and push again."exit 1fi
  • To automate this, we will edit existing “build_core_all” job itself
  • Just before xcodebuild command we will add script to execute pre_build.sh
  • Refer below snippet —
stages:
- test
- build
build_core_all:
stage: build
script:
- rm -rf Pods
- rm -rf Podfile.lock
- pod install
- bash $HOME/Documents/Pre-Build/pre_build.sh
- xcodebuild -workspace <WORKSPACE_NAME>.xcworkspace -scheme <SCHEME_NAME> clean build CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGN_ENTITLEMENTS="" CODE_SIGNING_ALLOWED=NO -destination generic/platform=iOS
except:
- tags

test_all:
stage: test
script:
- rm -rf Pods
- rm -rf Podfile.lock
- pod install
- xcodebuild -workspace <WORKSPACE_NAME>.xcworkspace -scheme <SCHEME_NAME> clean test -destination 'platform=iOS Simulator,name=iPhone 11,OS=14.0' -enableCodeCoverage YES
except:
- tags
- master

SONAR Analysis — Validate SONAR status

We should have SONAR analysis and validation as part of CI to make sure we are not adding any Techincal Debt & CodeCoverage etc are as per expected level and compliant with organisation level rules.

We will add a new stage named “sonar” and corresponding job which executes only on develop branch.

To setup SONAR — with Code coverage — go through this.

  • Download SONAR-Scanner at your runner machine and place at defined location.
  • Create file “sonar_validation.sh” at project root and add below commands to that. Update “SONAR_SERVER_DOMAIN” in below file with your server url.
#!/bin/bashfile="./sonar-project.properties"if [ -f "$file" ]thenecho "$file found."projectKey=`sed '/^\#/d' $file | grep 'sonar.projectKey'  | tail -n 1 | cut -d "=" -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//'`projectKey=${projectKey/":"/"%3A"}#echo project key is = $projectKeyelseecho "$file not found."fiURL=http://<SONAR_SERVER_DOMAIN>/sonar/api/qualitygates/project_status?projectKey=$projectKey#echo "url being hit is $URL"RESPONSE=$(curl -X GET $URL)PROJECTSTATUS=$( jq -r  '.projectStatus|.status' <<< "${RESPONSE}" )echo $PROJECTSTATUSif [ "$PROJECTSTATUS" = "OK" ];thenecho "This means that the quality gate has passed"elseecho "This means that quality gate has failed"exit 1fi
  • Now, once you have added “sonar-project.properties” & “xcode-coverage-mapper.sh” files at your project root, update your gitlab.yml as per below -
stages:
- test
- build
- sonar
build_core_all:
stage: build
script:
- rm -rf Pods
- rm -rf Podfile.lock
- pod install
- bash $HOME/Documents/Pre-Build/pre_build.sh
- xcodebuild -workspace <WORKSPACE_NAME>.xcworkspace -scheme <SCHEME_NAME> clean build CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGN_ENTITLEMENTS="" CODE_SIGNING_ALLOWED=NO -destination generic/platform=iOS
except:
- tags
test_all:
stage: test
script:
- rm -rf Pods
- rm -rf Podfile.lock
- pod install
- xcodebuild -workspace <WORKSPACE_NAME>.xcworkspace -scheme <SCHEME_NAME> clean test -destination 'platform=iOS Simulator,name=iPhone 11,OS=14.0' -enableCodeCoverage YES
except:
- tags
- master
test_sonar:
stage: sonar
script:
- rm -rf Pods
- rm -rf Podfile.lock
- pod install
- xcodebuild -workspace <WORKSPACE_NAME>.xcworkspace -scheme <SCHEME_NAME> clean test -destination 'platform=iOS Simulator,name=iPhone 11,OS=14.0' -enableCodeCoverage YES -derivedDataPath Build/
- bash xcode-coverage-mapper.sh Build/Logs/Test/*.xcresult/ > sonarqube-generic-coverage.xml
- bash $HOME/Documents/Sonar/sonar*/bin/sonar-scanner
- bash sonar_validation.sh
only:
- develop

Thats It!! Now REST ASSURED

Whenever Code is getting pushed and getting merged till develop, CI is making sure that

  • There are no Lint Issues
  • Build is passing
  • Tests are passing
  • SONAR is validated

--

--

Rakesh Chander

I believe in modular development practices for better reusability, coding practices, robustness & scaling — inline with automation.