Skip to content

How to Deliver Apps to Apple Store and Google Play Using Azure DevOps Pipelines?

Technology

Feb 23, 2021 - 12 minutes read

How To Deliver Apps Google Store Apple Store416x300
Daniel Stach DevOps Engineer

A DevOps enthusiast with a passion for automation and reliable solutions. In his free time, Daniel enjoys traveling and playing football with his friends.

See all Daniel's posts
Retail Report Resources

Share

At a point in the past, I was faced with a problem of smoothly and rapidly delivering iOS & Android packages from the Git repository to the Apple Store and Google Play. I knew that this delivery process will be frequently used during the development of the project. That’s why I needed a fully automated solution that could give us more time to focus on development, rather than operations in the long term. Mobile application stores are very popular, so I figured that they already had continuous delivery solutions that can further shorten the time required for implementation. It’s always nice to see an applicable, viable solution that’s already available on the market. I decided to gather all the requirements and create this list:

Requirements for the applied solution

  • Simplicity in maintenance —o ne small process with just a couple of steps,
  • No need to reinvent the wheel — plug and play solution would be a perfect fit,
  • Self-described solution — code type solution is highly desired,
  • Fast implementation — no more than a few hours.

With consideration of these points, it only took me a few minutes to find a suitable solution based on Azure DevOps. Now, I’ll describe how I set up the automated package delivery process to the mobile stores in less than 30 minutes, using the Azure DevOps Pipelines.

Fast Package Delivering Using the Azure DevOps Pipelines

The proposed solution is based on the Azure DevOps Pipelines, which are available on the Azure DevOps platform. This tool is extremely useful when it comes to creating automated processes. That’s why I decided to use it, especially as the whole project was already based on Azure.

Our solution included:

  • Azure DevOps Pipelines
  • Multi-stage pipeline approach
  • Pipelines defined using yaml files
  • Apple Store
  • Google Play

Overall, the process will be as follows:

General view of the build and delivery process

Diagram 1. General view of the build and delivery process.

Delivering to the Apple Store

In this section we’ll cover:

  • Installing the provisioning profile and certificate from the App Store Connect,
  • Building the iOS package (.ipa),
  • Signing the package,
  • Uploading the package to the Apple Store,
  • Implementation of all above steps using the Azure DevOps Pipeline.

Before we start and create the Azure Pipeline, it’s important to check what the prerequisites are:

  • Creating the application in the App Store Connect,
  • An Apple Developer Account,
  • The iOS Distribution Certificate uploaded into the Apple Developer Account,
  • An Apple Developer Account Provisioning Profile linked with the above-mentioned certificate and the Application Bundle ID in the App Store Connect

The good information is that all the above-mentioned steps are required to publish the application in the Apple Store, even if you want to do it manually. If you looked for automation solutions at the start of your project, they’re probably done already.

The following diagram illustrates this entire process:

Apple Store Prepare-Build-Sign-Release process diagram

Diagram 2. Apple Store Prepare-Build-Sign-Release process

Let’s take a look at the Azure Pipeline configuration. All the steps of the pipeline will be represented in the yaml sections. If you want the whole yaml file with all the necessary steps, just scroll to the bottom of this article and use the provided solution.

Configuration of the Azure Pipeline

name:1.0.$(BuildID)

 

trigger: 

- master 

 

pool: 

  vmImage: 'macOS-10.15' 

 

steps: 

  #Code in the next sections should be copied here 

 

The code posted above should be pasted at the beginning of the Azure Pipeline yaml file. It’s a basic configuration which allows us to:

  • Set the names for all builds of our pipeline,
  • Set an automatic build trigger whenever someone makes changes to the master branch,
  • Set the type of build agent as macOS,
  • Set the steps which will be performed during the run of this pipeline.

Install the provisioning profile and the certificate from the App Store Connect

task: InstallAppleCertificate@2 

  displayName: 'iOS Certificate instalation' 

  inputs: 

    certSecureFile: 'example_certification_file.p12' 

    certPwd: '$(ios_distribution_certification_password)' 

    keychain: 'temp' 

 

- task: InstallAppleProvisioningProfile@1 

  displayName: 'iOS provisioning Profile instalation' 

  inputs: 

    provisioningProfileLocation: 'secureFiles' 

    provProfileSecureFile: 'Example_Distribution.mobileprovision' 

The certificate and the provisioning profile must be downloaded from the App Store Connect and uploaded to the Azure Pipelines Library. Then, we’ll be able to install them on the build agent by using two simple steps from the Azure Pipeline Marketplace. We will use those files in the next sections to build, sign, and upload the ipa package to the Apple Store. I recommend that you keep the mentioned files in the “Secure Files” in the Azure Pipelines, not in the repository or any other easily accessible places.

