Skip to content

config: reject a non-list packages section instead of dropping it - #73

Open
JoaoMorais03 wants to merge 1 commit into
embeddedos-org:masterfrom
JoaoMorais03:fix/packages-section-must-be-list
Open

config: reject a non-list packages section instead of dropping it#73
JoaoMorais03 wants to merge 1 commit into
embeddedos-org:masterfrom
JoaoMorais03:fix/packages-section-must-be-list

Conversation

@JoaoMorais03

Copy link
Copy Markdown

Contribution after reviewing the project: load_config() already rejects a non-list targets: / toolchain / backend_config, but a packages: that is a mapping or scalar was dropped with no error. A common YAML mistake (packages: {name: zlib, version: "1.2.13"} instead of a list) therefore produced a project with zero package deps.

Issue

ebuild/core/config.py treated packages: as optional and only walked it when isinstance(raw_packages, list). Anything else — including a mapping, which is what you get if the - is omitted — was silently ignored. Individual list items that were not mappings, and mappings without name, were also skipped.

Approach

If packages is present, require a list of mappings with a name field (same shape as targets). Non-string YAML versions (e.g. version: 1.0 parsed as a float) are coerced to str so PackageDep.version stays a string. Omitted packages still means no dependencies.

Testing

Added cases in tests/ebuild/test_config_validation.py:

  • mapping / string / int packages: raises ConfigError
  • list items that are not mappings raise
  • a mapping without name raises
  • a valid two-package list still parses
  • omitted packages is empty

Validated by reading the parser and matching the existing target/toolchain tests. I did not run the full pytest suite in this environment (no local checkout of the repo).

Limitations

Does not invent a shorthand packages: [zlib] form. Does not validate that version is a useful version string, only that it can be stored as str. Duplicate package names are still allowed.

A mapping or scalar under packages: was silently ignored, so a common YAML
mistake produced a build with no package dependencies and no error.

Signed-off-by: João Morais <118842104+JoaoMorais03@users.noreply.github.com>
Copilot AI lite review requested due to automatic review settings August 29, 2026 16:26

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

packages[].name is not validated as a string (can propagate non-strings into resolver/CLI), and the new version-to-string coercion behavior lacks a direct regression test.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Tightens build.yaml config validation so a present-but-invalid packages: section fails fast instead of being silently ignored, preventing accidental “no dependencies” projects due to common YAML shape mistakes.

Changes:

  • Require packages (when present) to be a list of mappings and error on invalid shapes/items.
  • Enforce name presence and coerce non-string version scalars to str during parsing.
  • Add config validation tests covering invalid packages shapes and valid parsing/omission behavior.
File summaries
File Description
ebuild/core/config.py Adds strict validation for packages: entries and coerces non-string versions to strings.
tests/ebuild/test_config_validation.py Adds regression tests for invalid packages: shapes/items and successful parsing/omission.
Review details
  • Files reviewed: 2/2 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread ebuild/core/config.py
Comment on lines +296 to +302
pkg_name = pkg.get("name", "")
pkg_version = pkg.get("version")
if not pkg_name:
raise ConfigError("Package definition must have a 'name' field.")
if pkg_version is not None and not isinstance(pkg_version, str):
pkg_version = str(pkg_version)
packages.append(PackageDep(name=pkg_name, version=pkg_version))
Comment on lines +161 to +164
def test_omitted_packages_is_empty(tmp_path):
path = write_config(tmp_path, {"project": {"name": "demo"}})
config = load_config(path)
assert config.packages == []

@srpatcha srpatcha left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified — and it is the same failure shape as several others in this repo

The old code:

if isinstance(raw_packages, list):
    for p in raw_packages:
        if isinstance(p, dict):
            ...

Two silent skips, no else on either. A packages: section written as a mapping instead of a list, or a list containing a bare string, parsed to zero dependencies and the build went on to succeed without them. The developer gets a binary missing everything they asked for and nothing anywhere says why.

That is the same shape as the MISS case I have been adding to the build summary, as ebuild add writing entries that cannot resolve, and as the ecosystem runner reporting passes for tests that never ran. Raising ConfigError at the point of the malformed input is the right treatment: the message names the field, the expected shape, and what was found.

raise ConfigError(
    "Invalid package definition: expected a YAML mapping, "
    f"got {type(pkg).__name__}."
)

Requiring name is right too — a package entry without one is not a package, and previously it was dropped without comment.

One thing I would consider, not blocking

if pkg_version is not None and not isinstance(pkg_version, str):
    pkg_version = str(pkg_version)

This is coercion where the rest of the change is validation, and it is load-bearing: YAML parses version: 1.2 as a float, and str(1.2) is "1.2" — but version: 1.20 also parses as a float and stringifies to "1.2", silently losing the trailing zero. Worth either rejecting a non-string version and telling the author to quote it, or keeping the coercion with a comment about which YAML scalars it is absorbing. Either is defensible; the silent narrowing is the part I would not leave undocumented.

Merge order

Currently red on master for a reason unrelated to this PR — origin/master has a SyntaxError in ebuild/build/dispatch.py that makes the suite uncollectable. Merged on top of #66, which repairs it: 215 passed. Needs #66 first, then this goes in as-is.

Verification

Merged onto origin/master + #66; pytest 215 passed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants