Coverage for src / rhiza_hooks / check_workflow_names.py: 100%

43 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-04-02 06:48 +0000

1#!/usr/bin/env python3 

2"""Script to ensure GitHub Actions workflows have the (RHIZA) prefix. 

3 

4This hook checks that all rhiza workflow files have their 'name' field 

5properly formatted with the (RHIZA) prefix in uppercase. If not, it 

6automatically updates the file. 

7 

8Migrated from: https://github.com/Jebel-Quant/rhiza/.rhiza/scripts/check_workflow_names.py 

9""" 

10 

11from __future__ import annotations 

12 

13import sys 

14 

15import yaml 

16 

17 

18def check_file(filepath: str) -> bool: 

19 """Check if the workflow file has the correct name prefix and update if needed. 

20 

21 Args: 

22 filepath: Path to the workflow file. 

23 

24 Returns: 

25 bool: True if file is correct, False if it was updated or has errors. 

26 """ 

27 with open(filepath) as f: 

28 try: 

29 content = yaml.safe_load(f) 

30 except yaml.YAMLError as exc: 

31 print(f"Error parsing YAML {filepath}: {exc}") 

32 return False 

33 

34 if not isinstance(content, dict): 

35 # Empty file or not a dict 

36 return True 

37 

38 name = content.get("name") 

39 if not name: 

40 print(f"Error: {filepath} missing 'name' field.") 

41 return False 

42 

43 prefix = "(RHIZA) " 

44 # Remove prefix if present to verify the rest of the string 

45 clean_name = name[len(prefix) :] if name.startswith(prefix) else name 

46 

47 expected_name = f"{prefix}{clean_name.upper()}" 

48 

49 if name != expected_name: 

50 print(f"Updating {filepath}: name '{name}' -> '{expected_name}'") 

51 

52 # Read file lines to perform replacement while preserving comments 

53 with open(filepath) as f_read: 

54 lines = f_read.readlines() 

55 

56 with open(filepath, "w") as f_write: 

57 replaced = False 

58 for line in lines: 

59 # Replace only the top-level name field (assumes it starts at beginning of line) 

60 if not replaced and line.startswith("name:"): 

61 # Check if this line corresponds to the extracted name. 

62 # Simple check: does it contain reasonable parts of the name? 

63 # Or just blinding replace top-level name: 

64 # We'll use quotes to be safe 

65 f_write.write(f'name: "{expected_name}"\n') 

66 replaced = True 

67 else: 

68 f_write.write(line) 

69 

70 return False # Fail so pre-commit knows files were modified 

71 

72 return True 

73 

74 

75def main(argv: list[str] | None = None) -> int: 

76 """Execute the script.""" 

77 files = argv if argv is not None else sys.argv[1:] 

78 failed = False 

79 for f in files: 

80 if not check_file(f): 

81 failed = True 

82 

83 if failed: 

84 sys.exit(1) 

85 return 0 

86 

87 

88if __name__ == "__main__": 

89 main()