88import yaml
99
1010# Set up logging
11- logging .basicConfig (level = logging .INFO , format = ' %(message)s' )
11+ logging .basicConfig (level = logging .INFO , format = " %(message)s" )
1212logger = logging .getLogger (__name__ )
1313
1414
@@ -30,70 +30,74 @@ def validate_workflow_structure(file_path: Path):
3030 try :
3131 with open (file_path , "r" ) as f :
3232 workflow = yaml .safe_load (f )
33-
33+
3434 issues = []
35-
35+
3636 # Check required top-level keys
3737 if not isinstance (workflow , dict ):
3838 issues .append ("Workflow must be a YAML object" )
3939 return issues
40-
41- if ' name' not in workflow :
40+
41+ if " name" not in workflow :
4242 issues .append ("Missing required 'name' field" )
43-
43+
4444 # Check for 'on' field (can be parsed as True due to YAML boolean conversion)
45- if 'on' not in workflow and True not in workflow :
45+ if "on" not in workflow and True not in workflow :
4646 issues .append ("Missing required 'on' field" )
47-
48- if ' jobs' not in workflow :
47+
48+ if " jobs" not in workflow :
4949 issues .append ("Missing required 'jobs' field" )
50-
50+
5151 # Check for common malformed patterns we encountered
52- if ' jobs' in workflow :
53- for job_name , job in workflow [' jobs' ].items ():
52+ if " jobs" in workflow :
53+ for job_name , job in workflow [" jobs" ].items ():
5454 if not isinstance (job , dict ):
5555 continue
56-
57- if ' steps' in job :
58- steps = job [' steps' ]
56+
57+ if " steps" in job :
58+ steps = job [" steps" ]
5959 if isinstance (steps , list ):
6060 for i , step in enumerate (steps ):
6161 if not isinstance (step , dict ):
6262 continue
63-
63+
6464 # Check for malformed step structure (the main issue we had)
6565 step_str = str (step )
66- if step_str .count ('uses:' ) > 1 :
67- issues .append (f"Job '{ job_name } ' step { i + 1 } has duplicate 'uses' fields" )
68-
66+ if step_str .count ("uses:" ) > 1 :
67+ issues .append (
68+ f"Job '{ job_name } ' step { i + 1 } has duplicate 'uses' fields"
69+ )
70+
6971 # Check for steps that have name but no action (less strict)
70- if 'name' in step and len (step ) == 1 :
71- issues .append (f"Job '{ job_name } ' step { i + 1 } ('{ step ['name' ]} ') has no action" )
72-
72+ if "name" in step and len (step ) == 1 :
73+ issues .append (
74+ f"Job '{ job_name } ' step { i + 1 } ('{ step ['name' ]} ') has no action"
75+ )
76+
7377 return issues
74-
78+
7579 except Exception as e :
7680 return [f"Structure validation error: { e } " ]
7781
7882
7983def validate_workflow (file_path : Path ):
8084 """Validate a single workflow file comprehensively."""
8185 logger .info (f"Validating { file_path .name } ..." )
82-
86+
8387 # First check YAML syntax
8488 syntax_valid , syntax_error = validate_workflow_syntax (file_path )
8589 if not syntax_valid :
8690 logger .error (f" ❌ { file_path .name } : { syntax_error } " )
8791 return False
88-
92+
8993 # Then check workflow structure
9094 structure_issues = validate_workflow_structure (file_path )
9195 if structure_issues :
9296 logger .error (f" ❌ { file_path .name } : Structure issues:" )
9397 for issue in structure_issues :
9498 logger .error (f" - { issue } " )
9599 return False
96-
100+
97101 logger .info (f" ✅ { file_path .name } : Valid" )
98102 return True
99103
0 commit comments