Skip to content

Commit 4ade5f6

Browse files
committed
handle invalid plugin metadata gracefully
1 parent 780db1a commit 4ade5f6

2 files changed

Lines changed: 44 additions & 2 deletions

File tree

scripts/validate_plugins/run.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@
2424

2525
REQUIRED_METADATA_FIELDS = ("name", "desc", "version", "author")
2626
DEFAULT_CLONE_TIMEOUT = 120
27+
CONFLICT_MARKERS = ("<<<<<<<", "=======", ">>>>>>>")
28+
29+
30+
class MetadataLoadError(ValueError):
31+
pass
2732

2833

2934
def build_result(
@@ -104,8 +109,17 @@ def _parse_simple_yaml(path: Path) -> dict:
104109

105110

106111
def load_metadata(path: Path) -> dict:
112+
text = path.read_text(encoding="utf-8")
113+
if any(marker in text for marker in CONFLICT_MARKERS):
114+
raise MetadataLoadError(
115+
"could not find expected ':' (merge conflict markers found in metadata.yaml)"
116+
)
117+
107118
if yaml is not None:
108-
loaded = yaml.safe_load(path.read_text(encoding="utf-8"))
119+
try:
120+
loaded = yaml.safe_load(text)
121+
except yaml.YAMLError as exc:
122+
raise MetadataLoadError(str(exc)) from exc
109123
if isinstance(loaded, dict):
110124
return loaded
111125
return {}
@@ -121,7 +135,16 @@ def precheck_plugin_directory(plugin_dir: Path) -> dict:
121135
"message": "missing metadata.yaml",
122136
}
123137

124-
metadata = load_metadata(metadata_path)
138+
try:
139+
metadata = load_metadata(metadata_path)
140+
except MetadataLoadError as exc:
141+
return {
142+
"ok": False,
143+
"stage": "metadata",
144+
"message": "invalid metadata.yaml",
145+
"details": str(exc),
146+
}
147+
125148
missing = [
126149
field
127150
for field in REQUIRED_METADATA_FIELDS
@@ -367,6 +390,7 @@ def validate_plugin(
367390
ok=False,
368391
stage=precheck["stage"],
369392
message=precheck["message"],
393+
details=precheck.get("details"),
370394
)
371395

372396
plugin_dir_name = precheck["plugin_dir_name"]

tests/test_validate_plugins.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,24 @@ def test_reports_missing_required_metadata_fields(self):
215215
self.assertIn("desc", result["message"])
216216
self.assertIn("version", result["message"])
217217

218+
def test_reports_invalid_metadata_yaml_without_raising(self):
219+
module = load_validator_module()
220+
221+
with tempfile.TemporaryDirectory() as tmp_dir:
222+
plugin_dir = Path(tmp_dir)
223+
(plugin_dir / "metadata.yaml").write_text(
224+
"name: demo_plugin\n<<<<<<< HEAD\ndesc: broken\n=======\ndesc: fixed\n>>>>>>> branch\n",
225+
encoding="utf-8",
226+
)
227+
(plugin_dir / "main.py").write_text("print('hello')\n", encoding="utf-8")
228+
229+
result = module.precheck_plugin_directory(plugin_dir)
230+
231+
self.assertFalse(result["ok"])
232+
self.assertEqual(result["stage"], "metadata")
233+
self.assertIn("invalid metadata.yaml", result["message"])
234+
self.assertIn("could not find expected ':'", result["details"])
235+
218236

219237
class WorkerCommandTests(unittest.TestCase):
220238
def test_build_worker_command_contains_required_arguments(self):

0 commit comments

Comments
 (0)