DEV Community

Cover image for Creating, building and deploying an ionic application using Azure DevOps and App Center
Carlos Gabriel Goiani Flor
Carlos Gabriel Goiani Flor

Posted on

Creating, building and deploying an ionic application using Azure DevOps and App Center

Currently there is a high market demand for the creation of mobile solutions to meet customer needs, and for that frameworks and tools like Ionic, React Native and Flutter provide good solutions and facilities to build and deploy our Apps adding store facilities like Play Store and App Store (not so easy) which offers us good platforms and APIs to make the job of deploying, testing and monitoring easier. However if you are working or managing large teams and your App does a lot of builds and deploys, using only these approaches may not be efficient and quite problematic. With this in mind my goal in this article is to demonstrate the complete creation of CI/CD pipelines to test, build and deploy an Ionic hybrid application using Azure Pipelines, Github and Visual Studio App Center that can distribute the apk to Play Store.

Preparation and required setup

For the steps that will be shown here, you need the following prerequisite:

Note: I'm using a docker image created by beevelop and you can be found here, this will simplify our setup because the image already contains the essencial tools like nodejs, android build tools, cordova, gradle etc..., but isn't necessary in this case if you don't want to.

Tools

  • The latest version of node
  • Ionic CLI, you can install running # npm install -g @ionic/cli
  • Cordova to build the application for the android platform (generating the apk), you can install with this command # npm install -g cordova
  • Android Studio, which can be downloaded here

If you don't want to install the full android studio, you can just download the Command line tools which can be found on the same Android Studio Download page, and configure in the PATH, see the example.

  • Gradle, which can be installed following these steps.

This tool need jdk or jre installed as well, all the necessary information is present in the official documentation, i really recommend that you use a docker image that can be found here. On the other hand you need configure in PATH, see the example.

  • An text editor i used Visual Studio Code , which can be downloaded here

Note: These tools like Cordova, Android Studio, Android Sdk Tools and gradle are optional to making local tests, basically all of these tools are available on Hosted Microsoft agents that we are going to use.

Accounts

  • Github account to maintain our repo, you can create here
  • An Azure DevOps account and an organization created, more information can be found here > Note: it is also possible use github account to make login on Azure DevOps.
  • Visual Studio App Center account to deploy the App and enable distribution to Play Store, you can create here

Creating an ionic project

First let's create the App using ionic cli to start a simple app with Tabs, so execute ionic start SampleApp tabs --type=ionic-angular --cordova, this makes it explicit that we want to use Angular and cordova, after that you can navigate to the app folder using cd SampleApp, the structure should look like this:

Sample App Struct

At this moment the App was successfully created, now the next step is to test the App locally in the browser to see what the initial structure looks like to do this run ionic serve this command automatically open the App (simulated) in the browser, it is also possible to choose the port, browser and other options see more running ionic serve --help. After these steps you can change the App as you wish to differentiate from the template created automatically by ionic.

Note: It is also possible to use a docker container to create the initial structure of the App (e.g. docker run -it --rm -v $($PWD):/usr/app beevelop/ionic:latest ionic start SampleApp tabs --type=ionic-angular --cordova) note that in this case the variable $PWD is used, this variable contains the name of current working directory.

Finally you can modify the App as you wish, to do this use Visual Studio Code or any text editor of your choice.

Creating a repository on github and pushing the App code

To create a repo to the App on Github, first log into your account and select New repository , fill in the fields then choose the one of these two options:

  • Public (Anyone can see this repository. You choose who can commit)
  • Private (You choose who can see and commit to this repository)

Keep the Initialize this repository with a README option unchecked because we're importing an existing repository, if you filled everything in correctly, the fields should look like these:

Create New Github Repo

Go back to the terminal and navigate to the App code and run these commands:



git remote add origin https://github.com/<github_user>/<project_name>.git
git push -u origin master


Enter fullscreen mode Exit fullscreen mode

Now you have a repo created and the code inside it, in the next steps we will talk a little bit about Azure DevOps and how to create and configure pipelines.

Creating a project in Azure DevOps

Azure DevOps is a powerful Service (or on-premise) that offers support to teams plan work, collaborate with code development, build, deploy, tests and create and manage many different kinds of integrations to Azure or third-party services by default, such as Jenkins, Chef, Nuget and others. To learn more access the official microsoft documentation to see all possible capabilities.

