@@ -394,6 +394,51 @@ def run(self):
394394 self .check_board_file (file , vendor_prefixes )
395395
396396
397+ class ShieldYmlCheck (ComplianceTest ):
398+ """
399+ Validate shield.yml files when shield-related files change.
400+ """
401+
402+ name = "ShieldYml"
403+ doc = "Check the shield.yml file format"
404+
405+ @staticmethod
406+ def _is_shield_related (path : str ) -> bool :
407+ return (
408+ "/boards/shields/" in f"/{ path } "
409+ or path .endswith ("/shield.yml" )
410+ or path == "scripts/list_shields.py"
411+ or path == "scripts/schemas/shield-schema.yml"
412+ )
413+
414+ def run (self ):
415+ changed = get_files (filter = "d" )
416+ shield_related = [p for p in changed if self ._is_shield_related (p )]
417+ if not shield_related :
418+ self .skip ("No shield-related files changed" )
419+
420+ # Validate all shields for impacted board roots, not only changed files.
421+ # This catches malformed shield.yml and schema/parser regressions.
422+ board_roots = {str (ZEPHYR_BASE )}
423+ for rel_path in shield_related :
424+ abs_path = (GIT_TOP / rel_path ).resolve ()
425+ parts = list (abs_path .parts )
426+ if "boards" in parts and "shields" in parts :
427+ idx = parts .index ("boards" )
428+ if idx > 0 :
429+ board_roots .add (str (Path (* parts [:idx ])))
430+
431+ cmd = [sys .executable , str (ZEPHYR_BASE / "scripts" / "list_shields.py" )]
432+ for root in sorted (board_roots ):
433+ cmd .append (f"--board-root={ root } " )
434+ cmd .append ("--json" )
435+
436+ try :
437+ subprocess .run (cmd , check = True , stdout = subprocess .DEVNULL , stderr = subprocess .PIPE )
438+ except subprocess .CalledProcessError as ex :
439+ self .failure (ex .stderr .decode ("utf-8" ) or "Shield YAML validation failed" )
440+
441+
397442class ClangFormatCheck (ComplianceTest ):
398443 """
399444 Check if clang-format reports any issues
0 commit comments