Label the ESP-IDF version on both ESP images - #28
Merged
Conversation
Closes #26. A consumer that pins these images by digest - the only pin nothing can move underneath it - deliberately does not keep the tag, so the tag is the one place it cannot read the version from. Until now that left no cheap way to check which ESP-IDF an image carries: the answer existed only inside it, behind a 3.6-7.5 GB pull. The Matter half already had the registry-readable half of this (org.opencontainers.image.revision carries ESP_MATTER_REF); the IDF half had nothing, since org.opencontainers.image.version there is 24.04, inherited from the ubuntu base. Both images now carry `dev.jethome.idf.version`, in the same family the host image uses. esp-idf has the value already - IDF_BASE_TAG, which names its own base - and only needs it re-declared after FROM, or the LABEL would expand to the empty string without Docker objecting. esp-matter takes a new IDF_VERSION arg. Not because the label needs it - Docker inherits a base image's LABELs, so building on this repository's esp-idf would carry the label down by itself - but because inheritance cannot *check* the value, and carries nothing at all when BASE_IMAGE points elsewhere. CI passes that base as a digest, and a digest names no version, so the value comes from images/versions.json, taken from the same `base_tag` the digest was resolved from. That is the same value written twice, which is safe because the checker refuses a variant built with a version its own tag does not name: setting IDF_VERSION to v5.5.5 on the variant tagged idf-v5.4.1-matter-v1.4.2 fails ./scripts/check-versions.sh, verified. **The label is asserted, not declared.** Both verification layers already ran `idf.py --version` and printed it; both now compare it for equality against the labelled version, so an image whose label contradicts its contents fails to build rather than shipping. Equality rather than a substring, because `*v5.4.1*` also matches v5.4.11 - and with the expected value in the failure message, since a build arg is not otherwise visible in the log. esp-idf asserts only when IDF_BASE_TAG *is* a version. Its README offers `latest` and minor-line tags like `v5.3` as valid bases, and an alias never equals the release it resolves to, so a strict comparison would have failed builds that are perfectly valid. The skip is announced rather than silent, and applies locally only: CI always passes an exact version, so every published image is asserted. The docs get the same treatment as the code. The registry-side recipe needs the platform key - `{{ index (index .Image "linux/amd64").Config.Labels … }}` - because these tags are multi-arch indexes and `.Image.Config` on an index is a template error, not an answer; both forms were run against the published images. The `docker image inspect` variant is marked as reading the local daemon rather than the registry. And every custom-build recipe that overrides BASE_IMAGE now passes IDF_VERSION beside it, in the image README and in CLAUDE.md, or the build asserts the default version and fails after the expensive SDK install. Note on what was NOT verified locally: `espressif/idf:v5.5.5` would not pull here (Surge intercepts traffic in TUN mode and drops the connection around 400 MB, while that image carries a single 3.4 GB layer), so neither ESP image was built on this machine. The exact output shape - one line, `ESP-IDF v<version>` - was read out of the master run's logs for four combinations: both IDF versions, both images, both architectures. The assertion's own logic was exercised in a plain container against a match, a mismatch, an alias and an empty answer. The build itself is checked by the pull request, which builds both images on both architectures.
There was a problem hiding this comment.
Pull request overview
This PR adds a registry-readable label for the ESP-IDF version to both ESP images (esp-idf and esp-matter), and strengthens verification so the label is asserted against idf.py --version during the build. This fits the repo’s goal of publishing Docker images with metadata that consumers can validate without pulling multi-GB layers.
Changes:
- Add
dev.jethome.idf.versionlabel toimages/esp-idf/Dockerfileandimages/esp-matter/Dockerfile. - Pass
IDF_VERSIONfor esp-matter builds viaimages/versions.json, and assert it matches the SDK-reported version. - Document how to read the label from the registry (and locally) in both image READMEs, and update
CLAUDE.mdguidance.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| images/versions.json | Adds IDF_VERSION to esp-matter build args so CI can label/assert even when the base is referenced by digest. |
| images/esp-matter/README.md | Documents reading the IDF version label and updates custom-build instructions to include IDF_VERSION. |
| images/esp-matter/Dockerfile | Introduces IDF_VERSION ARG, labels it, and asserts idf.py --version matches. |
| images/esp-idf/README.md | Documents how to read dev.jethome.idf.version from the registry and locally, plus assertion semantics. |
| images/esp-idf/Dockerfile | Adds the IDF version label and asserts it matches idf.py --version (with alias-skip behavior). |
| CLAUDE.md | Updates repo-wide CI/build documentation to reflect the new IDF_VERSION flow for esp-matter. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
… docs Copilot's three points on #28. The `case` glob `v[0-9]*.[0-9]*.[0-9]*` was not the test its own comment described: `*` swallows anything, so `v5.5.5-rc1` and `v5.5.5-dirty` were taken for exact versions and asserted against - the opposite of what that branch exists for. An anchored `[[ =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]` says it exactly; bash is already this image's SHELL. Verified against v5.5.5, v5.5.5-rc1, v5.5.5-dirty, v5.x.y, v5.3, latest and v5.10.12. The esp-matter custom-build recipe used the same `v<version>` placeholder for IDF_VERSION and ESP_MATTER_VERSION, which invites copying one value into both; they are now `v<idf-version>` and `v<matter-version>`, and the base tag matches. CLAUDE.md's local recipe said `IDF_VERSION=<version>` with no leading `v`, which would have produced a confusing assertion failure - it now shows the form the SDK reports.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #26.
A consumer that pins these images by digest — the only pin nothing can move
underneath it — deliberately does not keep the tag, so the tag is the one place it
cannot read the ESP-IDF version from. Until now the answer lived only inside the
image, behind a 3.6–7.5 GB pull. The Matter half already had a registry-readable
pin (
org.opencontainers.image.revisioncarriesESP_MATTER_REF); the IDF halfhad nothing, since
org.opencontainers.image.versionthere is24.04, inheritedfrom the ubuntu base.
Both images now carry
dev.jethome.idf.version, in the samedev.jethome.*family the host image uses.
Where the value comes from
esp-idf already has it —
IDF_BASE_TAG, which names its own base. It onlyneeded re-declaring after
FROM, or theLABELwould have expanded to the emptystring without Docker objecting.
esp-matter takes a new
IDF_VERSIONarg. Not because the label needs it —Docker inherits a base image's LABELs, so building on this repository's esp-idf
carries the label down by itself. What inheritance cannot do is check the value,
and it carries nothing at all when
BASE_IMAGEpoints somewhere else. CI passesthat base as a digest, and a digest names no version, so the value comes from
images/versions.json, taken from the samebase_tagthe digest was resolvedfrom.
That is the same value written twice, which is safe because the checker already
refuses a variant built with a version its own tag does not name — setting
IDF_VERSIONtov5.5.5on the variant taggedidf-v5.4.1-matter-v1.4.2fails./scripts/check-versions.sh, verified.The label is asserted, not declared
Both verification layers already ran
idf.py --versionand printed it. Both nowcompare it for equality against the labelled version, so an image whose label
contradicts its contents fails to build rather than shipping. Equality rather than
a substring, because
*v5.4.1*also matchesv5.4.11— and the failure messagenames both the expected and the reported value, since a build arg is not otherwise
visible in the log.
esp-idf asserts only when
IDF_BASE_TAGis a version. Its README offerslatestand minor-line tags such asv5.3as valid bases for a custom build, andan alias never equals the release it resolves to, so an unconditional comparison
would fail builds that are perfectly valid. The skip is announced in the log
rather than silent, and it applies locally only: CI always passes an exact version
from
images/versions.json, so every published image is asserted. The label thenrepeats whatever was passed — which means a label shaped like an exact version is
exactly the one that was checked.
Documentation held to the same bar
{{ index (index .Image "linux/amd64").Config.Labels … }}— because these tagsare multi-arch indexes, and
.Image.Configon an index is a template errorrather than an answer. Both forms were run against the published images.
docker image inspectvariant is marked as reading the local daemon, notthe registry.
BASE_IMAGEnow passesIDF_VERSIONbeside it — in the image README and in
CLAUDE.md— or the build asserts thedefault version and fails after the expensive SDK install.
What was not verified locally
espressif/idf:v5.5.5would not pull on the machine this was written on, soneither ESP image was built here. What that leaves, and how it was covered:
ESP-IDF v<version>) was read from the masterrun's logs for four combinations — both IDF versions × both images × both
architectures;
mismatch, an alias and an empty answer;
./scripts/lint.sh(hadolint included),./scripts/check-versions.sh, thematrix generator and an offline link check are green.
This pull request is the real check: it builds both images on both
architectures. If an assertion is wrong, it fails in the verification layer, which
prints the expected and the reported value side by side.
Note on the checks
images/versions.jsonis in every image workflow'spaths:filter, so this alsorebuilds host and platformio. That is the documented trade of keeping the version
data in one file.