To create a project on Azure DevOps, make log in with your microsoft account (or github account) and follow these steps:

  • Select New project

Azure DevOps Create Project

Just fill in the Project name field and click on New project , you can check the project created to this article here.

Creating build and deploy pipelines

To manage pipelines Azure DevOps provides the Azure Pipelines that can be used to automatically build, tests and deploys supporting any language or project types, in our case we will use to create three pipelines to build, test and deploy our App.

Note: Don't necessarily have to be three pipelines, this separation is only logical

Azure DevOps has a division between Build and Release Pipeline, the first one is generally used to generate one or more artifacts using the source code of the project and the second one is more common used to deploy this artifacts generated. The two next topics explain how to create two Build Pipelines and one Release Pipeline to deploy the App.

Build pipelines

Then to create the first pipeline that is responsible for building (Angular part) and testing the App, follow these steps:

  • Click on Pipelines

build-pipelines-menu.png

  • Select New pipeline or Create Pipeline
  • Select Github as the source code option

You can choose the User the classic editor option if you want a GUID to help while creating the pipeline

  • Select the repository created for your App

At this point Azure DevOps will redirect you to the github page for you accept and install Azure Pipelines app.

  • Select approve and install

If everything is ok, some templates will appear to choose one, let's start from scratch.

  • Select Starter Pipeline.

Finally you can see the minimum pipeline structure, delete all lines and put the following:



# Disables CI builds entirely, then commits don't trigger a build
trigger: none

# Activates pull request trigger, so any pull request to the master trigger a build
pr:
- master

# Specifies which pool (Hosted or Self-hosted) to use for this pipeline. 
# In this case it is in the scope of the pipeline, but you can use it at the stage or job level
pool:
  vmImage: 'ubuntu-latest'

# Specific variables to use, in our case it is just one and at the pipeline level 
variables:
  chromeDriverVersion: '80.0.3987.106'

# Specifies a linear sequence of operations that make up a job
# We need only one job, so we can use the simplified structure
steps:
# Tasks are the building blocks of a pipeline
# We can choose from a catalog of tasks available by default or download from the Azure DevOps marketplace 
# In this case I chose to use 6 tasks (not mandatory) to install dependencies and build the App. 
  - task: Npm@1 # Run npm install
    displayName: 'npm install'
    inputs:
      command: install
      workingDir: '$(System.DefaultWorkingDirectory)'

  - task: Npm@1 # Run npm run build
    displayName: 'npm run build'
    inputs:
      command: custom
      customCommand: run build

  - script: | # Run unit tests
      npx --no-install ng test --watch=false --reporters=progress,junit
    displayName: 'ng test'

# This task download the chromedriver binary, required for e2e tests
# In this case I installed a specific version of chromedriver to avoid errors with the version of Chrome in the hosted agent microsoft, see the troubleshooting topic.
  - script: |
      node node_modules/.bin/webdriver-manager update --versions.chrome=$(chromeDriverVersion) --gecko false --standalone false
    displayName: 'changing version of chromedriver to $(chromeDriverVersion)'


  - script: | # Run e2e tests without update webdriver
      npx --no-install ng e2e --webdriverUpdate=false
    displayName: 'ng e2e tests'

  - task: PublishTestResults@2 # Publish tests results
    displayName: 'Publishing test results'
    condition: succeededOrFailed()
    inputs:
      testResultsFormat: 'JUnit'
      testResultsFiles: '**/TESTS-*.xml'

  - publish: 'www/' # Publish App artifact
    artifact: 'www'
    displayName: 'Publishing Artifact'


Enter fullscreen mode Exit fullscreen mode
  • Click on Save and Run to test the pipeline

You can now rename this pipeline to App Build CI by clicking Edit → Three dots → Triggers → YAML and editing the Name field.

This build specification is responsible to install, build and test the App, this pipeline generates the www artifact whenever it is successfully executed, you can see a example of the artifact generated here, this artifact includes assets, styles and scripts that have been compiled and will be used to build the .apk file in the next step.

Finally, to generate the .apk file, it is necessary to create another pipeline with the name Android App CI, and put the following content:



# Enable CI builds, trigger a build whenever the master branch receive new modifications 
trigger: ['master']
pr: none # Disable PR builds entirely

pool:
  vmImage: 'ubuntu-latest'

