Plan unit tests as a required gate, then matrix-build WROOM and JC4827W543 images with published flash artifacts.
9.5 KiB
CI/CD — Gitea Actions
Status: planned on refactor/esp-idf-modular-ui. Implementation follows the IDF skeleton
(migration step 2 in docs/TARGET_ARCHITECTURE.md). Workflows live in .gitea/workflows/
(Gitea Actions, GitHub-compatible YAML). PRs target develop.
Purpose
Every change that lands on develop must:
- Run host unit tests for recipe tables and the machine state machine (no hardware).
- Build two firmware images:
board_wroom(ESP32 classic) andboard_jc4827w543(ESP32-S3). - Publish flashable artifacts (app binary, bootloader, partition table, merged image, size report).
Legacy PlatformIO (platformio.ini) is not a CI target.
Runner requirements
Gitea act_runner must be registered against git.i3omb.com with labels that match runs-on.
Workflow runs-on |
Typical runner label | Notes |
|---|---|---|
ubuntu-latest |
ubuntu-latest:docker://gitea/runner-images:ubuntu-latest |
Host tests, checkout, artifact upload |
(job container) |
Docker socket available on the runner | Firmware jobs use espressif/idf:<tag> as the job container |
Firmware builds need:
- Docker on the runner (job-level
container:) - Enough disk for the IDF image (~several GB) and two
build/trees - Outbound pull of
docker.io/espressif/idf(or a mirror on the Gitea registry)
If the instance cannot pull Docker Hub, mirror espressif/idf:v5.4 into the Gitea container registry and point container.image at that.
Do not depend on espressif/esp-idf-ci-action or other GitHub-hosted actions for the compile step. Those often assume GitHub path layout. Drive idf.py inside espressif/idf instead. actions/checkout and actions/upload-artifact (or Gitea’s equivalents) are fine.
Triggers
| Event | What runs |
|---|---|
pull_request into develop |
tests + both firmware builds (required check) |
push to develop |
tests + both firmware builds; keep artifacts |
push to refactor/** and feature/** |
same, so the branch is green before the PR |
push tags v* |
tests + builds + attach binaries to the Gitea release |
workflow_dispatch |
manual rebuild |
main is not a CI integration branch for this refactor.
Jobs
test (host) ──► firmware [wroom / jc4827w543] ──► (tag only) release
Firmware must needs: test. A failing recipe or state-machine test must not produce a binary.
1. test — host unit tests
Runs on ubuntu-latest without the IDF container so the gate is fast.
- Toolchain: CMake + GCC (C11), CTest.
- Tree:
tests/host/linkingcomponents/app_processandcomponents/app_machineagainst stub HAL (tests/host/stubs/: motor, temp, audio, display no-ops; queues replaced with a tiny fake or a vendored FreeRTOS POSIX port if the machine task is exercised). - Framework: Unity (can be vendored) or plain
assert+ CTest. Prefer Unity so on-target tests later share the same style. - Must cover:
find/ lookup of all six process names- frozen default times, temps, CW/CCW for every step (values from
docs/CURRENT_STATE.md) - session ±5 s overlay does not mutate the const table
- state machine: Idle → Select → Arm → Start → Stop (EN disabled) → Resume → Complete → Return
Stopis accepted inStepRunningand during a complete-alarm
- Recipe-value tests fail the build if defaults change without an explicit doc update.
Command sketch:
cmake -S tests/host -B build/host
cmake --build build/host
ctest --test-dir build/host --output-on-failure
Until tests/host exists, the workflow file is not merged as a required check — or the job is if’d on path existence. Do not ship a red pipeline on docs-only commits after the workflow is enabled; use:
- name: Host tests
if: hashFiles('tests/host/CMakeLists.txt') != ''
2. firmware — matrix build
Job container: espressif/idf:v5.4 (pin the tag; bump in one place when IDF is upgraded).
Matrix:
| Name | IDF_TARGET |
Board cmake/sdkconfig |
|---|---|---|
wroom |
esp32 |
-DAUTOFILM_BOARD=wroom + sdkconfig.defaults + sdkconfig.wroom |
jc4827w543 |
esp32s3 |
-DAUTOFILM_BOARD=jc4827w543 + sdkconfig.defaults + sdkconfig.s3 |
Steps:
- Checkout (full history not required).
source /opt/esp/idf/export.sh(already true in the official image entrypoint; useidf.pydirectly).idf.py set-target ${IDF_TARGET}then
idf.py @sdkconfig.defaults @sdkconfig.<board> -DAUTOFILM_BOARD=<board> buildidf.py size(fail the job only if the binary will not fit flash; log the report either way).- Merge a single flash image:
esptool.py --chip ${IDF_TARGET} merge_bin -o autofilm-${board}-${GIT_SHA}.bin @build/flash_args - Upload artifacts (see below).
S3 build must enable octal PSRAM in sdkconfig.s3. WROOM must not.
Arduino-as-component is pulled by the IDF CMake on the WROOM image first; the S3 image may share it until debt burn-down. Cache managed_components/ by lock hash when a idf_component.yml exists.
3. release (tags only)
needs: [test, firmware]. Download both artifact sets and attach to the Gitea release for tag v*:
autofilm-wroom-<version>.bin(merged)autofilm-jc4827w543-<version>.bin(merged)- raw
app.bin/bootloader.bin/partition-table.binper board sdkconfigsnapshot per boardsize.txtper board
Version string is the git tag. CI also stamps version in firmware via -DAUTOFILM_GIT_DESC=$(git describe --always --dirty).
Artifacts
| File | Why |
|---|---|
autofilm-<board>-<sha>.bin |
Merged image for esptool write_flash 0x0 |
build/autofilm.bin (app) |
OTA / partial flash |
build/bootloader/bootloader.bin |
First-time flash |
build/partition_table/partition-table.bin |
First-time flash |
build/flasher_args.json |
Exact offsets |
size.txt / size.json |
Flash/RAM budget |
sdkconfig |
Repro the build flags |
Retention: 14 days on branch builds, keep release attachments. Artifact names must include board + short SHA so WROOM and S3 cannot overwrite each other.
Example workflow (target shape)
Path: .gitea/workflows/ci.yml
name: ci
on:
pull_request:
branches: [develop]
push:
branches: [develop, "refactor/**", "feature/**"]
tags: ["v*"]
workflow_dispatch:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Host unit tests
if: hashFiles('tests/host/CMakeLists.txt') != ''
run: |
cmake -S tests/host -B build/host
cmake --build build/host
ctest --test-dir build/host --output-on-failure
firmware:
needs: test
runs-on: ubuntu-latest
container:
image: docker.io/espressif/idf:v5.4
strategy:
fail-fast: false
matrix:
include:
- board: wroom
target: esp32
sdkconfig: sdkconfig.wroom
- board: jc4827w543
target: esp32s3
sdkconfig: sdkconfig.s3
steps:
- uses: actions/checkout@v4
- name: Build
if: hashFiles('CMakeLists.txt') != ''
run: |
git config --global --add safe.directory '*'
idf.py -B build/${{ matrix.board }} set-target ${{ matrix.target }}
idf.py -B build/${{ matrix.board }} \
-D SDKCONFIG_DEFAULTS="sdkconfig.defaults;${{ matrix.sdkconfig }}" \
-D AUTOFILM_BOARD=${{ matrix.board }} \
build
idf.py -B build/${{ matrix.board }} size | tee size-${{ matrix.board }}.txt
python $IDF_PATH/components/esptool_py/esptool/esptool.py \
--chip ${{ matrix.target }} merge_bin \
-o autofilm-${{ matrix.board }}-${{ gitea.sha }}.bin \
@build/${{ matrix.board }}/flash_args
- uses: actions/upload-artifact@v3
if: hashFiles('CMakeLists.txt') != ''
with:
name: firmware-${{ matrix.board }}
path: |
autofilm-${{ matrix.board }}-*.bin
build/${{ matrix.board }}/*.bin
build/${{ matrix.board }}/bootloader/bootloader.bin
build/${{ matrix.board }}/partition_table/partition-table.bin
build/${{ matrix.board }}/flasher_args.json
build/${{ matrix.board }}/sdkconfig
size-${{ matrix.board }}.txt
Context substitutions: Gitea accepts both github.* and gitea.* in many versions; prefer gitea.sha / gitea.ref and fall back if the runner is older.
hashFiles guards keep docs-only stages of this branch from going red before the IDF tree exists.
Required status on PRs
Once the IDF skeleton compiles, mark on the develop branch protection (Gitea repo settings):
testfirmware (wroom)firmware (jc4827w543)
No merge to develop with a red firmware matrix.
Caching (optional, second pass)
- Cache
~/.cache/pipand IDF tools if not using the pre-bakedespressif/idfimage. - Cache
managed_components/keyed onidf_component.yml+idf.lock. - Do not cache
build/across boards or targets.
Secrets
CI must not need device tokens or Wi-Fi credentials. Firmware builds are offline-configurable. If a later job publishes to a hardware farm, use Gitea secrets — never commit them.
Local equivalent
cmake -S tests/host -B build/host && cmake --build build/host && ctest --test-dir build/host --output-on-failure
docker run --rm -v "$PWD":/project -w /project espressif/idf:v5.4 \
bash -lc 'idf.py -B build/wroom set-target esp32 && idf.py -B build/wroom -D AUTOFILM_BOARD=wroom build'
Agents must run the host tests before pushing once tests/host exists.