Creating, building and deploying an ionic application using Azure DevOps and App Center
Tags: ionic, azuredevops, visualstudioappcenter, cicd
Published: 2020-06-05 01:34:58

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][2], 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][1]
- 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][3] 

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](#configure_android_sdk_tools_on_path).

- Gradle, which can be installed following these [steps][5].

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][6]. On the other hand you need configure in PATH, see the [example][30].

- An ***text editor*** i used ***Visual Studio Code*** , which can be downloaded [here][4]

> 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][9]
- An Azure DevOps account and an ***organization*** created, more information can be found [here][7]
> 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][8]

## 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_][10] and [_cordova_][11], after that you can navigate to the app folder using `cd SampleApp`, the structure should look like this:

![Sample App Struct][sampleapp_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][9], 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][create_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
```

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][12]. To learn more access the [official microsoft documentation][13] to see all possible capabilities.

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

- Select ***New project***

![Azure DevOps Create Project][azuredevops_new_project]

Just fill in the ***Project name field*** and click on ***New project*** , you can check the project created to this article [here][my-project-ionic-app]. 

### 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](https://i.ibb.co/qJ0HkpJ/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:

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

- Click on ***Save and Run*** to test the pipeline

You can now rename this pipeline to ***App Build CI*** by clicking ***Edit*** &rarr; ***Three dots*** &rarr; ***Triggers*** &rarr; ***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][15], 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:

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

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
```

Then put in ***Library*** &rarr; ***Secure File*** which can be found in the ***Pipelines*** menu. 

![pipelines-menu-library][17]

![library-secure-files-option][18]

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.][16].

#### 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*** &rarr;  ***Add new app***.

![app_center_add_new_app](https://i.ibb.co/27d8VDC/app-center-add-new-app.png)

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

![app-center-new-app-fields](https://i.ibb.co/98rVp1F/app-center-new-app-fields.png)

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

![app-center-account-settings](https://i.ibb.co/Mgtj3LY/app-center-account-settings.png)

- 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***  &rarr; ***Releases***

- Select ***New***
- Select ***New release pipeline***

![release-pipeline-new-release-pipeline][20]

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][28]

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][19]

- 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][21]

