from __future__ import annotations import argparse import json from collections.abc import Sequence from pathlib import Path from engine.devops_agent.compiler import compile_workflow from engine.devops_agent.providers.gitea import GiteaProvider from engine.devops_agent.runtime import run_issue_comment_workflow from engine.devops_agent.spec import load_workflow_spec from engine.devops_agent.validator import validate_workflow_spec def build_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser( prog="devops-agent", description="CLI for the agentic DevOps runtime.", ) parser.add_argument( "--version", action="store_true", help="Print the runtime version and exit.", ) subparsers = parser.add_subparsers(dest="command") compile_parser = subparsers.add_parser("compile") compile_parser.add_argument("spec_path") compile_parser.add_argument("--output", required=True) validate_parser = subparsers.add_parser("validate") validate_parser.add_argument("spec_path") run_parser = subparsers.add_parser("run") run_parser.add_argument("spec_path") run_parser.add_argument("--event-payload", required=True) run_parser.add_argument("--output-dir", required=True) run_parser.add_argument("--base-url", required=True) run_parser.add_argument("--token", required=True) acceptance_parser = subparsers.add_parser("acceptance") acceptance_parser.add_argument("spec_path") acceptance_parser.add_argument("--base-url", required=True) acceptance_parser.add_argument("--repo", required=True) acceptance_parser.add_argument("--token", required=True) acceptance_parser.add_argument("--issue-number", required=True) acceptance_parser.add_argument("--output-dir", required=True) acceptance_parser.add_argument( "--comment-body", default="@devops-agent acceptance run", ) return parser def _load_compile_and_validate(spec_path: str) -> tuple[dict[str, object], list[str]]: spec = load_workflow_spec(spec_path) errors = validate_workflow_spec(spec) return compile_workflow(spec), errors def main(argv: Sequence[str] | None = None) -> int: parser = build_parser() args = parser.parse_args(argv) if args.version: from engine.devops_agent import __version__ print(__version__) return 0 if not getattr(args, "command", None): parser.print_help() return 0 if args.command == "compile": lock, errors = _load_compile_and_validate(args.spec_path) if errors: print(json.dumps({"errors": errors}, ensure_ascii=False, indent=2)) return 1 output_path = Path(args.output) output_path.parent.mkdir(parents=True, exist_ok=True) output_path.write_text(json.dumps(lock, ensure_ascii=False, indent=2), encoding="utf-8") return 0 if args.command == "validate": _, errors = _load_compile_and_validate(args.spec_path) if errors: print(json.dumps({"errors": errors}, ensure_ascii=False, indent=2)) return 1 print("workflow is valid") return 0 if args.command == "run": lock, errors = _load_compile_and_validate(args.spec_path) if errors: print(json.dumps({"errors": errors}, ensure_ascii=False, indent=2)) return 1 provider = GiteaProvider(base_url=args.base_url, token=args.token) payload = json.loads(Path(args.event_payload).read_text(encoding="utf-8")) run_issue_comment_workflow( lock=lock, provider=provider, event_payload=payload, output_dir=args.output_dir, ) return 0 if args.command == "acceptance": lock, errors = _load_compile_and_validate(args.spec_path) if errors: print(json.dumps({"errors": errors}, ensure_ascii=False, indent=2)) return 1 provider = GiteaProvider(base_url=args.base_url, token=args.token) payload = { "repository": {"full_name": args.repo}, "issue": {"number": int(args.issue_number)}, "comment": {"body": args.comment_body}, } run_issue_comment_workflow( lock=lock, provider=provider, event_payload=payload, output_dir=args.output_dir, ) return 0 parser.error(f"unsupported command: {args.command}") return 2 if __name__ == "__main__": raise SystemExit(main())