Automatically Upload Symbols and Deploy Releases from CI
Uploading symbol files and deploying OTA updates from your CI system is a great way to automate the process of releasing new firmware versions. This guide will show you how to use the Memfault CLI to upload symbol files and deploy OTA updates from your CI system.
Methodology
The Memfault CLI is used to upload symbol files and deploy OTA updates, once the firmware has been built. The steps are as follows:
-
Build the firmware, generating a symbol file and an OTA payload. In Zephyr, this is typically a
.elf
file and a.bin
file, respectively:build/zephyr/zephyr.elf
build/zephyr/app_update.bin
Memfault always recommends signing OTA artifacts, to protect devices from installing counterfeit/invalid firmware. Zephyr has built-in support for signed OTA images using MCUBoot.
-
Use the Memfault CLI to upload the symbol file, with the
upload-mcu-symbols
command:memfault upload-mcu-symbols build/zephyr/zephyr.elf
-
Use the Memfault CLI to create the Release and deploy the OTA update, with the
upload-ota-payload
anddeploy-release
commands:# note: this command both creates the Release, if it doesn't exist, and
# uploads the OTA payload
memfault upload-ota-payload \
--hardware-version <hardware_version> \
--software-type <software_type> \
--software-version <software_version> \
build/zephyr/app_update.bin
memfault deploy-release \
--release-version <software_version> \
--cohort <cohort>
GitHub Action for Uploading Symbols
Memfault provides a simple GitHub Action for uploading symbol files. Here's an example of how to use it:
- name: Upload symbol file to Memfault
uses: memfault/github-action-upload-symbols@v1
with:
MEMFAULT_ORG_TOKEN: ${{ secrets.MEMFAULT_ORG_TOKEN }}
MEMFAULT_ORG_SLUG: ${{ secrets.MEMFAULT_ORG_SLUG }}
MEMFAULT_PROJECT_SLUG: ${{ secrets.MEMFAULT_PROJECT_SLUG }}
symbol_file: symbols.elf
See the action here: https://github.com/memfault/github-action-upload-symbols
GitHub Actions Example: Upload Symbols and Deploy Release
Here's a full example using GitHub Actions to build + upload + deploy a firmware release. This example is using Zephyr, but the principles are the same for other RTOSs or bare-metal projects.
Set up some GitHub Actions secrets, for the Organization Auth Token and the Memfault Organization and Project (for private repos, those last two don't need to be secrets).
And this is the example workflow file:
# Depending on your project needs, you can have the build + upload trigger on
# different conditions. This example only uploads + deploys when a tag is
# created.
on:
pull_request:
branches:
- main
push:
branches:
- main
workflow_dispatch:
# run on tag creation
create:
tags:
env:
# these must be as returned by memfault_platform_get_device_info() in the
# firmware being built
HARDWARE_VERSION: mp
SOFTWARE_TYPE: nrf91ns-fw
# this Cohort will be the target for the release deployment
COHORT: auto-ota
jobs:
build:
runs-on: ubuntu-latest
# In a real implementation, it's likely a container would be used.
# See a worked example here:
# https://interrupt.memfault.com/blog/ncs-github-actions
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Build firmware
# need to run as bash for '== "refs/tags/"*' to work
shell: bash
id: build
run: |
# create a version string. if it's a tag build, use the tag name,
# otherwise use the most recent tag + git short sha
cd nrfconnect-ci-app
if [ -n "$GITHUB_REF" ]; then
if [[ "$GITHUB_REF" == "refs/tags/"* ]]; then
VERSION=$(echo $GITHUB_REF | sed 's/refs\/tags\///')
else
VERSION=$(git describe --tags --abbrev=0)
VERSION="${VERSION}+$(git rev-parse --short HEAD)"
fi
else
echo "ERROR: Cannot determine version"
exit 1
fi
# save the version string to this step's output
echo "version=${VERSION}" > ${GITHUB_OUTPUT}
<your build commands here, i.e. west build>
# if the Docker image used for the build doesn't already have Python,
# you can install it with this GitHub Action
- name: Set up Python
id: python
uses: actions/setup-python@v4
with:
python-version: 3.10
- name: Upload payload and deploy release
if: github.event_name == 'create' && github.event.ref_type == 'tag'
run: |
# install the memfault CLI
pip install memfault-cli==1.0.6
# upload the symbol file
memfault \
--org-token ${{ secrets.MEMFAULT_ORG_TOKEN }} \
--org ${{ secrets.MEMFAULT_ORG }} \
--project ${{ secrets.MEMFAULT_PROJECT }} \
upload-mcu-symbols build/zephyr/zephyr.elf
# create the release and upload the OTA payload. the version is from
# the build step
memfault \
--org-token ${{ secrets.MEMFAULT_ORG_TOKEN }} \
--org ${{ secrets.MEMFAULT_ORG }} \
--project ${{ secrets.MEMFAULT_PROJECT }} upload-ota-payload \
--hardware-version ${{ env.HARDWARE_VERSION }} \
--software-type ${{ env.SOFTWARE_TYPE }} \
--software-version ${{ steps.build.outputs.version }} \
build/zephyr/app_update.bin
memfault \
--org-token ${{ secrets.MEMFAULT_ORG_TOKEN }} \
--org ${{ secrets.MEMFAULT_ORG }} \
--project ${{ secrets.MEMFAULT_PROJECT }} deploy-release \
--release-version ${{ steps.build.outputs.version }} \
--cohort ${{ env.COHORT }}