Most firmware engineers are familiar with GitHub as a code hosting platform for version controlled projects. However GitHub also has a very powerful automation tool called GitHub Actions. Since GH Actions is built into your GH repository it's very easy to integrate into your workflow.
For example, with GH Actions you can easily do all of the following on a {% c-line %}git push{% c-line-end %}:
- Run a code linter
- Build your project
- Run unit-tests (and detect failures)
- Create assets (e.g. merged hex image)
Adding GitHub Actions to your repository is straightforward:
- Create a top-level folder structure called {% c-line %}.github/workflows{% c-line-end %}
- Add 1 or more workflow files, e.g. {% c-line %}.github/workflows/build_actions.yml{% c-line-end %}
- In the workflow file add the actions you would like to perform
You can have multiple workflow files in your workflows folder. Each workflow is triggered by a specific event defined in the file. To demonstrate how easy it is to set up a simple workflow, fork and then clone the following project:
Now open the file {% c-line %}.github/workflows/build_actions.yml{% c-line-end %} in the project and let's walk through each section.
First we're going to describe the workflow and set which events will trigger it.
{% c-block language="yaml" %}
name: Simple pipeline to build firmware and save generated image #Describe workflow
on: [push] #trigger workflow on every push event
{% c-block-end %}
Next, we handle the possibility of multiple users triggering a workflow by enabling {% c-line %}concurrency{% c-line-end %}.
{% c-block language="yaml" %}
concurrency:
group: DUT #Name concurrency group
cancel-in-progress: false #Allow in-progress workflow to continue to completion
{% c-block-end %}
GitHub Actions are organized by {% c-line %}jobs{% c-line-end %} and {% c-line %}steps{% c-line-end %}. A {% c-line %}job{% c-line-end %} consists of one or more {% c-line %}steps{% c-line-end %}. Jobs can run in parallel with other jobs and can also be configured to run conditionally, for example, to only run if a previous job succeeds. {% c-line %}steps{% c-line-end %} on the other hand always run in series, and are the set of instructions that a job is to carry out.
We see that this workflow has a single job called {% c-line %}build{% c-line-end %}.
{% c-block language="yaml" %}
jobs: #jobs are independent tasks that can either run serially or in parallel, and can also depend on the outcome of other jobs
build: #this the start of the job "build". this can be called anything
runs-on: ubuntu-latest #what operating system this job will run on
steps:
{% c-block-end %}
Under the {% c-line %}build{% c-line-end %} job, there are four steps:
Step 1: Checkout the code
This step clones the project and pulls in any submodules.
{% c-block language="yaml" %}
- name: Checkout the code
uses: actions/checkout@v2
with:
submodules: 'recursive'
{% c-block-end %}
Step 2: Run linter
This step will use {% c-line %}cpplint{% c-line-end %} to lint our source code before building it.
{% c-block language="yaml" %}
- name: Run linter
uses: docker://lagerdata/devenv-cortexm-nrf52
with:
entrypoint: /usr/local/bin/cpplint
args: /github/workspace/application/src/main.c
{% c-block-end %}
Step 3: Build Project
After verifying that our source code is properly linted, we'll go ahead and build the project
{% c-block language="yaml" %}
- name: Build Project
uses: docker://lagerdata/devenv-cortexm-nrf52 #use build environment in docker image lagerdata/devenv-cortexm-nrf52 to build project
with:
entrypoint: /usr/bin/make #build project using make
{% c-block-end %}
Step 4: Save hex image
Finally we'll save the hex file so that we have a record of the image generated as part of this build.
{% c-block language="yaml" %}
- name: Save hex image
uses: actions/upload-artifact@v2 #Save generated image
with:
name: blinky_hex_image
path: _build/nrf52840_xxaa.hex
{% c-block-end %}
The last step {% c-line %}Save hex image{% c-line-end %} illustrates another advantage of using GH Actions to automate your builds. Because you can save assets as part of a workflow, which in turn is linked to a specific commit hash, you eliminate the need to include tagged images in the repository, avoiding repo bloat.
Happy automating!
Reach out to learn more about Lager's hardware test automation platform.
Try One Month Free
hello@lagerdata.com