The variable ios_distribution_certification_password should be known by the creator of the certificate. I don’t recommend hardcoding a password in the yaml section. It’s better to save it in the Azure Key Vault, the Pipeline Library or in the Pipeline Variables to keep it save, like I showed in the example.

The code posted above can be pasted under the steps section in the yaml file.

Build the iOS package

At this point, we’re ready to build our iOS package (ipa). To do so, we will use a simple step to build iOS with minor changes in the default configuration.

task: Xcode@5 

  displayName: 'Build iOS' 

  inputs: 

    actions: 'build' 

    configuration: 'Release' 

    sdk: 'iphoneos' 

    xcWorkspacePath: '**/Example.xcworkspace' 

    scheme: 'ExampleScheme' 

    xcodeVersion: 'specifyPath' 

    xcodeDeveloperDir: '/Applications/Xcode_11.5.app' 

    packageApp: true 

    archivePath: '$(system.defaultworkingdirectory)/archive' 

    signingOption: 'manual' 

    signingIdentity: '$(APPLE_CERTIFICATE_SIGNING_IDENTITY)' 

    provisioningProfileUuid: '$(APPLE_PROV_PROFILE_UUID)'  

    

The highlighted lines are the most important here. The variables are automatically set by the previous section where we installed the Provisioning Profile and the Certificate. That’s why we need to keep them named the same as in the example. Other parameters like the xcode version or the sdk can be changed according to your requirements.

Like in the previous section, you can copy it to the yaml file under the steps section just after the installation of the provisioning profile.

Release the iOS package to the Apple Store

Finally, we’ve reached to the point where we can deliver our package to the Apple Store. Again, to complete this step we will use an Azure DevOps Pipeline step:

task: AppStoreRelease@1 

  displayName: 'Publish to AppStore' 

  inputs: 

    authType: 'UserAndPass' 

    username: 'example_username@example.domain' 

    password: '$(example_username_password)' 

    appIdentifier: '$(bundleid)' 

    appType: 'iOS' 

    ipaPath: '**/*.ipa' 

    releaseTrack: 'TestFlight' 

    shouldSkipWaitingForProcessing: true 

   

At this point, we have to provide the required information:

  • A username which will be used to upload package to Apple Store Connect,
  • A password for the username,
  • The bundle id of the application in Apple Store Connect.

You can use your own username and password to the Apple Store Connect but I highly recommend creating a service account and using it for this purpose. It’s more secure and ensures high reliability for the project.

After all the mentioned steps, our Azure Pipeline is ready to be run and deliver the ipa package to the Apple Store.

What we need to keep in mind:

  • Store certificates, provisioning profiles and passwords in a safe place like the Azure Key Vault or the Azure Pipeline Library,
  • Manually renew expired certificates and provisioning profiles and update them in Azure.

Delivering to Google Play

In this section we will:

  • Build the Android package (.apk)
  • Sign the package
  • Upload the package to Google Play
  • Implement all the above steps using Azure DevOps Pipeline

Before we start and create the Azure Pipeline, let’s check what the prerequisites are:

  • Creating a Service Account and obtaining the json file with its credentials,
  • Creating an Application Project with a bundle id in Google Play,
  • Creating the keystore,
  • Manually uploading the first version of the Android package (.apk) into the application project in Google Play,
  • Importing the upload certificate taken from Google Play into the keystore, after the first manual upload of the android package into Google Play.

Except for the upload certificate and the service account, all other points are required to manually publish an application in Google Play. In comparison, delivering a package to the Apple Store requires performing more actions. It forces us to spend more time to set up an automated package delivery process to the mobile store at the beginning of the project.

Let’s take a look on this diagram to check what our delivery process will look like:

Google Play Build-Sign-Release process diagram

Diagram 3. Google Play Build-Sign-Release process

Configuration of the Azure Pipeline

 

name: 1.0.$(BuildID)  

 

trigger: 

- master 

 

pool: 

  vmImage: 'ubuntu-18.04' 

 

steps: 

  #Code in the next sections should be copied here 

The code posted above should be pasted at the beginning of the Azure Pipeline yaml file. It’s a basic configuration which allows us to:

  • Set the names for all builds of our pipeline,
  • Set an automatic build trigger whenever someone makes changes to the master branch,
  • Set the type of build agent as ubuntu,
  • Set the steps which will be performed during the run of this pipeline.

This section is very similar to the iOS one, there is only one difference—the type of the build agent.

