Files
devops-skills/engine/devops_agent/validator.py

50 lines
1.8 KiB
Python

from __future__ import annotations
from typing import Any
from engine.devops_agent.spec import WorkflowSpec
WRITE_PERMISSIONS = {"issues", "pull_requests", "contents"}
def _is_write_permission(value: Any) -> bool:
return str(value).strip().lower() == "write"
def validate_workflow_spec(spec: WorkflowSpec) -> list[str]:
errors: list[str] = []
if spec.provider not in {"gitea"}:
errors.append(f"unsupported provider: {spec.provider}")
triggers = spec.frontmatter.get("on")
if not isinstance(triggers, dict) or not triggers:
errors.append("workflow spec must declare at least one trigger in 'on'")
permissions = spec.frontmatter.get("permissions") or {}
safe_outputs = spec.frontmatter.get("safe_outputs") or {}
if not isinstance(permissions, dict):
errors.append("'permissions' must be a mapping")
if not isinstance(safe_outputs, dict):
errors.append("'safe_outputs' must be a mapping")
if isinstance(permissions, dict):
has_write_permission = any(
permission_name in WRITE_PERMISSIONS and _is_write_permission(permission_value)
for permission_name, permission_value in permissions.items()
)
if has_write_permission and not safe_outputs:
errors.append("write permissions require declared safe_outputs")
policy = spec.frontmatter.get("policy") or {}
if policy and not isinstance(policy, dict):
errors.append("'policy' must be a mapping")
elif isinstance(policy, dict) and "path_scope" in policy:
path_scope = policy["path_scope"]
if not isinstance(path_scope, list) or any(
not isinstance(item, str) or not item.strip() for item in path_scope
):
errors.append("policy.path_scope must be a list of non-empty path prefixes")
return errors