steps:
# This task trigger a new build to run, this build is the same as the step above that generates the bundle
- task: TriggerBuild@3
  displayName: 'trigger App Build CI'
  inputs:
    definitionIsInCurrentTeamProject: true
    buildDefinition: 'App Build CI'
    queueBuildForUserThatTriggeredBuild: true
    ignoreSslCertificateErrors: true
    useSameSourceVersion: false
    useCustomSourceVersion: false
    useSameBranch: true
    waitForQueuedBuildsToFinish: true
    waitForQueuedBuildsToFinishRefreshTime: '10'
    failTaskIfBuildsNotSuccessful: true
    cancelBuildsIfAnyFails: false
    treatPartiallySucceededBuildAsSuccessful: false
    downloadBuildArtifacts: false
    storeInEnvironmentVariable: true
    authenticationMethod: 'OAuth Token'
    password: '$(System.AccessToken)' # Necessary allow permission of Queue builds to <Project> Build Service, See the troubleshooting topic 
    enableBuildInQueueCondition: false
    dependentOnSuccessfulBuildCondition: false
    dependentOnFailedBuildCondition: false
    checkbuildsoncurrentbranch: false
    failTaskIfConditionsAreNotFulfilled: false

# Download the artifact of the build triggered above
- task: DownloadPipelineArtifact@2
  displayName: 'download artifact App Build CI'
  inputs:
    buildType: 'specific'
    project: 'Ionic App'
    definition: 'App Build CI'
    specificBuildWithTriggering: true
    buildVersionToDownload: 'specific'
    runId: '$(TriggeredBuildIds)'
    targetPath: '$(Build.BinariesDirectory)'

# Moves the www folder containing bundle to the Default Working Directory path (e.g. /agent/_work/1/s)
- bash: |
      mv $(Build.BinariesDirectory)/www $(System.DefaultWorkingDirectory)
  displayName: 'mv $(Build.BinariesDirectory)/www $(System.DefaultWorkingDirectory)'

- bash: | # Installing cordova
    sudo npm i -g cordova
  displayName: 'npm i -g cordova@latest'

- bash: | # Build android App using cordova
    npx ionic cordova build android --no-build --release
  displayName: 'ionic cordova build android --no-build --release'

# Needed to solve missing dependency problem, see the troubleshooting topic
- bash: |
    sudo apt-get install lib32z1
  displayName: 'sudo apt-get install lib32z1'

# Signin the .apk file with the specified keystore
- task: AndroidSigning@3
  displayName: 'android signing'
  inputs:
    apkFiles: '**/outputs/apk/release/app*.apk'
    apksignerKeystoreFile: 'sampleapp.keystore'
# This password is placed in Variables inside the pipeline and the option to keep the value secret is checked.
    apksignerKeystorePassword: '$(password_keystore)'
    apksignerKeystoreAlias: 'sampleapp'

- task: CopyFiles@2 # Copies all .apk files to publish
  displayName: 'copy **/outputs/apk/release/app*.apk to $(Build.BinariesDirectory)'
  inputs:
    SourceFolder: '$(System.DefaultWorkingDirectory)'
    Contents: '**/outputs/apk/release/app*.apk'
    CleanTargetFolder: true
    TargetFolder: '$(Build.BinariesDirectory)'
    flattenFolders: true


- publish: '$(Build.BinariesDirectory)' # Publish artifact
  artifact: 'android-app'
  displayName: 'publish artifact'


Enter fullscreen mode Exit fullscreen mode

Before running this pipeline it is necessary to generate the keystore file on your local machine using the following command that comes with the JDK:



$ keytool -genkey -v -keystore sampleapp.keystore -alias sampleapp -keyalg RSA -keysize 2048 -validity 10000


Enter fullscreen mode Exit fullscreen mode

Then put in Library → Secure File which can be found in the Pipelines menu.

pipelines-menu-library

library-secure-files-option

Now you can run and see the result, whenever you run the Android App CI pipeline, another App Build CI will triggered and the generated artifact will be downloaded to generate the .apk file. You can find more information about yaml structure in the official Microsoft YAML reference..

Release pipeline

The last pipeline is responsible for deploying the App to the Visual Studio App Center, So to do that you need to first create an app in the App Center and then a new release pipeline in Azure DevOps.

Creating an App in Visual Studio App Center

Access your account at the App Center and click on Add new → Add new app.

