Job Images
Every job runs in a container, and this page is about that container’s image. For what a DUT boots, see Test Images.
Without image, a workflow’s jobs run in the FirmwareCI default image. Name your own image to have your tools ready when the job starts, instead of installing them in every job.
Name your image
Set image in workflow.yaml. A public image only needs its reference:
A private image also needs credentials:
| Attribute | Description |
|---|---|
| ref | Image reference. A short name such as alpine:3.20 means Docker Hub, and without a tag it means latest. To always run the same image, add a digest: ghcr.io/acme/env:1.2@sha256: and 64 hex characters. A tag next to a digest only informs the reader. |
| username | Registry user, written out or as one [[secrets.NAME]]. Needs password. |
| password | Registry password or token. Free text, and each [[secrets.NAME]] in it is replaced with that project secret. Needs username. |
Warning: Text written out in
passwordends up in your repository, and FirmwareCI stores it as it stands. Use a secret.
Note: Credentials go to the host that
refnames, so anyone who can push to the repository can send them to a host of their choice.
What the image has to contain
- It has to be built for
linux/amd64, and its compressed layers can take at most 10 GB. - It has to contain the tools your test steps call, see What test steps need. It needs nothing for FirmwareCI itself, so a plain
alpineordebianworks. - FirmwareCI ignores the image’s entrypoint, user and working directory. A job runs as root and starts in
/. - FirmwareCI copies its runner to
/fwci/contestand its SSH key to/root/.ssh/fwciand/root/.ssh/fwci.pub, replacing any files at those paths.
What the default image contains
The default image is based on Ubuntu 24.04 and has these tools. The list can change with a FirmwareCI release.
| Purpose | Tools |
|---|---|
| Emulation | qemu-system-x86_64, qemu-system-aarch64, qemu-system-arm, qemu-system-arm-openBMC, socat |
| BMC | ipmitool, rf_service_validator, rf_interop_validator, rf_protocol_validator |
| Firmware analysis | rizin, fwhunt-scan with its rules, sbctl |
| Test frameworks | robot, task |
| Scripting | python3, pip, jq, bc, make, git, curl, sshpass, xz |
| Network | ip, ifconfig |
Build on the default image
Build on the default image when your tests need its tools plus a few of your own. It’s published as ghcr.io/blindspotsoftware/firmwareci-env, and pulling it needs no login.
Start your Dockerfile from a version of it, then add what your tests need:
Build it for linux/amd64 and push it to a registry your jobs can reach:
Name it in workflow.yaml with image: ghcr.io/acme/env:1.2. A private registry also needs credentials. A step can then call your tools by name:
Keep these in mind:
- Versions: The package page lists the versions. A version tag keeps pointing at the same image, so your image stays on the version you named, also when a FirmwareCI release moves the default image to a newer one.
- Python packages: Ubuntu 24.04 refuses a plain
pip installwithexternally-managed-environment, so pass--break-system-packages. - Environment:
ENVreaches your steps, including changes toPATH. - What you can leave out:
ENTRYPOINT,USERandWORKDIRhave no effect on a job, and FirmwareCI copies in its runner and SSH key, see What the image has to contain. - DUT stages: A DUT’s pre and post stages run in this image too, so it has to carry what they need. Building on the default image keeps its tools available to them.
What test steps need
A step with local transport runs in the job’s image, and a step with ssh transport runs on the machine it connects to. Tools a step runs on the DUT belong on the DUT, see Test Images.
These steps need something in the job’s image:
| Step | What the image needs |
|---|---|
| Qemu | The binary named in executable, and socat |
IPMI with local transport | ipmitool. sol also needs sh, GNU timeout, mktemp and grep |
Redfish with local transport | rf_service_validator, rf_interop_validator or rf_protocol_validator for the service, interop and protocol engines |
| FWHunt | python3, rizin, fwhunt-scan and its Python requirements in /root/default-tools/fwhunt-scan, and the FwHunt rules in FwHunt-rules inside that directory |
Robot Test Framework with local transport | Robot Framework at /usr/local/bin/robot |
| SecureBoot Management | sbctl at /root/default-tools/sbctl/sbctl, which the step copies to the DUT |
| Binarly | CA certificates, since the step connects over HTTPS |
cmd and shell with local transport | What your command runs. shell also needs sh |
When the image is checked
When you push, FirmwareCI checks a new or changed image against its registry, using the project’s credentials. An unchanged image isn’t checked again, so a push that only edits a test doesn’t depend on the registry.
| Result of the check | Effect on the push |
|---|---|
| A secret the project doesn’t have | fails |
| The registry doesn’t have the image, tag or digest | fails |
| The registry refuses the credentials | fails |
No linux/amd64, or more than 10 GB | fails |
| The registry doesn’t answer, or its address isn’t public | warning, the image isn’t checked |
fwci validate only checks the format and doesn’t contact the registry.
When a job runs
A job keeps the reference its workflow named when the job was created, and a rerun uses that same reference. With a digest, it always runs that image. With only a tag, it runs what the tag points at when the job starts, so a rerun can run a newer image than the first run did. The job’s Pull Docker image preparation step shows the exact image it ran, with its digest.
Before each job, FirmwareCI asks the registry with the project’s current credentials and pulls only the layers it doesn’t have. If the registry refuses access or doesn’t have the image, the job fails at once. If it doesn’t answer, the job is tried once more before it fails.
A daily cleanup removes images that no running job uses, and the next job pulls the image again.