Build the Android package

The first step in our Android Azure Pipeline will be building our Android package (apk). My application is written in ionic, so I use the command line step to perform ionic commands, to build the application into the apk package. If you’re using another tool, like Xamarin or Gradle, to build your Android project, feel free to look for them in the Azure Marketplace and I can assure you that you will find those tools easily.

task: CmdLine@2 

  displayName: 'Build Android' 

  inputs: 

    script: | 

      ionic cordova platform add android 

      ionic cordova build --configuration prod --release android --verbose 

 

My build step is simple because it’s performing only two commands which will give me an apk package. This is the most important part in preparation for the next step. If you use another tool to build the Android package, you just need to ensure that the result of the build step will be the apk package and that you know the location (path) of the package on the build agent, because it will be needed in the next step.

Sign the Android package

The second step is important because that’s when we have to sign the package using the correct keystore. Without it, we won’t be able to upload our package to the store. Additionally, it’s crucial to have the keystore with the imported upload certificate which can be downloaded from Google Play.

Before we add the signing step to our Azure Pipeline, let me show you how to generate the keystore and import the upload certificate into it.

The following command will let you generate the keystore which must be used for the required first manual upload of the package to Google Play.

keytool -v -genkey -v -keystore example.keystore -alias example.alias -keyalg RSA -validity 10000 

To manually upload the apk artifact to Google Play, you have to login to the store, go to manage versions, and upload the artifact for the tracks: alfa, beta and production.

After that, you’ll have the possibility to download the upload certificate for the application.

To import the upload certificate into the newly created keystore, you must download the upload certificate from Google Play and then execute the below command:

keytool -importcert -file .\upload_cert.der -keystore .\example.keystore 

An already prepared keystore file can be used in the Azure Pipeline to sign the apk artifact.

This step can be tricky, but when you already have this kind of keystore file, you can just add this task to your steps section in the Azure Pipeline yaml file:

task: AndroidSigning@3 

  displayName: 'Sign .apk package' 

  inputs: 

    apkFiles: 'platforms/android/**/*.apk' 

    apksignerKeystoreFile: 'keystore_file' 

    apksignerKeystorePassword: '$(keystore_password)' 

    apksignerKeystoreAlias: 'example.alias' 

Upload the Android package

The last step lets us upload our signed apk package to the Google Play store by selecting the package using the apkFile parameter and by indicating the location of the service account credential file using the serviceAccountKey parameter.

task: GooglePlayRelease@3 

  displayName: 'Upload .apk package' 

  inputs: 

    authType: 'JsonFile' 

    serviceAccountKey: 'service_account_credential_file.json' 

    apkFile: 'platforms/android/**/*.apk' 

    track: 'beta' 

   

What surprised me the most is the fact that the credential file of the service account must be kept in the repository along with the application code. At the time of creating this article, the GooglePlayRelease task does not support the Azure Key Vault or Secure Files in the Azure Pipeline Library. It’s an acceptable solution as long as we’re controlling access to our repository, and we won’t let some anonymous user reach the contents of the repository. Nevertheless, I prefer to keep such critical files in more secure places, like the Azure Key Vault.

Now we’re ready to run our Azure Pipeline and deliver the apk package to Google Play.

What we need to keep in mind:

  • Before we start creating the Android automated delivery process, we need to ensure that we have the correct keystore with the upload certificate,
  • We should create a service account and use it in the upload step,
  • There’s no possibility to keep the service account credential file in a safe place like the Azure Key Vault, instead we must keep it in the repository along with our application code.

Advantages and Disadvantages

One of the advantages of the described solution is its simplicity. One pipeline with three to four separate steps in a human-readable language thanks to the yaml format. Even a person who’s not an IT specialist will be able to understand the entire configuration of package delivery to Google Play and the Apple Store. Another advantage of this solution is the automation — it doesn’t require additional work. This greatly speeds up the lead time (time measured from making a change in the repository, to delivering the finished product to the store). It gives us more time to focus on code development, instead of operations required for the release. As it’s a code solution, it can be versioned in any VCS, which makes it safer to introduce new changes. Even if we break the entire process with an invalid change, we can easily revert those changes and go back to a stable version. Finally, all the required steps are already implemented and available in the Azure Pipeline Marketplace. You can use it for free, no extra knowledge required.