app_center_add_new_app

  • Make sure you are checking the following options:
    • Release Type Alpha
    • OS Android
    • Platform Java/Kotlin

app-center-new-app-fields

  • After that you need your App Center API key. To get it navigate to your Account Settings

app-center-account-settings

  • Edit User API Tokens
  • Select New API Token
  • Make sure you are selecting the Full Access option
  • Copy and save the generated API token

You'll need to use this API token to configure the automatic deployment of the App.

Creating a Release pipeline

Log in to Azure DevOps and navigate to Pipelines → Releases

  • Select New
  • Select New release pipeline

release-pipeline-new-release-pipeline

The pipeline configuration should appear to add stages and artifacts.

First you need to add an artifact to the pipeline, click Add an artifact and select Android App CI as Source (build pipeline)

release_pipeline_add_an_artifact

Add a new stage by clicking on Add a stage box and select Empty Job because it is not necessary to use any templates, next change the stage name to Deploy Alpha for example, now it is necessary to add the deploy task for this click on Tasks

release-pipeline-tasks

  • Click the plus icon to add a new task
  • Search for Visual Studio App Center in the task catalog
  • Add the App Center distribute task to the job

release-pipeline-new-task-visual-studio

release_pipeline_new_task_visual_studio_fields

After that for everything to work well you must fill in the fields of the task, below i'll explain how to fill each of them.

  • App Center service connection

task-visual-studio-app-center

Select the service connection to Visual Studio App Center, in this case you need to create one first by following these steps: Enter Manage, select New service connection → Visual Studio App Center → Next and fill in the API Token with the token generated in Visual Studio App Center, Lastly give a name to connection and go back to the task and select the created connection.

  • App slug

task-visual-studio-app-center-field-app-slug

