Skip to main content

Self-hosted Live Updates with Azure DevOps

Overview

Self-hosted Live Updates allows customers to build and host their web artifacts then deploy them as Live Updates to app users. To help ensure secure end-to-end delivery, code signing and signature verification steps are included as well.

This example walks through setting up Self-hosted live updates in a Capacitor app using Azure DevOps.

Requirements

For this example, we've already followed the Self-hosted live update App Setup instructions. This includes creating an Appflow app, a personal access token (PAT), a public/private key, and configuring the Capacitor app.

Azure DevOps Setup

With your Capacitor app configured to use self-hosted live updates, we can configure Azure DevOps next.

Store the Private Key Securely

To store the private key securely in Azure, upload it to "Secure Files", found under Pipelines -> Library -> Secure files. This also makes it available to the pipeline we'll write shortly.

Store a Personal Access Token Securely

The personal access token is used to authenticate access to your Appflow account via the Ionic Cloud CLI, so it also needs to be made available to the pipeline. Under Pipelines -> Library -> Variable groups, create a new variable group ("Ionic Tokens" for example), then create a variable named APPFLOW_PAT (or similar). Click the lock icon to mark the variable as secret.

Create an Azure Storage Blob

The final Azure setup step involves creating an Azure storage blob to host the live updates. From the Azure Portal, navigate to Storage Accounts and create a new one. You may need to create a new Resource group (such as "liveupdates"),

On the next screen, Advanced, turn on "Allow enabling public access on containers." Public access is required for the Capacitor Live Updates plugin to download live updates onto your app user's devices. It's worth noting that by default, Azure encrypts storage account data at rest. Turn on "Require secure transfer for REST API operations" and set Access tier to "Hot", ideal for frequently access data such as live updates.

Once the Storage Account has been created, navigate into it and under Data Storage -> Containers, create a new container ("liveupdates"). This is where the live update artifacts will be stored. Set the Public access level to "Blob (anonymous read access for blobs only)."

Once the container has been created, click into it and navigate to Properties. Note the URL - this is the public URL that we'll register with Appflow. An example URL looks like: https://ionicselfhosted.blob.core.windows.net/liveupdates.

Write a Pipeline

Now, we're ready to write an Azure pipeline that creates and registers a live update. Here's the complete YAML script:

Complete YAML Script

# Self-hosted Live Updates pipeline example
trigger:
- main

pool:
vmImage: ubuntu-latest

variables:
- group: "Ionic Tokens"
- name: liveUpdateFilepath
value: "live-update-bundle-$(Build.SourceVersion)"
- name: newLiveUpdateArtifactPath
value: LIVE_UPDATE_CONTAINER/$(liveUpdateFilepath)/live-update-manifest.json

steps:

- task: Npm@1
inputs:
command: 'install'
workingDir: '$(Build.SourcesDirectory)'
displayName: 'npm Install'

- script: |
npm run build
workingDirectory: $(Build.SourcesDirectory)
displayName: 'Build Web'

- script: curl -sL https://ionic.io/get-ionic-cloud-cli | bash
displayName: 'Install Ionic Cloud CLI'

# Download a secure file to the agent machine
- task: DownloadSecureFile@1
name: ionicSigningKey
inputs:
secureFile: 'PEM_FILE'

# Generate Live Update manifest file
- script: |
ionic-cloud live-update generate-manifest
--build-dir=DIR
--token=$(APPFLOW_PAT)
--signing-key=$(ionicSigningKey.secureFilePath)
displayName: 'Generate Live Update manifest file'

# Upload the live update to Azure Storage
- task: AzureCLI@2
inputs:
azureSubscription: 'SUBSCRIPTION'
scriptType: pscore
scriptLocation: 'inlineScript'
inlineScript: |
az storage blob upload-batch
--account-name ACCOUNT_NAME
-d liveupdates
--destination-path $(liveUpdateFilepath)
-s www/
displayName: 'Upload all live update files to Azure blob storage'

# Register the signed live update with Appflow
- script: |
ionic-cloud live-update register-artifact
--app-id=APP_ID
--artifact-url='$(newLiveUpdateArtifactPath)'
--artifact-type=manifest
--commit-ref=$(Build.SourceBranchName)
--commit-sha=$(Build.SourceVersion)
--commit-message='$(Build.SourceVersionMessage)'
--token=$(APPFLOW_PAT)
displayName: 'Register live update with Appflow'

Let's walk through this pipeline step by step.

Pipeline Trigger and VM

This pipeline will run every time there is a commit to the "production" branch. We recommend running on a Linux VM, so ubuntu-latest is used.

# Self-hosted Live Updates pipeline example
trigger:
- production

pool:
vmImage: ubuntu-latest

Create Variables

Next, we'll prepare some variables.

  • group: Including the "Ionic Tokens" group makes the personal access token we stored earlier (APPFLOW_PAT) accessible to this pipeline.
  • liveUpdateFilepath: The filename for the live update that will be created in this pipeline. Adding the Git commit SHA to the end (Build.SourceVersion) will make each live update filename unique. newLiveUpdateArtifactPath: The URL to the Azure storage blog we created earlier, such as https://ionicselfhosted.blob.core.windows.net/liveupdates/$(liveUpdateFilepath)/live-update-manifest.json.
