As a lot of our articles can attest, we at Hybrid Heroes are big fans of Continuous Integration. Not only does it help to catch bugs and exceptions early on, it also accelerates our development and delivery workflows and help our team be more efficient and productive.

In this article we will demonstrate how to implement CI with Gitlab on a cross-platform Ionic project.

#Runner and environment variables

Using Gitlab CI for continuous integration requires setting up an environment for the build process to be run on. This environment can be encapsulated in a docker image, or simply be the shell of a targeted machine. We will be installing our runner on a macOS machine so we can make iOS builds.

We first need to install Gitlab Runner on our environment of choice, and register it with the unique token provided by Gitlab.

The next logical step is to provide environment variables to feed the runner jobs. A typical set up in a Cordova project would be to specify the build configuration in a build.json file. A good practice here is to put this configuration in a BUILD_CONFIG environment variable. For example, our variable might look like this:

{ "ios" : { "debug" : { "codeSignIdentity" : "iPhone Developer" , "developmentTeam" : "XXXXXXXXXX" , "packageType" : "development" , "automaticProvisioning" : true , "buildFlag" : [ "-UseModernBuildSystem=0" , "-allowProvisioningUpdates" ] }, "release" : { "codeSignIdentity" : "iPhone Developer" , "developmentTeam" : "XXXXXXXXXX" , "packageType" : "app-store" , "automaticProvisioning" : true , "buildFlag" : [ "-UseModernBuildSystem=0" , "-allowProvisioningUpdates" ] } }, "android" : { "debug" : { "keystore" : "./development.jks" , "storePassword" : "12345abcde" , "alias" : "my-app" , "password" : "12345abcde" , "keystoreType" : "" }, "release" : { "keystore" : "./development.jks" , "storePassword" : "12345abcde" , "alias" : "my-app" , "password" : "12345abcde" , "keystoreType" : "" } } }

Jobs within a CI configuration can belong to three different default stages: build, test and deploy. In order to keep this article focused, we will only pay attention to the deploy stage, and work our script around that. The CI script uses the YAML markup language, make sure you get familiar with the syntax before diving head on in the code.

We will create a deploy template that will hold the common configuration for all types of builds. Note how we pass our build configuration from the environment variable $BUILD_CONFIG to the project folder.

.deploy: stage: deploy cache: key: "deploy-$CI_COMMIT_SHORT_SHA" paths: - platforms/ - plugins/ before_script: - rm -fr ~/Library/MobileDevice/Provisioning\ Profiles/ - echo "$BUILD_CONFIG" | tr -d '\r' > build.json after_script: - rm build.json - cp $OUTPUT_PATH.$FILE_TYPE $CI_PROJECT_NAME-$CI_COMMIT_REF_SLUG.$FILE_TYPE artifacts: name: paths: - $CI_PROJECT_NAME-$CI_COMMIT_REF_SLUG.$FILE_TYPE expire_in: 7 days tags: - macOS when: manual

Using this template as the basis for all types of build, it becomes easy to define our different build jobs. Make sure you create build scripts for each cases.

Android

deploy: android:debug: <<: environment: name: production variables: PLATFORM: android FILE_TYPE: apk OUTPUT_PATH: platforms/android/app/build/outputs/apk/debug/app-debug script: - npm ci - ionic cordova build android --debug deploy: android:release: <<: environment: name: production variables: PLATFORM: android FILE_TYPE: apk OUTPUT_PATH: platforms/android/app/build/outputs/apk/release/app-release script: - npm ci - ionic cordova build android --release

iOS

deploy: ios:debug: <<: environment: name: production variables: PLATFORM: iOS FILE_TYPE: ipa OUTPUT_PATH: platforms/ios/build/device/my-app script: - npm ci - ionic cordova build ios --debug deploy: ios:release: <<: environment: name: production variables: PLATFORM: iOS FILE_TYPE: ipa OUTPUT_PATH: platforms/ios/build/device/my-app script: - npm ci - ionic cordova build ios --release

From then on, it's easy to generate the types of build we need at any given time. Having a generic template gives us the flexibility to add/remove new features for to build system. Want to hook up your Slack bot? Piece of cake!

- 'curl -X POST -H ' 'Content-type: application/json' ' --data ' ' ' '' '{"text":"🚀 ' '${CI_PROJECT_NAME}' ' ' '${PLATFORM}' ' *' '${CI_COMMIT_REF_NAME}' '* is now available for download: <https://gitlab.com/<my-workspace>/<my-app>/-/jobs/' '${CI_JOB_ID}' '/artifacts/download>" }' '' ' ' ' ${SLACK_HOOK}'

Simply trigger the build process from the Gitlab interface to have your files delivered directly to the Gitlab server.

#Wrapping up

Manually building your branches on devices and simulators with Ionic can sometimes take a tremendous amount of time, especially when different developers work on the same project, or when a non-technical QA has to go through the process every time.

Automating builds is the best and sufire way to provide constant delivery of your software builds and will help you optimize turnaround times and focus more on what matters in your own Ionic development lifecycle. Happy continuous integration!