To find the slug app go to Visual Studio App Center and click on the App and extract from the URL (e.g. https://appcenter.ms/users/carlosggflor-gmail.com/apps/SampleApp → carlosggflor-gmail.com/SampleApp)

  • Binary file path

task-visual-studio-app-center-field-binary-file-path

Relative path from the repo root to the APK/AAB or IPA file you want to publish, in this case you need to select the artifact (.apk file) path generated by running Android App CI build (e.g. $(System.DefaultWorkingDirectory)/_Android App CI/android-app/app-release-unsigned.apk).

  • Release notes

task-visual-studio-app-center-field-create-release-notes

Release notes will be attached to the release and shown to testers on the installation page.

  • Release destination

task-visual-studio-app-center-field-release-destination

This field is very important because it is where you decide the destination that the release will be distributed, in the case if Groups is selected you can deploy it to one or more groups using commas or semicolons to separate multiple IDs, you can create these groups in the Visual Studio App Center navigating to your App → Distribute → Groups

app-center-distribute-groups

To get the ID, just select the group created and click on the settings icon

app-center-distribute-groups-settings

app-center-distribute-groups-settings-id

In case of you leave the Destination ID field empty, then the default group (Collaborators) is chosen.

Otherwise, if you select Store you need to enter the ID of distribution store to deploy, in our case we didn't create the store connection (e.g. Google play or Intune Company Portal), but if you want to create enter Visual Studio App Center navigating to your App → Distribute → Stores → Connect to store and follow the steps.

Finally you can rename your pipeline and run clicking on Create release → Create.

release-pipeline-create-new-release

After the deployment as finished you can see the release in the Visual Studio App Center → Distribute → Releases, the image below mark the main information about this page.

app_center_distribute_releases

1 - The App version, you can change that version by editing the config.xml file in the source code.

2 - The destination chosen in the deployment task for the App Center.

From here you can distribute the same release to other groups and stores (Google Play or Intune), set up testers and generate links to download the App.

Lastly, you need to configure Continuous deployment in the pipeline for create a release whenever build Android App CI is finished, to do this click on lightning icon and enable the Continuous deployment trigger option.

release-pipeline-enable-continuous-deployment

Conclusion

We created an hybrid application using ionic with cordova and Azure DevOps to integrated with Github to keep the code and created CI/CD pipelines to test, build and deploy our App in the Visual Studio App Center. We can change any part related to Azure DevOps like the CI/CD tool and still use it as an intermediary. We went through a detailed explanation of each step of the build and the deployment stage.

Source code and pipelines

SampleApp on Github. https://github.com/carlosgit2016/SampleApp

Ionic App on Azure DevOps. https://dev.azure.com/gabrielggff25/Ionic%20App

Extras

Configuring Android SDK tools in PATH

I didn't find a simple example of this, so if you don't want to install Android Studio or are using Linux and need to configure the tools in PATH, see below

Linux



# i downloaded the command line tools in /tmp/ and unzipped it in /opt/android/tools
# use any editor to edit the /etc/profile
sudo vi /etc/profile
cat /etc/profile 
# put the PATH variable at the end of the file
# should look like this

# /etc/profile: system-wide .profile file for the Bourne shell (sh(1))
# and Bourne compatible shells (bash(1), ksh(1), ash(1), ...).

if [ "${PS1-}" ]; then
  if [ "${BASH-}" ] && [ "$BASH" != "/bin/sh" ]; then
    # The file bash.bashrc already sets the default PS1.
    # PS1='\h:\w\$ '
    if [ -f /etc/bash.bashrc ]; then
      . /etc/bash.bashrc
    fi
  else
    if [ "`id -u`" -eq 0 ]; then
      PS1='# '
    else
      PS1='$ '
    fi
  fi
fi

if [ -d /etc/profile.d ]; then
  for i in /etc/profile.d/*.sh; do
    if [ -r $i ]; then
      . $i
    fi
  done
  unset i
fi

PATH=$PATH:/opt/android/tools/bin


Enter fullscreen mode Exit fullscreen mode

Windows

  • Open control panel
  • Look for Edit the system environment variables
  • Select Environment Variables
  • Two click on Path in System Variables
  • Click New and put the path where you unzip the file (e.g. C:\Temp\tools\bin)

Troubleshooting

Without permission to trigger another build in the Android App CI pipeline.
  • Error:
    Error message: Error: TF215106: Access denied. Ionic App Build Service (gabrielggff25) needs Queue builds permissions for build pipeline 20:App Build CI in team project Ionic App to perform the action. For more information, contact the Azure DevOps administrator.

  • Solution:
    Allow Queue builds permission to user Project Build Service, see the image below.

build-pipelines-permissions-queue-builds

Problem with dependency missing during the execution of pipeline Android App CI build

The problem occurs during the build of the App for android, see Error while loading shared libraries

Problem with chromedriver version on agent

Maybe the current version of chromedriver is new to Google Chrome installed on the agent, see installed softwares on ubuntu machines

References

Deploy Azure DevOps Builds with App Center

Ionic Framework Getting Started

Azure DevOps Official Documentation

YAML Schema Reference

Top comments (7)

Collapse
 
rodneyjoyce profile image
Rodney

Ok, another trap for young players - if you ONLY have 1 DevOps Build Pool/agent then your Trigger Build Task will queue until it hits 1 hour (for Azure Hosted one) and time out.

I created a 2nd build machine so that 2 tasks could run in parallel.

Collapse
 
carlosgit2016 profile image
Carlos Gabriel Goiani Flor

Thanks Rodney.

That is a good point in case of using Self Hosted agents instead of Microsoft hosted agents

Collapse
 
rodneyjoyce profile image
Rodney

Hi Carlos, this is a brilliant, useful article - FYI you have to install the TriggerBuild task from DevOps marketplace - marketplace.visualstudio.com/items...

Collapse
 
carlosgit2016 profile image
Carlos Gabriel Goiani Flor

Nice catch. I forgot to mention that

Collapse
 
stefanhk31 profile image
Stefan

Great article, thank you! How does this process look when building iOS? Are there any particular resources to check out and/or pitfalls to watch out for?

Collapse
 
carlosgit2016 profile image
Carlos Gabriel Goiani Flor

Hey Stefan. Thank you.
For iOS some things will need to change. But in fact you just need to touch in the second pipeline "Android App CI", the signing method need to be changed and the command used for build to android too.

For publishing you can't use the Visual Studio App Center I think. You'll need configure a agent with the XCode.

Collapse
 
edwardchanjw profile image
Edward Chan Jia Wei

Hi Caros, what you mean signing method need to be changed?

  1. You mean just the variable?
  2. Or you mean change to play app signing (I not really familiar with Android ecosystem's history, I think roughly is they added one more secure method, play app signing)

Furthermore, build for android also, does the workflow or tooling chance? Or you just tell Stefan to look up all newbie issue?