Skip to content

chore(ci): add Justfile and codegen drift check - #7605

Open
pfeerick wants to merge 3 commits into
mainfrom
pfeerick/codegen-drift-ci
Open

chore(ci): add Justfile and codegen drift check#7605
pfeerick wants to merge 3 commits into
mainfrom
pfeerick/codegen-drift-ci

Conversation

@pfeerick

@pfeerick pfeerick commented Aug 1, 2026

Copy link
Copy Markdown
Member

Summary

Wraps the three existing code generators in just recipes — runnable either on the host or in the dev container — and adds a path-gated workflow that regenerates each one in ghcr.io/edgetx/edgetx-dev:latest and warns, without failing, when the committed output has drifted.

None of make_fonts.sh, cfn_sorter.sh or generate-yaml.sh was referenced by CI, so nothing detected when the committed artefacts fell out of sync with their sources. Two of the three were also undocumented (see #7604).

Justfile

$ just --list
Available recipes:
    default                   # Show available recipes

    [codegen]
    cfn-sort                  # Regenerate the custom-function sort order (radio/src/cfn_sort.cpp)
    codegen                   # Regenerate the YAML parsers, LVGL fonts and cfn sort order
    gen-fonts                 # Regenerate the LVGL fonts (radio/src/fonts/lvgl/{std,sml,lrg}/lv_font_*.c)
    gen-yaml FLAVOR=''        # Regenerate the YAML parsers (radio/src/storage/yaml/yaml_datastructs_*.cpp)

    [codegen (docker)]
    docker-cfn-sort           # Regenerate the custom-function sort order in the dev container
    docker-codegen            # Regenerate everything in the dev container
    docker-gen-fonts          # Regenerate the LVGL fonts in the dev container
    docker-gen-yaml FLAVOR='' # Regenerate the YAML parsers in the dev container

The recipes are thin wrappers — each script self-cds and uses relative paths deliberately, to keep absolute paths out of the generated files, so nothing is reimplemented.

Host group needs the tooling installed locally: 19 system locales, lv_font_conv, and a specific libclang. Most contributors won't have that (the locales part is fixed for Ubuntu by #7603).

Docker group runs the same scripts in the dev container, which already has all of it. These invoke the scripts directly rather than just-in-the-container, since the image doesn't ship just. They also run as the invoking user, so generated files aren't left owned by root on Linux hosts.

CI keeps using the plain host recipes, since its jobs already run inside that image.

Workflow

One workflow, three drift jobs, gated per-generator by dorny/paths-filter so a dataconstants.h change doesn't trigger a full font regeneration. Shared logic lives in a composite action under .github/actions/, following the existing convention there.

The drift check never fails the build. This is deliberate — contributors without the local toolchain must not be blocked. Drift is reported as a job annotation plus the diff in the step summary; the job still concludes success. Note a ::warning:: does not produce a neutral check, so the checks tab shows a green tick with the annotation attached.

A Justfile change is handled separately, since it cannot cause drift — the recipes are wrappers, so editing one cannot change what a generator produces. What it can do is rename a recipe out from under the jobs, which pass recipe names to the composite action. So the changes job resolves the three recipes the workflow calls, catching renames and parse errors in well under a second rather than spending ~10 minutes regenerating everything. That check is allowed to fail: the never-fail behaviour covers drift, not a workflow referencing a recipe that no longer exists.

Environment

ghcr.io/edgetx/edgetx-dev:latest already has everything needed — verified against dev/Dockerfile on build-edgetx main: lv_font_conv, node 20, python3, build-essential, and locale-gen of all 19 locales, an exact match for the list in cfn_sorter.sh. The apt block there is literally commented # For font generation and cfn sorting.

Testing

Each recipe run end to end against a clean tree, on the host in the container and again through the docker- variants:

Recipe Time Result
cfn-sort 5.7s 19 languages, byte-identical to committed
gen-fonts 3m22s all 153 fonts byte-identical, no warnings
gen-yaml ~1m yaml_data built, byte-identical

So there is no existing drift. The drift-check logic was exercised for all four cases — clean tree, modified tracked file, untracked generated file, and an oversized diff — each exiting 0. The recipe validation was checked against both a renamed recipe and a malformed Justfile. actionlint is clean and just --fmt --check passes.

Testing the docker variants turned up one real defect worth calling out: docker-gen-yaml failed outright, because FETCHCONTENT_BASE_DIR defaults to ${CMAKE_SOURCE_DIR}/.cache/fetchcontent (CMakeLists.txt#L46) — inside the bind-mounted repo. The container therefore inherits whatever CMake state host builds left there, and macOS-generated googletest subbuild state makes the Linux configure fail. Anyone who had built natively first would have hit it. Fixed by pointing the container at its own .cache/fetchcontent-docker via the env var CMakeLists.txt#L42 already honours — still cached between runs, still gitignored, but isolated from host builds.

Also

Gitignores the intermediates make_fonts.sh and cfn_sorter.sh leave behind if interrupted (lv_font.inc, lz4_font, tools/a.out), which would otherwise show up as drift.

Possible follow-up

Adding just to the edgetx-dev image would let both the workflow and the composite action drop their install step, and collapse the whole docker group into a single generic just docker <recipe> wrapper.

🤖 Generated with Claude Code

Wrap the three existing code generators in `just` recipes, and add a
path-gated workflow that regenerates each in ghcr.io/edgetx/edgetx-dev and
warns - without failing - when the committed output has drifted.

None of make_fonts.sh, cfn_sorter.sh or generate-yaml.sh was referenced by
CI, and the first two were undocumented, so there was no way to discover
that editing radio/src/translations/i18n/*.h requires re-running them, and
nothing detected when the committed artefacts fell out of sync.

The check is deliberately non-failing: contributors without the local
toolchain must not be blocked, so drift is reported as a job annotation
plus a step summary diff.

Also gitignore the intermediates both scripts leave behind when they abort
(lv_font.inc, lz4_font, a.out), which would otherwise show up as drift.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@pfeerick pfeerick added this to the 3.0 milestone Aug 1, 2026
@pfeerick pfeerick added the ci/cd 🔧 Related to GitHub Actions and similar issues label Aug 1, 2026
pfeerick and others added 2 commits August 2, 2026 09:44
The codegen recipes need the tools installed on the host - 19 system
locales, lv_font_conv, and a specific libclang. Most contributors will not
have those, but the dev container already does.

Add a docker- variant of each recipe, in their own just group, that runs
the same script in ghcr.io/edgetx/edgetx-dev. They invoke the scripts
directly rather than just-in-the-container, since the image does not ship
just. CI keeps using the plain recipes, as its jobs already run in that
image.

The container runs as the invoking user so generated files are not left
owned by root on Linux hosts.

docker-gen-yaml also points FETCHCONTENT_BASE_DIR at its own directory.
The default is ${CMAKE_SOURCE_DIR}/.cache/fetchcontent, which is inside the
bind mounted repo, so it would be shared with host builds - and CMake state
generated on a macOS host makes the container build fail when FetchContent
tries to reuse it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A Justfile change cannot cause drift - the recipes are thin wrappers, so
editing one cannot change what the generators produce. It was still listed
in all three per-generator filters, so touching it triggered three full
regenerations, which is around ten minutes of CI. The docker- recipes made
that worse, since the workflow never invokes them at all.

What a Justfile change can do is rename a recipe out from under the jobs,
which pass recipe names to the composite action. Check for that directly
instead: the changes job now resolves the three recipes the workflow calls,
which catches both renames and parse errors in well under a second.

The drift jobs need the changes job, so broken recipes stop them running
rather than failing three times over. That failure is deliberate - the
never-fail behaviour covers drift, not a workflow that references a recipe
which no longer exists.

Also pins the just version once at workflow level, shared by the changes
job and the composite action.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cd 🔧 Related to GitHub Actions and similar issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant