Building and Testing OS Images with GitHub Actions

3 minute read

In this blog post I present a game changer for the future development of edi based project configurations (such as edi-pi, edi-var and edi-cl). The following two items will help me - and hopefully others - a lot to further improve the quality of edi and its project configurations:

Self-hosted Runner

The following sketch depicts the setup of the self-hosted runner:

Runner Setup

As a prerequisite a GitOps ready Raspberry Pi 4 got flashed (see edi-pi, command sudo edi -v image create pi4-bullseye-arm64-gitops.yml). However, this time we do not turn the Raspberry Pi into a kiosk terminal. Instead, we choose to go with a different configuration and enter the following key/value pairs using the Mender configure add on (step ①):

Mender Configure

Accordingly - after receiving the config artifact ② - the device will clone the playbook edi-gh-actions-runner-playbook ③ (main branch). The playbook itself makes use of two roles ④:

The roles will then fetch the runner binaries from GitHub ⑤ and a lot of Debian packages from the upstream Debian repositories and the edi PackageCloud repository ⑥.

If something should go wrong, then the Mender remote terminal add-on might be useful:

Debug Terminal

However - once everything is properly setup - nothing should go wrong anymore and the self-hosted runner will connect to the edi-ci GitHub repository:

Registered Runner

Please note that the edi-ci project is a private repository according to the GitHub security recommendation. In order to still make the setup transparent, I provide the cloned repository edi-ci-public.

As a bonus even a complete OS update on the runner is possible and the whole setup will get re-applied thanks to this commit.

OS Build and Test Workflow

Using the self-hosted runner we are now able to trigger our OS build, dispatch and test workflow:

Workflow Setup

As a first step (the only manual step) we tell the workflow which configuration (here iot-gate-imx8-bullseye-arm64.yml) of which repository (here edi-cl using the main branch) shall be applied to our chosen device (referenced by the Mender device id) ①:

Run Workflow

The job will then be sent to our self-hosted runner ②. The runner will check out the source code of edi-ci and the specified edi project configuration (here edi-cl) ③. edi will then take care of building the specified OS artifact (here iot-gate-imx8-bullseye-arm64.yml) from scratch. During this process a lot of Debian packages will get fetched from various Debian repositories ④. Using the Mender REST API the resulting Mender OS artifact will get uploaded to Mender ⑤ and then dispatched to our chosen device ⑥.

After our test device got successfully updated, we run some tests that are based on pytest and Testinfra ⑦.

Testinfra is a pretty powerful pytest plugin and - as an example - the following test makes sure that all systemd services are running properly on our test device:

def test_systemd_overall_status(host):
    cmd ="systemctl is-system-running")
    assert cmd.rc == 0
    assert "running" in cmd.stdout

Another test checks that our root device got properly resized and comes with the expected mount points:

import re
import pytest

def test_root_device(host):
    cmd ="df / --output=pcent")
    assert cmd.rc == 0
    match ="(\d{1,3})%", cmd.stdout)
    assert match
    # if the usage is below 50% then the root device got properly resized
    assert int( < 50

def test_resize_completion(host):
    assert host.file("/etc/edi-resize-rootfs.done").exists

@pytest.mark.parametrize("mountpoint", ["/", "/data", "/boot/firmware", ])
def test_mountpoints(host, mountpoint):
    assert host.mount_point(mountpoint).exists

The result of the whole workflow gets nicely presented on GitHub ⑧:

Workflow Summary

The attentive reader will now ask the legitimate question: Where does the workflow know the various credentials from?

Using GitHub Actions you have the possibility to store encrypted secrets.

I have summarized the required secrets here.


After having completed all this automation, I can now enjoy the flow of the bike trail while my edi OS workflow is running. If something should go wrong, then a GitHub e-mail will make me aware that I have to fix something once I am back home.

The whole stuff presented here should be helpful also for other edi based projects. Apart from GitHub Actions I did not introduce any new technology. Instead, I was re-using already familiar tools (Ansible, Mender, Debian, pytest and edi) and hardware.

It should also be fairly easy to use Jenkins or GitLab instead of GitHub Actions.


Leave a comment