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!