Release Rust embedded firmware using Github Actions
Github Actions #
Github Actions is a nice way to setup CI/CD pipelines for your Github projects. Let’s setup it for an embedded firmware project written in Rust.
Continuous integration script would run every time when new code is pushed to master
branch and release script would run only if a new tag is pushed.
Release in Github looks like this:
Let’s get started. We need following files in project root:
.github/workflows/ci.yaml
.github/workflows/release.yaml
File names actually do not matter.
ci.yaml #
It is assumed that your Cargo project is setup in such a way that you could just run cargo build --release
in root directory and everything builds. This script is doing just that:
name: Continuous Integration
on:
push:
branches:
- master
env:
CARGO_TERM_COLOR: always
jobs:
compile:
runs-on: ubuntu-latest
strategy:
matrix:
toolchain:
- stable
steps:
- uses: actions/checkout@v2
- name: Install Rust ${{ matrix.toolchain }}
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ matrix.toolchain }}
target: thumbv6m-none-eabi
override: true
- name: cargo build release
uses: actions-rs/cargo@v1
with:
command: build
args: --release
release.yaml #
It is assumed that all tags are like 0.9.1
, 1.0.0
etc. So when such a tag is pushed following action will run:
name: Release
on:
push:
tags:
- '*.*.*'
env:
CARGO_TERM_COLOR: always
jobs:
compile:
runs-on: ubuntu-latest
strategy:
matrix:
toolchain:
- stable
steps:
- uses: actions/checkout@v2
- name: Install Rust ${{ matrix.toolchain }}
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ matrix.toolchain }}
target: thumbv6m-none-eabi
override: true
components: llvm-tools-preview
- name: Install cargo-binutils
uses: actions-rs/install@v0.1
with:
crate: cargo-binutils
version: latest
use-tool-cache: true
- name: Run cargo build --release
uses: actions-rs/cargo@v1
with:
command: build
args: --release
- name: Run cargo objcopy
uses: actions-rs/cargo@v1
with:
command: objcopy
args: --release --bin firmware -- -O binary firmware.bin
- name: Extract version from tag
id: version_tag
run: echo ::set-output name=TAG_VERSION::${GITHUB_REF#refs/tags/}
- name: Rename .elf and .bin files
env:
TAG_VERSION: ${{ steps.version_tag.outputs.TAG_VERSION }}
run: |
mv target/thumbv6m-none-eabi/release/firmware firmware-$TAG_VERSION.elf
mv firmware.bin firmware-$TAG_VERSION.bin
- name: Create release
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: firmware*
tag: ${{ github.ref }}
overwrite: true
file_glob: true
This is a little bit more involved. Basically we would like to release following files if tag 1.0.1
is pushed:
firmware-1.0.1.elf
firmware-1.0.1.bin
Those files can be downloaded from Github release page.
Notes #
It was kind of pain to strip 1.0.1
part out of tag which is like refs/tags/1.0.1
. That is why there is separate step for that:
- name: Extract version from tag
id: version_tag
run: echo ::set-output name=TAG_VERSION::${GITHUB_REF#refs/tags/}
Later it can be used as an expression (do not confuse it with bash environment variable) :
${{ steps.version_tag.outputs.TAG_VERSION }}
Notice that this script also installs llvm-tools-preview
and basically does cargo install cargo-binutils
which is needed to convert from .elf
to .bin
.
There you have it!