CICD for Mobile App Development Using Capacitor JS on Azure DevOps

In the world of iOS mobile app development, implementing a robust CI/CD (Continuous Integration/Continuous Delivery) pipeline is essential to ensure efficient and reliable software delivery. Capacitor JS is a powerful framework that allows developers to build cross-platform mobile apps using web technologies. When combined with Azure DevOps, it enables a seamless CI/CD pipeline for iOS app development. In this blog post, we will guide you through the process of setting up a CI/CD pipeline for iOS mobile apps using Capacitor JS and Azure DevOps.

A Note on Capacitor JS

Capacitor is a free and open source (MIT-licensed) platform that enables web developers to build cross-platform apps with standard web technology that runs in modern browsers. Capacitor takes your existing web application and runs it as a native app on each platform, providing hooks into the native platform via JavaScript. These hooks can be built directly into the app, or as standalone plugins to be reused and distributed to others.

Can I reuse existing web code and share new code with a web app?
Yes! One of the strengths of Capacitor is that it runs normal web apps natively. In many cases, teams have a single codebase for web and mobile using Capacitor.

Prerequisite –

1. An azure devops account.
2. Working Web app code
3. App center account for distribution

Let’s begin

Setup an azure devops account. 
By default under free subscription we get to use linux VMs. In order to use mac VM, we will need to get subscription. Only one agent will be more than enough for our CICD pipeline.

Create an organization and a project. Once we have create our project, we move forward to create a new pipeline.


Configure to link the current project with your code base, which can reside in any VCS. We will be using bitbucket cloud so need to configure the same.


Connect, select, configure and review according to your requirements.

Once done, we will start with writing code for the pipeline. If you already worked with azure pipelines, you might know how easy it is to add steps since azure provides us with an assistant.




Step 1 – configure pool image – we are developing for ios so have configured macOS-12, since the default xcode version provided by macOS-12 is xcode 14, which satisfies our requirement.


Step 2 – optional – configure environment variables for your pipeline. In this I’m adding variable to specify the deployment environment.


Step 3 – Download the required node version on the agent.


Step 4 – Installing dependencies and capacitor core and cli.


Step 5 – Building the app using “npm run ng build”


Step 6 – npx sync for ios/android folder 


Step 7 – Install apple provisioning certificate and profiles. This requires us to first upload the certificate and profile files to azure, under secure files inside the library in pipelines.

Once uploaded, we need to add step in our pipeline to install the profile and certificate


Step 8 – Install cocoapods in synced ios folder. Cocoapods helps you to manage and incorporate third-party libraries into your project without getting concerned about setting up and configuring your project.


Step 9 – Xcode build- this will be the step where we bundle and package our application to .ipa file

Step 10 – optional – Naming ipa file.It  is best practice to name the app with some meaningful name, based on environment and version and moving finished app to artifact staging


Step 11 – Publish artifact 


Step 12 – Publish to App center. For this, add a service connection for App center in Project setting>Service connection. To generate a service connection, create an account at app center and create a new API token, making sure the API token has full access (so that Azure DevOps can create new releases for distribution), then copy the key. We will use this service connection to establish communication between azure devops and the app center. Alternatively, we can upload the artifact at s3 bucket as well, depending upon the requirements.


We’ll need to add the appSlug and distributionGroupId from app center account as well.
Once done, this is what the pipeline looks like –  

pool:
    vmImage: macOS-12

parameters:
  - name: environment
    displayName: Configuration based on env
    type: string
    default: DEV
    values:
    - PROD
    - UAT
    - SIT
    - DEV
        
steps:
     
- task: NodeTool@0
  displayName: install node version
  inputs:
    versionSource: 'spec'
    versionSpec: '16.18.0'

- task: CmdLine@2
  displayName: install dependecies
  inputs:
    script: |
      npm install --ignore-scripts
      npm install [email protected] --save-dev
      npm i @capacitor/core
      npm i -D @capacitor/cli
      npm install @capacitor/ios
      