On the other hand, there are two disadvantages. One of them deals with Apple Certificates and Apple Provisioning Profiles. When they expire, you have to renew them manually and upload them to the secure files in the Azure DevOps Pipeline Library. A reminder or an extra solution like the Fastlane tool may be needed when we forget about the expiration, and our process is unable to deliver the package to the store. Another drawback comes into sight with the automated package delivery to Google Play where you have to generate the credential file and import the upload certificate to the keystore. It’s not necessary when submitting the app manually, so it delays the start of the delivery process. Last but not least, at the moment there is no option to keep the service account credential file in a secure place like the Azure Key Vault, instead it has to be placed in the repository. This may introduce problems with security and compliance.

Summary

Delivering packages to mobile stores using the Azure DevOps Pipelines steps is simple and cheap, while the disadvantages are relatively minor. All the steps are implemented, you just need to adopt them into your pipeline process. Moreover, the whole configuration is really simple and readable for any other project member. The ease of implementation is another advantage, especially for those who are just starting their journey with the Azure Pipelines. I am a big fan of this kind of solution. Hopefully, in the near future, the Azure Pipeline steps for delivering packages to Google Play will start supporting the Azure Key Vault or the secure files in the Azure Pipeline Library. This would make this solution safer, and in result, even better.

Attachments

Complete Azure Pipeline yaml file for Apple Store delivery

name: 1.0.$(BuildID)  

 

trigger: 

- master 

 

pool: 

  vmImage: 'macOS-10.15' 

 

steps: 

- task: InstallAppleCertificate@2 

  displayName: 'iOS Certificate instalation' 

  inputs: 

    certSecureFile: 'example_certification_file.p12' 

    certPwd: '$(ios_distribution_cert_password)' 

    keychain: 'temp' 

 

- task: InstallAppleProvisioningProfile@1 

  displayName: 'iOS provisioning Profile instalation' 

  inputs: 

    provisioningProfileLocation: 'secureFiles' 

    provProfileSecureFile: 'Example_Distribution.mobileprovision' 

 

- task: Xcode@5 

  displayName: 'Build iOS' 

  inputs: 

    actions: 'build' 

    configuration: 'Release' 

    sdk: 'iphoneos' 

    xcWorkspacePath: '**/Example.xcworkspace' 

    scheme: 'ExampleScheme' 

    xcodeVersion: 'specifyPath' 

    xcodeDeveloperDir: '/Applications/Xcode_11.5.app' 

    packageApp: true 

    archivePath: '$(system.defaultworkingdirectory)/archive' 

    signingOption: 'manual' 

    signingIdentity: '$(APPLE_CERTIFICATE_SIGNING_IDENTITY)' 

    provisioningProfileUuid: '$(APPLE_PROV_PROFILE_UUID)' 

 

- task: AppStoreRelease@1 

  displayName: 'Publish to AppStore' 

  inputs: 

    authType: 'UserAndPass' 

    username: 'example_username@example.domain' 

    password: '$(example_username_password)' 

    appIdentifier: '$(bundleid)' 

    appType: 'iOS' 

    ipaPath: '**/*.ipa' 

    releaseTrack: 'TestFlight' 

    shouldSkipWaitingForProcessing: true 

  Complete Azure Pipeline yaml file for Google Play delivery 

name: 1.0.$(BuildID)  

 

trigger: 

- master 

 

pool: 

  vmImage: 'ubuntu-18.04' 

 

steps: 

 

- task: CmdLine@2 

  displayName: 'Build Android' 

  inputs: 

    script: | 

      ionic cordova platform add android 

      ionic cordova build --configuration prod --release android --verbose 

 

- task: AndroidSigning@3 

  displayName: 'Sign .apk package' 

  inputs: 

    apkFiles: 'platforms/android/**/*.apk' 

    apksignerKeystoreFile: 'example_keystore' 

    apksignerKeystorePassword: '$(keystore_password)' 

    apksignerKeystoreAlias: 'example.alias' 

 

- task: GooglePlayRelease@3 

  displayName: 'Upload .apk package' 

  inputs: 

    authType: 'JsonFile' 

    serviceAccountKey: 'service_account_credential_file.json' 

    apkFile: 'platforms/android/**/*.apk' 

    track: 'beta' 

 
Retail Report Resources
Daniel Stach DevOps Engineer

A DevOps enthusiast with a passion for automation and reliable solutions. In his free time, Daniel enjoys traveling and playing football with his friends.

See all Daniel's posts

Related posts

You might be also interested in

Contact

Start your project with Objectivity

CTA Pattern - Contact - Middle

We use necessary cookies for the functionality of our website, as well as optional cookies for analytic, performance and/or marketing purposes. Collecting and reporting information via optional cookies helps us improve our website and reach out to you with information regarding our organisaton or offer. To read more or decline the use of some cookies please see our Cookie Settings.