ConnectIQ build with Azure DevOps

Published on Friday, July 5, 2019

Introduction

Two weeks ago I got an idea about a new watchface for my Garmin Fenix 5X sportwatch. Garmin Connect IQ platform let developers write applications in Monkey C 🐵 language (a language similar to C++ and Java, more info on Garmin website).

For both work and personal applications I already use Azure DevOps so why don't use it also for this project? I developed application with Eclipse using the official SDK provided by Garmin. After that i pushed all the code on a GIT repository in Azure Repos and now I'm ready to write build pipeline!

Signing key

Garmin ConnectIQ build process is simple enough but developers need to sign applications using a key.

The process to generate key is well documented but I need to convert my developerkey.der in something usable in a build pipeline.

NEVER commit such things (private keys, certificates...) in the git repository along with source code!

I decided to host my key on Azure Key Vault: an Azure service designed to keep secure password, keys, certificates...

First of all I need the base64 string of our developerkey.der with the command:

base64 -w 0 developerkey.der

Then I copy the generated string and paste it in a new secret on Azure KeyVault:

Ok, now it's all set and I'm ready to show you the final pipeline.

Azure Pipeline

azure-pipelines.yaml

trigger:
- master

pool:
  vmImage: 'Ubuntu-16.04'

steps:
- script: |
    sudo apt-get update -y;
    sudo apt-get install -qqy openjdk-8-jdk;
    sudo apt-get install -qqy unzip wget git ssh tar gzip ca-certificates;
    sudo apt-get clean;
    sudo rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*;
  displayName: 'Install Dependancies'

- script: |
    cd /opt;
    wget -q https://developer.garmin.com/downloads/connect-iq/sdks/connectiq-sdk-lin-3.0.9-2019-3-25-0df4ba2.zip -O ciq.zip;
    unzip ciq.zip -d ciq;
    rm -f ciq.zip;
  displayName: 'Install ConnectIQ SDK'

- task: AzureKeyVault@1
  inputs:
    azureSubscription: AZURE_SUBSCRIPTION
    keyVaultName: KEYVAULT_NAME
    secretsFilter: SECRET_NAME
  displayName: 'Get Developer Key'

- script: |
    echo '$(SECRET_NAME)' | base64 --decode > /opt/connectiq-developer-key.der;
  displayName: 'Write Developer Key'

- script: |
    java -Dfile.encoding=UTF-8 -Dapple.awt.UIElement=true -jar /opt/ciq/bin/monkeybrains.jar -o $(Build.ArtifactStagingDirectory)/MY_APP.iq -e -w -y /opt/connectiq-developer-key.der -r -f $(Build.SourcesDirectory)/monkey.jungle;
  displayName: 'Build & Package iq file'

- task: PublishBuildArtifacts@1
  inputs:
    pathtoPublish: '$(Build.ArtifactStagingDirectory)/MY_APP.iq' 
    artifactName: 'iq'
  displayName: 'Publish Artifact'

Explanation

Let's scompose and analyze every task:

Trigger

trigger:
- master

This is a CI build and I want that every commit to master trigger a new build.

Base image

pool:
  vmImage: 'Ubuntu-16.04'

Build is executed on a virtual machine (to be more precise in a container) running Ubuntu 16.04

Prerequisites installation

- script: |
    sudo apt-get update -y;
    sudo apt-get install -qqy openjdk-8-jdk;
    sudo apt-get install -qqy unzip wget git ssh tar gzip ca-certificates;
  displayName: 'Install Dependancies'

Let's start with the interesting part.

This first bash script install dependancies for Connect IQ SDK

Garmin ConnectIQ SDK installation

- script: |
    cd /opt;
    wget -q https://developer.garmin.com/downloads/connect-iq/sdks/connectiq-sdk-lin-3.0.9-2019-3-25-0df4ba2.zip -O ciq.zip;
    unzip ciq.zip -d ciq;
    rm -f ciq.zip;
  displayName: 'Install ConnectIQ SDK'

Now let's download and unzip ConnectIQ SDK.

Remember to replace last part of download link with your desidered SDK version (if you're not using 3.0.9).

Key retrieval

- task: AzureKeyVault@1
  inputs:
    azureSubscription: AZURE_SUBSCRIPTION
    keyVaultName: KEYVAULT_NAME
    secretsFilter: SECRET_NAME
  displayName: 'Get Developer Key'

Now we need to retrieve signing key.

This AzureKeyVault task creates a new variable containing the secret(s) specified in secretsFilter parameter.

Key save

- script: |
    echo '$(SECRET_NAME)' | base64 --decode > /opt/connectiq-developer-key.der;
  displayName: 'Write Developer Key'

The base64 encoded key is now available as environment variable in our build process but we need to store it in a file. This single line script just do this.

Build

- script: |
    java -Dfile.encoding=UTF-8 -Dapple.awt.UIElement=true -jar /opt/ciq/bin/monkeybrains.jar -o $(Build.ArtifactStagingDirectory)/MY_APP.iq -e -w -y /opt/connectiq-developer-key.der -r -f $(Build.SourcesDirectory)/monkey.jungle;
  displayName: 'Build & Package iq file'

Let's build and package our iq file calling monkeybrains jar 🙉!

Publish artifact

- task: PublishBuildArtifacts@1
  inputs:
    pathtoPublish: '$(Build.ArtifactStagingDirectory)/MY_APP.iq' 
    artifactName: 'iq'
  displayName: 'Publish Artifact'

To close the build process let's upload iq file as Build Artifact.

Finished! Now our iq file is ready to be uploaded and distributed on Garmin Connect IQ store!