Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 19 additions & 12 deletions ebuild/core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,18 +281,25 @@ def load_config(config_path: str | Path) -> ProjectConfig:

# --- packages section (optional, Phase 2) ---
packages: List[PackageDep] = []
raw_packages = raw.get("packages", [])

if isinstance(raw_packages, list):
for p in raw_packages:
if isinstance(p, dict):
pkg_name = p.get("name", "")
pkg_version = p.get("version")

if pkg_name:
packages.append(
PackageDep(name=pkg_name, version=pkg_version)
)
if "packages" in raw:
raw_packages = raw["packages"]
if not isinstance(raw_packages, list):
raise ConfigError(
"'packages' must be a list of package definitions."
)
for pkg in raw_packages:
if not isinstance(pkg, dict):
raise ConfigError(
"Invalid package definition: expected a YAML mapping, "
f"got {type(pkg).__name__}."
)
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 +296 to +302

return ProjectConfig(
name=project_name,
Expand Down
66 changes: 66 additions & 0 deletions tests/ebuild/test_config_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,69 @@ def test_toolchain_mapping_is_parsed(tmp_path):
assert config.toolchain.sysroot == "/opt/arm-none-eabi"
assert config.toolchain.extra_cflags == ["-mcpu=cortex-m4"]
assert config.toolchain.extra_ldflags == ["--specs=nosys.specs"]


@pytest.mark.parametrize("invalid_packages", [
{"name": "zlib", "version": "1.2.13"},
"zlib",
42,
])
def test_packages_must_be_a_list(tmp_path, invalid_packages):
path = write_config(
tmp_path,
{"project": {"name": "demo"}, "packages": invalid_packages},
)

with pytest.raises(ConfigError, match="'packages' must be a list"):
load_config(path)


@pytest.mark.parametrize("invalid_item", ["zlib", 42, None])
def test_package_definition_must_be_mapping(tmp_path, invalid_item):
path = write_config(
tmp_path,
{"project": {"name": "demo"}, "packages": [invalid_item]},
)

with pytest.raises(ConfigError, match="expected a YAML mapping"):
load_config(path)


def test_package_definition_requires_name(tmp_path):
path = write_config(
tmp_path,
{
"project": {"name": "demo"},
"packages": [{"version": "1.2.13"}],
},
)

with pytest.raises(ConfigError, match="must have a 'name'"):
load_config(path)


def test_packages_list_is_parsed(tmp_path):
path = write_config(
tmp_path,
{
"project": {"name": "demo"},
"packages": [
{"name": "zlib", "version": "1.2.13"},
{"name": "mbedtls"},
],
},
)

config = load_config(path)

assert len(config.packages) == 2
assert config.packages[0].name == "zlib"
assert config.packages[0].version == "1.2.13"
assert config.packages[1].name == "mbedtls"
assert config.packages[1].version is None


def test_omitted_packages_is_empty(tmp_path):
path = write_config(tmp_path, {"project": {"name": "demo"}})
config = load_config(path)
assert config.packages == []
Comment on lines +161 to +164