Repository navigation
feat(cli): add ebuild doctor — one-command environment diagnosis - #72
Merged
Merged
Conversation
| import sys | ||
| from dataclasses import dataclass | ||
| from pathlib import Path | ||
| from typing import List, Optional |
|
|
||
| import json | ||
|
|
||
| import pytest |
Comment on lines
+24
to
+34
| from ebuild.system.doctor import ( | ||
| MISSING, | ||
| OK, | ||
| WARN, | ||
| Check, | ||
| exit_code, | ||
| format_report, | ||
| host_checks, | ||
| run_all, | ||
| toolchain_checks, | ||
| ) |
| """ | ||
|
|
||
| import subprocess | ||
| from pathlib import Path |
| "--abbrev-ref", "HEAD"], | ||
| capture_output=True, text=True, timeout=15) | ||
| branch = proc.stdout.strip() | ||
| except (OSError, subprocess.TimeoutExpired): |
The MLP developer walk in the platform design document ends with a build that
says how much of the board it used:
Flash: 384 KB
RAM: 72 KB
Ready to flash.
Nothing produced those numbers. A developer had to run `size` themselves and
remember which columns to add, which is not being told — it is being left to
find out.
$ ebuild build
[ok] Build completed successfully.
Flash: 1.9 KB of 1.00 MB (0.2%)
RAM : 300.6 KB of 192.0 KB (156.6%)
[warn] RAM usage 300.6 KB exceeds the board's 192.0 KB -- this image
will not fit.
The accounting matches scripts/measure_footprint.py in the eos repo so the two
tools cannot disagree about what a number means:
flash = text + data
ram = data + bss
`data` is charged to both because it is stored in flash and copied to RAM at
startup; reading `size`'s "dec" column instead understates RAM.
Capacity comes from the project's own board.yaml `memory.flash_size` /
`ram_size` when it ships one — the convention the descriptions under
hardware/board/ already use — and otherwise from a table of the reference part
for each board family. Boards that boot from removable storage, and
Linux-class parts with no fixed budget, are deliberately absent: a percentage
against a guessed ceiling reads as authoritative, so those report absolute
sizes only.
An over-budget image is a warning, not a build failure. It linked; it will not
fit. The developer needs to hear that now rather than from a board that will
not boot.
A cross build is measured with its own `size` and never falls back to the host
one — host `size` on an ARM ELF reports numbers for a different target and
nothing in the output would say so. Where no suitable tool exists the report
is skipped rather than guessed, and never fails the build.
Also fixes .gitignore, found while committing this: line 8 was a bare `build/`
from the Python-packaging block, which also matched `ebuild/build/` — the
source package holding dispatch.py, ninja_backend.py and toolchain.py. Those
four files predate the rule and stayed tracked, but every new module added
there was silently ignored, including this one. Anchored to `/build/`, which
keeps the setuptools artifact directory ignored and stops the pattern reaching
nested source.
235 tests pass, up from 202; 32 are new here.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The MLP list asks for it, and the absence shows: the same class of problem
currently surfaces three different ways. A missing cross toolchain appears as
a compiler-not-found partway through a build. A missing repo cache appears as
`eos/hal.h: No such file`. A missing binutils silently drops the footprint
report. None of the three names the fix.
$ ebuild doctor
OK python 3.12.14
OK ninja 1.13.2 (/usr/bin/ninja)
OK host compiler 15.2.0 (/usr/bin/cc)
OK arm-none-eabi 14.2 (/usr/bin/arm-none-eabi-gcc)
warn xtensa-esp32-elf not installed — no esp32 builds
OK eos repo ~/.ebuild/repos/eos (master)
No problems. The host build path is ready.
Optional, for other targets:
- install the xtensa-esp32-elf toolchain to target esp32
Three decisions worth naming.
Read-only. It reports; it does not repair. `ebuild setup` fetches the repos,
and installing a toolchain belongs to the developer's package manager —
guessing which one they use is how a diagnostic tool starts doing damage.
The exit code is non-zero only for things that actually stop a build. A
host-only machine legitimately has no cross toolchain, and a doctor that
always exits 1 stops being consulted, which costs more than it saves.
A missing cross toolchain reports the boards it would have unlocked rather
than just its own absence. "arm-none-eabi-gcc not found" is a fact; "no
stm32f4, stm32h7, nrf52, rp2040 or tms570 builds" is the consequence the
developer is actually deciding about.
`--json` emits the same checks for CI, and agrees with the text form on the
exit code.
254 tests pass, up from 235; 19 are new here.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
srpatcha
force-pushed
the
feat/ebuild-doctor
branch
from
August 29, 2026 22:42
290f5e8 to
c3727e6
Compare
This was referenced Aug 29, 2026
| from __future__ import annotations | ||
|
|
||
| import glob | ||
| import glob |
| import glob | ||
| import os | ||
| import re | ||
| import re |
srpatcha
pushed a commit
that referenced
this pull request
Sep 1, 2026
`import ebuild.cli.commands` raises NameError on master, so the `ebuild` command does not start and pytest cannot collect nine test modules -- the suite reports "9 errors during collection" and runs zero tests. Four PRs landed their tests while their implementation was discarded during conflict resolution, and the merges that did it were not gated by a run: f5209ac, current master, has no CI runs at all. #64 (6a22e36) tests/unit/test_golden_path_commands.py landed; _workspace_repo_paths, _cached_repo_libraries, _record_board_selection, _EOS_PROJECT_CONFIG and the `configure --board` option did not. #89 (32348f0) commands.py gained five `re.` uses, no `import re`. #90 (d3958f7) the initramfs test kept its assertions but lost its `_newc_members` helper and its `os` / `stat` imports. #92 registry.version_key was renamed version_sort_key without updating its caller in the tests. Restoring them, taken verbatim from the branches that were merged: * commands.py: `import re`; `_workspace_repo_paths`, `_cached_repo_libraries`, `_record_board_selection`, `_EOS_PROJECT_CONFIG`; the `--board` option on `configure` and the call that persists it; and the `{**_workspace_repo_paths(), **...}` merge at both `_install_packages` call sites. Without that last hunk a scaffolded project that `use`s eos or eboot gets no include or link paths from ~/.ebuild/repos, which is the "fatal error: eos/hal.h: No such file or directory" that #64 fixed. Without the option, the documented golden path -- `configure --board` then a bare `build` -- fails with "no such option: --board". * the initramfs test: `os` / `stat` imports and the `_newc_members` newc parser the assertions call. * test_package_registry.py: import the name the module now exports. Collection goes from 9 errors / 0 tests to 446 passing. The 12 that still fail are pre-existing and out of scope here: `doctor` (#72) and `package` (#77) are two more features whose implementations the same merges dropped and which should be restored by their authors, and the rest are separate bugs -- see the PR description. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This was referenced Sep 1, 2026
srpatcha
pushed a commit
that referenced
this pull request
Sep 1, 2026
…green The six remaining failures were all the same shape as the ones already fixed: work that landed as tests while its implementation was discarded during conflict resolution, or two implementations of one thing left side by side with the wrong one wired in. * backend precedence (#96). The 22 lines that stop detect_backend() outranking a build.yaml that declares targets were gone; the test file survived. A Makefile kept for `make flash` still won, the dispatcher ran the external tool, nothing declared was built, and the build reported success. Restored from bba6f75. * dispatch errors. Three mechanisms sat on top of each other: BackendError + _validate_backend, and UnknownBackendError + a _unknown_backend() -- twice, the class and the function each defined two ways, the later shadowing the earlier. _validate_backend ran first and preempted the per-step error with a wrong type and a message that contradicted itself by listing ninja as supported while rejecting it. Consolidated on the surviving pair; BackendError stays as an alias so callers that catch it keep working. The guard moved ahead of the mkdir it used to precede, so rejecting a backend no longer leaves a build directory behind. * registry ordering. registry.py carried both version_sort_key and a private _version_sort_key, and every caller used the private one, which ranks any non-numeric version below every numeric one -- so v2.9.3, littlefs's own tag format, sorted below 1.10.0 and get() picked the wrong latest. Dropped the duplicate; the callers now use the tested public key. One assertion in tests/ebuild encoded the private key's ordering and is updated, which is the one judgement call here. * system_config. Parsed and validated in load_config(), then never passed to ProjectConfig, so the whole [system] section was inert. * footprint on macOS. Apple size(1) prints "__TEXT __DATA __OBJC others", and _SIZE_LINE matched that row too -- reading __OBJC, always 0, as bss. Every footprint measured on a Mac silently reported RAM containing no zero-initialised data. Falls back to `size -m` and sums sections, so bss is the real 4096 rather than 0. * doctor (#72) and package (#77). Both commands were absent from the group; ebuild/system/doctor.py and ebuild/build/firmware_image.py were already on master, only the CLI registration was lost. Restoring `package` surfaced a genuine collision: integration.py defines another `package`, and register_commands() runs last, so it had been silently replacing the .efw one. Renamed that to `package-deliverable` -- see the PR description, this one wants a maintainer's eye. Also adds a missing `import glob`: _serial_ports() raised NameError on every call, the same defect as the missing `import re`. 525 passed, 0 failed. mypy goes from 23 errors to 15; ruff is unchanged. Verified end to end: `ebuild doctor` reports the environment, and `ebuild build` links a running Mach-O executable and prints a footprint. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
The MLP list asks for "one-command environment diagnosis", and the absence shows: the same class of problem currently surfaces three different ways.
eos/hal.h: No such fileNone of the three names the fix.
Three decisions worth reviewing
Read-only. It reports; it does not repair.
ebuild setupfetches the repos, and installing a toolchain belongs to the developer's package manager — guessing which one they use is how a diagnostic tool starts doing damage.Exit code is non-zero only for things that actually stop a build. A host-only machine legitimately has no cross toolchain. A doctor that always exits 1 stops being consulted, which costs more than it saves. Missing
ninja→ 1. Missingarm-none-eabi-gcc→ 0.A missing toolchain reports the boards it would have unlocked, not just its own absence.
arm-none-eabi-gcc not foundis a fact; "no stm32f4, stm32h7, nrf52, rp2040 or tms570 builds" is the consequence the developer is deciding about.--jsonemits the same checks for CI and agrees with the text form on the exit code.Verification
pytest: 255 passed, 19 new here. Tests cover the exit-code contract in both directions, the report's problem/advisory split, column alignment, and both output modes agreeing.Branches off #71 (footprint), which branches off #70 (the
masterrepair) —mastercannot currently run its own suite.🤖 Generated with Claude Code