variables:
- group: "Ionic Tokens"
- name: liveUpdateFilepath
value: "live-update-bundle-$(Build.SourceVersion)"
- name: newLiveUpdateArtifactPath
value: LIVE_UPDATE_CONTAINER/$(liveUpdateFilepath)

Build the Web App

Next, we build the web app. Make any changes needed to build your app.

steps:

- task: Npm@1
inputs:
command: 'install'
workingDir: '$(Build.SourcesDirectory)'
displayName: 'npm Install'

- script: |
npm run build
workingDirectory: $(Build.SourcesDirectory)
displayName: 'Build Web'

Install Ionic Cloud CLI

Next, install the Ionic Cloud CLI so we can use Self-hosted live update features:

- script: curl -sL https://ionic.io/get-ionic-cloud-cli | bash
displayName: 'Install Ionic Cloud CLI'

Download Private Key to VM

Next, we download the private key file we stored earlier to make it accessible to this pipeline.

  • secureFile: The private key pem file, such as ionic_cloud_private.pem.
# Download a secure file to the agent machine
- task: DownloadSecureFile@1
name: ionicSigningKey # The name with which to reference the secure file's path on the agent
inputs:
secureFile: 'PEM_FILE'

Create a Signed Live Update

Now that the web app has been built, we can use the Cloud CLI generate-manifest command to create a live update manifest file. The resulting live-update-manifest.json file includes a hash for every file in the compiled web app directory. If a native app has a live update manifest file, local files on disk are compared with the manifest and only changed files are downloaded. This makes the update much smaller and faster, resulting in improved performance over slower connections.

  • build-dir: The top-level build directory containing the compiled web app, such as www for an Angular app.
  • signing-key: The private key we retrieved above, used to sign the live update.
  • token: The personal access token pulled in from the "Ionic Tokens" variable group above.
# Bundle the web artifact
- script: |
ionic-cloud live-update generate-manifest
--build-dir=DIR
--signing-key=$(ionicSigningKey.secureFilePath)
--token=$(APPFLOW_PAT)
displayName: 'Generate Live Update manifest file'

Upload Live Update to Azure Blob Storage

With the new live update created, we can upload it to Azure blob storage. There are several ways to do so; in this case, we can use the Azure CLI task. We recommend adding this step using the Assistant (search for the "azure cli" task). This example uses the upload-batch command.

  • azureSubscription: The subscription name and GUID (will look similar to 'Azure subscription 1(cdc3c210-xxxxxx)').
  • scriptType: Set to pscore (PowerShell Core) and location to inlineScript.
  • inlineScript: Configure the inline script with various upload parameters. --account-name is the storage account name, -d is the destination blob container where the files will be uploaded, --destination-path is the destination path that will be prepended to the blob name, and -s is the source directory where the files to be uploaded are located (www/ in an Angular app).
# Upload the live update to Azure Storage
- task: AzureCLI@2
inputs:
azureSubscription: 'SUBSCRIPTION'
scriptType: pscore
scriptLocation: 'inlineScript'
inlineScript: |
az storage blob upload-batch
--account-name ACCOUNT_NAME
-d liveupdates
--destination-path $(liveUpdateFilepath)
-s DIR
displayName: 'Upload all live update files to Azure blob storage'

Register Signed Live Update with Appflow

In this last step, inform Appflow that a new live update is available with the Cloud CLI register-artifact command.

  • app-id: The Appflow app id, like 042a1261. Find this value under the app's name in Appflow or the URL (https://dashboard.ionicframework.com/app/042a1261/getting-started).
  • artifact-type: The type of artifact being registered, either zip or manifest for differential live updates (recommended).
  • artifact-url: The URL where the new live update artifact is stored. The Capacitor live update plugin will download the live update from your storage provider. When using manifest artifact-type (recommended), URL should end with live-update-manifest.json. When using zip artifact-type, URL should end with .zip.
  • token: The Appflow personal access token.
  • commit-ref: The branch name of the commit, such as "main" or "production."
  • commit-sha: The Git commit SHA, such as "adf9137y45".
  • commit-message: The Git commit message, such as "Fix login bug."
# Register the signed live update with Appflow
- script: |
ionic-cloud live-update register-artifact
--app-id=APP_ID
--artifact-type=manifest
--artifact-url='$(newLiveUpdateArtifactPath)'
--commit-ref=$(Build.SourceBranchName)
--commit-sha=$(Build.SourceVersion)
--commit-message='$(Build.SourceVersionMessage)'
--token=$(APPFLOW_PAT)
displayName: 'Register live update with Appflow'

Save and run the pipeline. A Self-hosted live update hosted in Azure is now available!

Deploy a Live Update

Once the pipeline has finished running, you'll find the new Self-hosted live update in your App's Builds list in Appflow. From there, you can follow the standard Live Update flow of assigning the build to a Live Update Channel.