- task: CmdLine@2
  displayName: npm build
  inputs:
    script: 'npm run ng build -- --c=${{ parameters.environment }}'

- task: CmdLine@2
  displayName: sync up android and iOS folder
  inputs:
    script: |
        npx cap sync

- task: InstallAppleCertificate@2
  displayName: install apple signing certificate
  inputs:
    certSecureFile: 'P12_signing_cert.p12'
    certPwd: '****'
    keychain: 'temp'

- task: InstallAppleProvisioningProfile@1
  displayName: install apple provisioning profile
  inputs:
    provisioningProfileLocation: 'secureFiles'
    provProfileSecureFile: 'DEV_PROFILE.mobileprovision'

- task: CocoaPods@0
  displayName: install cocoapods
  inputs:
    workingDirectory: 'ios/App'
    forceRepoUpdate: false

- task: Xcode@5
  displayName: 'Building IPA File'
  inputs:
    actions: 'build archive'
    sdk: iphoneos
    xcWorkspacePath: 'ios/App/App.xcworkspace'
    scheme: 'App'
    packageApp: true
    signingOption: manual
    signingIdentity: '$(APPLE_CERTIFICATE_SIGNING_IDENTITY)'
    provisioningProfileUuid: '$(APPLE_PROV_PROFILE_UUID)'
    teamId: '******'
    xcodeVersion: "default"
  

- task: CmdLine@2
  displayName: Naming ipa file
  inputs:
    script: |
        appversion=$(cat package.json | head | grep -w "version" | awk '{print $2}' |cut -c 2-6)
        mv /Users/runner/work/1/s/output/App.ipa /Users/runner/work/1/s/output/myapp_${{parameters.environment}}_${appversion}.ipa

- task: CopyFiles@2
  displayName: 'Copy Files to: $(build.artifactStagingDirectory)' 
  inputs:
    SourceFolder: '/Users/runner/work/1/s/output'
    Contents: '*.ipa'
    TargetFolder: '$(Build.ArtifactStagingDirectory)'


- task: PublishBuildArtifacts@1
  displayName: 'Publish Artifact: drop'
  inputs:
    PathtoPublish: '$(Build.ArtifactStagingDirectory)'
    ArtifactName: 'ios'
    publishLocation: 'Container'

- task: AppCenterDistribute@3
  displayName: distribute to app center
  inputs:
      serverEndpoint: 'mobileCI'
      appSlug: '******'
      appFile: '$(Build.ArtifactStagingDirectory)/**/*.ipa'
      releaseNotesOption: 'input'
      releaseNotesInput: 'Test Version'
      symbolsOption: 'Apple'
      destinationType: 'groups'
      isSilent: False
      distributionGroupId: '*******'
    

Save and run the pipeline. Once completed, we can see the published artifact on Azure devops pipelines build page. Check at the app center for the latest version of the app available.

Additionally, we can also configure teams channel notifications to notify team whenever a build is triggered : 

Create a teams channel, and add azure pipelines app to the channel.
Login to the azure account, but first we will also need to allow third party application access via oAuth  in our azure organization settings – under Security > Policies

Now from teams channel, we need to subscribe to our pipeline/project, depending on what all notifications we need.

Conclusion

Implementing a CI/CD pipeline for iOS mobile app development using Capacitor JS and Azure DevOps streamlines the software delivery process, enhances collaboration, and ensures the quality of your iOS apps. By following the steps outlined in this blog post, you can establish an efficient CI/CD pipeline to automate build, test, and deployment processes, allowing you to deliver high-quality iOS apps to your users quickly and reliably.

Blog Pundits: Mehul Sharma and Sandeep Rawat

OpsTree is an End-to-End DevOps Solution Provider.

Connect with Us

2 thoughts on “CICD for Mobile App Development Using Capacitor JS on Azure DevOps”

Leave a Reply