![release_pipeline_new_task_visual_studio_fields][37]

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][22]

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*** &rarr; ***Visual Studio App Center*** &rarr; ***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][23]

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` &rarr; `carlosggflor-gmail.com/SampleApp`)

- ***Binary file path***

![task-visual-studio-app-center-field-binary-file-path][24]

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][26]

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][27]

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*** &rarr; ***Distribute*** &rarr; ***Groups***

![app-center-distribute-groups][38]

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

![app-center-distribute-groups-settings][40]

![app-center-distribute-groups-settings-id][39]

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*** &rarr; ***Distribute*** &rarr; ***Stores*** &rarr; ***Connect to store*** and follow the steps.

Finally you can rename your pipeline and run clicking on ***Create release*** &rarr; ***Create***.

![release-pipeline-create-new-release][41]

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

![app_center_distribute_releases][29]

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][42]

## 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

### <a name="configure_android_sdk_tools_on_path"></a> 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***
```bash
# 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
```

***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][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][35]

##### 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][36]

## References

[Deploy Azure DevOps Builds with App Center][31]

[Ionic Framework Getting Started][32]

[Azure DevOps Official Documentation][33]

[YAML Schema Reference][34]


[1]: https://nodejs.org/en/
[2]: https://hub.docker.com/r/beevelop/ionic/dockerfile
[3]: https://developer.android.com/studio?hl=pt-br#downloads
[4]: https://code.visualstudio.com/
[5]: https://gradle.org/install/
[6]: https://hub.docker.com/_/gradle
[7]: https://azure.microsoft.com/pt-br/services/devops/
[8]: https://visualstudio.microsoft.com/app-center/
[9]: https://github.com/
[10]: https://angular.io/
[11]: https://cordova.apache.org/
[12]: https://docs.microsoft.com/en-us/azure/devops/pipelines/library/service-endpoints?view=azure-devops&tabs=yaml#common-service-connection-types
[13]: https://docs.microsoft.com/pt-br/azure/devops/user-guide/what-is-azure-devops?view=azure-devops
[14]: https://azure.microsoft.com/pt-br/services/devops/
[15]: https://dev.azure.com/gabrielggff25/Ionic%20App/_build/results?buildId=569&view=artifacts&type=publishedArtifacts
[16]: https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=azure-devops&tabs=schema%2Cparameter-schema#pr-trigger
[17]: https://i.ibb.co/0jrYZPT/pipelines-menu-library.png
[18]: https://i.ibb.co/qNwk52Z/library-secure-files-option.png
[19]: https://i.ibb.co/kJ6DM1b/release-pipeline-tasks.png
[20]: https://i.ibb.co/ZYBLHcc/release-pipeline-new-release-pipeline.png
[21]: https://i.ibb.co/Jxy3cPC/release-pipeline-new-task-visual-studio.png
[22]: https://i.ibb.co/7b1CCs8/task-visual-studio-app-center.png
[23]: https://i.ibb.co/6nZ4stc/task-visual-studio-app-center-field-app-slug.png
[24]: https://i.ibb.co/0qSWDyy/task-visual-studio-app-center-field-binary-file-path.png
[25]: https://i.ibb.co/qy63ybh/task-visual-studio-app-center-field-build-version.png
[26]: https://i.ibb.co/3mWFsyQ/task-visual-studio-app-center-field-create-release-notes.png
[27]: https://i.ibb.co/gFy2Zh4/task-visual-studio-app-center-field-release-destination.png
[28]: https://i.ibb.co/9W8JBGM/release-pipeline-add-an-artifact.png
[29]: https://i.ibb.co/Yt4s6x4/app-center-distribute-releases.png
[30]: https://docs.gradle.org/current/userguide/installation.html#step_3_configure_your_system_environment
[31]: https://docs.microsoft.com/en-us/appcenter/distribution/vsts-deploy
[32]: https://ionicframework.com/getting-started
[33]: https://docs.microsoft.com/pt-br/azure/devops/?view=azure-devops&viewFallbackFrom=vsts
[34]: https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=azure-devops&tabs=schema%2Cparameter-schema
[35]: https://www.softprayog.in/troubleshooting/error-while-loading-shared-libraries-libz-so-1
[36]: https://github.com/actions/virtual-environments/blob/master/images/linux/Ubuntu1804-README.md
[37]: https://i.ibb.co/pzPwY2H/release-pipeline-new-task-visual-studio-fields.png
[38]: https://i.ibb.co/z8FHP9r/app-center-distribute-groups.png
[39]: https://i.ibb.co/sKCJJbC/app-center-distribute-groups-settings-id.png
[40]: https://i.ibb.co/Fs7TzVP/app-center-distribute-groups-settings.png
[41]: https://i.ibb.co/RgjsM6x/release-pipeline-create-new-release.png
[42]: https://i.ibb.co/LRtDbr1/release-pipeline-enable-continuous-deployment.png

[sampleapp_struct]: https://i.ibb.co/QpS8QgN/build-and-deploy-ionic-app-using-azure-pipelines-ls-sample-app.png
[create_github_repo]: https://i.ibb.co/vm3Js72/build-and-deploy-ionic-app-using-azure-pipelines-create-repo-github.png
[azuredevops_new_project]: https://i.ibb.co/xCGXZnB/build-and-deploy-ionic-app-using-azure-pipelines-azuredevops-newproject.png
[my-project-ionic-app]: https://dev.azure.com/gabrielggff25/Ionic%20App
[visual-studio-app-center-distribute-release]:https://appcenter.ms/users/carlosggflor-gmail.com/apps/SampleApp/distribute/releases
[build-pipelines-permissions-queue-builds]:https://i.ibb.co/zGbw76G/build-pipelines-permissions-queue-builds.png
[article-image]: https://i.ibb.co/bvZysrY/Article-image.png