From 325b2111d6a5a16658943ac6f7250f76dace45fd Mon Sep 17 00:00:00 2001 From: Sheng Yu Date: Tue, 4 Jul 2023 15:52:27 -0400 Subject: [PATCH] config: allow bundle without metadata.yaml --- charmcraft/models/charmcraft.py | 45 +++++++++++++++++++-------------- tests/commands/test_build.py | 6 ++--- tests/test_models.py | 8 +++--- 3 files changed, 32 insertions(+), 27 deletions(-) diff --git a/charmcraft/models/charmcraft.py b/charmcraft/models/charmcraft.py index 05e6989c8..93490ba91 100644 --- a/charmcraft/models/charmcraft.py +++ b/charmcraft/models/charmcraft.py @@ -117,9 +117,9 @@ class CharmcraftConfig( metadata_legacy: bool = False type: str - name: pydantic.StrictStr - summary: pydantic.StrictStr - description: pydantic.StrictStr + name: Optional[pydantic.StrictStr] + summary: Optional[pydantic.StrictStr] + description: Optional[pydantic.StrictStr] charmhub: CharmhubConfig = CharmhubConfig() parts: Optional[Dict[str, Any]] bases: Optional[List[BasesConfiguration]] @@ -150,11 +150,27 @@ def validate_charm_type(cls, charm_type): @pydantic.validator("name", pre=True, always=True) def validate_name(cls, name, values): """Verify charm name is valid with exception when instantiated without YAML.""" - if not name: - raise ValueError("value must not be empty") + if values.get("type") == "charm" and not name: + raise ValueError("needs value") return name + @pydantic.validator("summary", pre=True, always=True) + def validate_summary(cls, summary, values): + """Verify charm summary is valid with exception when instantiated without YAML.""" + if values.get("type") == "charm" and not summary: + raise ValueError("needs value") + + return summary + + @pydantic.validator("description", pre=True, always=True) + def validate_description(cls, description, values): + """Verify charm name is valid with exception when instantiated without YAML.""" + if values.get("type") == "charm" and not description: + raise ValueError("needs value") + + return description + @pydantic.validator("parts", pre=True, always=True) def validate_special_parts(cls, parts, values): """Verify parts type (craft-parts will re-validate the schemas for the plugins).""" @@ -313,31 +329,22 @@ def unmarshal(cls, obj: Dict[str, Any], project: Project): } ) elif obj.get("type") == "bundle": - # only need name from bundle, - # metadata.yaml will be copied without validation + # bundle may not have metadata.yaml. + # but if it does, it should have name and optional description + # metadata.yaml will be copied without validation if it exists metadata_legacy = parse_bundle_metadata_yaml(project.dirpath) return cls.parse_obj( { "project": project, "name": metadata_legacy.name, - "summary": "", - "description": metadata_legacy.description or "", + "description": metadata_legacy.description, "metadata-legacy": True, **obj, } ) else: # fallthrough for pydantic to handle - return cls.parse_obj( - { - "project": project, - "name": "invalid-type", - "summary": "", - "description": "", - "metadata-legacy": True, - **obj, - } - ) + pass return cls.parse_obj({"project": project, **obj}) except pydantic.error_wrappers.ValidationError as error: diff --git a/tests/commands/test_build.py b/tests/commands/test_build.py index 8913b8cb0..47ac40fd1 100644 --- a/tests/commands/test_build.py +++ b/tests/commands/test_build.py @@ -245,9 +245,9 @@ def test_build_error_without_metadata_yaml(basic_project): assert str(exc_info.value) == dedent( """\ Bad charmcraft.yaml content: - - field 'name' required in top-level configuration - - field 'summary' required in top-level configuration - - field 'description' required in top-level configuration""" + - needs value in field 'name' + - needs value in field 'summary' + - needs value in field 'description'""" ) diff --git a/tests/test_models.py b/tests/test_models.py index 152c06563..50f2fc7c7 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -95,7 +95,7 @@ def test_load_minimal_metadata_from_charmcraft_yaml_missing_name( ) ) - with pytest.raises(CraftError, match="field 'name' required in top-level configuration"): + with pytest.raises(CraftError, match="needs value in field 'name'"): load(tmp_path) @@ -139,7 +139,7 @@ def test_load_minimal_metadata_from_charmcraft_yaml_missing_summary( ), ) - with pytest.raises(CraftError, match="field 'summary' required in top-level configuration"): + with pytest.raises(CraftError, match="needs value in field 'summary'"): load(tmp_path) @@ -161,9 +161,7 @@ def test_load_minimal_metadata_from_charmcraft_yaml_missing_description( ), ) - with pytest.raises( - CraftError, match="field 'description' required in top-level configuration" - ): + with pytest.raises(CraftError, match="needs value in field 'description'"): load(tmp_path)