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

131 lines
4.5 KiB
Python

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())