> ## Content Index
> Fetch the complete content index at: https://hybridheroes.de/blog/llms.txt
> Use this file to discover other available public pages before exploring further.

# Automate Ionic Builds With Gitlab CI: Walkthrough and Tutorial
- URL: https://hybridheroes.de/blog/2019-04-08-ionic-gitlab-ci/
- Published: 2019-04-08T08:00:00.000Z
- Updated: 2022-04-11T20:47:51.000Z
- Author: Hybrid Heroes
- Tags: Entwicklung, Ionic, Continuous Integration, #Import 2026-07-29 15:59

As [a lot](https://hybridheroes.de/blog/2018-09-24-react-native-gitlab-ci/) of our [articles](https://hybridheroes.de/blog/2019-02-04-continuous-integration/) 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](https://docs.gitlab.com/runner/install/) 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](https://cordova.apache.org/docs/en/latest/guide/platforms/ios/#using-buildjson) 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:

```json
{
  "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": ""
    }
  }
}

```

![environment-variables](https://hybridheroes.de/blog/content/images/2022/03/environment-variables.jpg)

## Configure *.gitlab-ci.yml*

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.

```yaml
.deploy: &deploy
  stage: deploy
  cache:
    # speed up packaging by sharing cache amongst multiple deploy jobs of the same commit
    key: "deploy-$CI_COMMIT_SHORT_SHA"
    paths:
      - platforms/
      - plugins/
  before_script:
    # workaround Xcode not downloading updated profiles
    - 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**

```yaml
deploy:android:debug:
  <<: *deploy
  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:
  <<: *deploy
  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**

```yaml
deploy:ios:debug:
  <<: *deploy
  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:
  <<: *deploy
  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!

```yaml
- '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!