File size: 1,367 Bytes
a7caaff 4b07aaf | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | """YAML parsing helpers."""
from typing import Any, Optional, Tuple
import yaml
def safe_parse_yaml(content: str) -> Tuple[Optional[Any], Optional[str]]:
"""Parse YAML content safely.
Returns (parsed, error_message). If parsing succeeds, error_message is None.
If parsing fails, parsed is None and error_message contains the description.
"""
try:
parsed = yaml.safe_load(content)
return parsed, None
except yaml.YAMLError as exc:
return None, str(exc)
def is_valid_workflow(content: str) -> Tuple[bool, Optional[str]]:
"""Check if content is a valid GitHub Actions workflow YAML.
Returns (is_valid, error_message).
"""
parsed, err = safe_parse_yaml(content)
if err:
return False, f"YAML parse error: {err}"
if not isinstance(parsed, dict):
return False, "Workflow root must be a mapping"
if "jobs" not in parsed:
return False, "Workflow must define 'jobs'"
jobs = parsed.get("jobs")
if not isinstance(jobs, dict) or not jobs:
return False, "Workflow must define at least one job"
for job_name, job in jobs.items():
if not isinstance(job, dict):
return False, f"Job '{job_name}' must be a mapping"
if "runs-on" not in job:
return False, f"Job '{job_name}' is missing 'runs-on'"
return True, None
|