| | """GitHub-powered deployment agent for direct deployment actions.""" |
| |
|
| | from __future__ import annotations |
| |
|
| | import os |
| | from typing import Any, Dict, List, Optional |
| |
|
| | from enhanced_mcp_client import EnhancedMCPClient |
| | from schemas import ReadinessPlan, ReadinessRequest |
| |
|
| |
|
| | class DeploymentAgent: |
| | """Handles actual deployment actions via GitHub MCP.""" |
| |
|
| | def __init__(self): |
| | self.mcp_client = EnhancedMCPClient() |
| |
|
| | async def prepare_deployment( |
| | self, request: ReadinessRequest, plan: ReadinessPlan |
| | ) -> Dict[str, Any]: |
| | """Prepare deployment configuration and actions.""" |
| | github_repo = os.getenv("GITHUB_REPO") |
| | github_branch = os.getenv("GITHUB_BRANCH", "main") |
| | |
| | deployment_config = { |
| | "repo": github_repo, |
| | "branch": github_branch, |
| | "ready": False, |
| | "actions": [] |
| | } |
| |
|
| | if not github_repo: |
| | deployment_config["actions"].append({ |
| | "type": "error", |
| | "message": "GITHUB_REPO not configured", |
| | "actionable": False |
| | }) |
| | return deployment_config |
| |
|
| | |
| | deployment_config["actions"].append({ |
| | "type": "check_workflow", |
| | "message": f"Checking for deployment workflow in {github_repo}", |
| | "actionable": True |
| | }) |
| |
|
| | |
| | pr_title = f"Deploy: {request.release_goal}" |
| | pr_body = f""" |
| | ## Deployment Readiness Summary |
| | |
| | **Project**: {request.project_name} |
| | **Goal**: {request.release_goal} |
| | |
| | ### Checklist Items |
| | {chr(10).join(f"- [ ] {item.title}" for item in plan.items[:5])} |
| | |
| | ### Code Summary |
| | {request.code_summary[:200]}... |
| | |
| | ### Infrastructure Notes |
| | {request.infra_notes or "None provided"} |
| | |
| | --- |
| | *Generated by Deployment Readiness Copilot* |
| | """.strip() |
| |
|
| | deployment_config["actions"].append({ |
| | "type": "create_pr", |
| | "title": pr_title, |
| | "body": pr_body, |
| | "branch": f"deploy/{request.project_name.lower().replace(' ', '-')}", |
| | "actionable": True |
| | }) |
| |
|
| | |
| | deployment_config["actions"].append({ |
| | "type": "trigger_workflow", |
| | "workflow": ".github/workflows/deploy.yml", |
| | "branch": github_branch, |
| | "actionable": True |
| | }) |
| |
|
| | deployment_config["ready"] = True |
| | return deployment_config |
| |
|
| | async def execute_deployment( |
| | self, deployment_config: Dict[str, Any] |
| | ) -> Dict[str, Any]: |
| | """Execute deployment actions via MCP to selected platform.""" |
| | results = { |
| | "success": False, |
| | "actions_executed": [], |
| | "errors": [], |
| | "message": "" |
| | } |
| |
|
| | platform = deployment_config.get("platform", "").lower() |
| | framework = deployment_config.get("framework", "").lower() |
| | repo = deployment_config.get("repo") |
| |
|
| | if not platform or platform == "none": |
| | results["message"] = "No deployment platform selected" |
| | return results |
| |
|
| | try: |
| | |
| | if platform == "vercel": |
| | if repo: |
| | |
| | deploy_result = await self.mcp_client.deploy_to_vercel( |
| | repo=repo, |
| | framework=framework |
| | ) |
| | results["actions_executed"].append({ |
| | "type": "vercel_deploy", |
| | "result": deploy_result |
| | }) |
| | results["success"] = True |
| | results["message"] = f"β
Deployed to Vercel (framework: {framework})" |
| | else: |
| | results["message"] = "β οΈ GitHub repo required for Vercel deployment. Configure GITHUB_REPO." |
| | |
| | |
| | elif platform == "netlify": |
| | if repo: |
| | deploy_result = await self.mcp_client.deploy_to_netlify( |
| | repo=repo, |
| | framework=framework |
| | ) |
| | results["actions_executed"].append({ |
| | "type": "netlify_deploy", |
| | "result": deploy_result |
| | }) |
| | results["success"] = True |
| | results["message"] = f"β
Deployed to Netlify (framework: {framework})" |
| | else: |
| | results["message"] = "β οΈ GitHub repo required for Netlify deployment." |
| | |
| | |
| | elif platform == "aws": |
| | deploy_result = await self.mcp_client.deploy_to_aws( |
| | repo=repo, |
| | framework=framework, |
| | config=deployment_config |
| | ) |
| | results["actions_executed"].append({ |
| | "type": "aws_deploy", |
| | "result": deploy_result |
| | }) |
| | results["success"] = True |
| | results["message"] = f"β
AWS deployment initiated (framework: {framework})" |
| | |
| | |
| | elif platform == "gcp": |
| | deploy_result = await self.mcp_client.deploy_to_gcp( |
| | repo=repo, |
| | framework=framework, |
| | config=deployment_config |
| | ) |
| | results["actions_executed"].append({ |
| | "type": "gcp_deploy", |
| | "result": deploy_result |
| | }) |
| | results["success"] = True |
| | results["message"] = f"β
GCP deployment initiated (framework: {framework})" |
| | |
| | |
| | elif platform == "azure": |
| | deploy_result = await self.mcp_client.deploy_to_azure( |
| | repo=repo, |
| | framework=framework, |
| | config=deployment_config |
| | ) |
| | results["actions_executed"].append({ |
| | "type": "azure_deploy", |
| | "result": deploy_result |
| | }) |
| | results["success"] = True |
| | results["message"] = f"β
Azure deployment initiated (framework: {framework})" |
| | |
| | |
| | elif platform == "railway": |
| | deploy_result = await self.mcp_client.deploy_to_railway( |
| | repo=repo, |
| | framework=framework |
| | ) |
| | results["actions_executed"].append({ |
| | "type": "railway_deploy", |
| | "result": deploy_result |
| | }) |
| | results["success"] = True |
| | results["message"] = f"β
Railway deployment initiated (framework: {framework})" |
| | |
| | |
| | elif platform == "render": |
| | deploy_result = await self.mcp_client.deploy_to_render( |
| | repo=repo, |
| | framework=framework |
| | ) |
| | results["actions_executed"].append({ |
| | "type": "render_deploy", |
| | "result": deploy_result |
| | }) |
| | results["success"] = True |
| | results["message"] = f"β
Render deployment initiated (framework: {framework})" |
| | |
| | |
| | elif platform == "fly.io": |
| | deploy_result = await self.mcp_client.deploy_to_flyio( |
| | repo=repo, |
| | framework=framework |
| | ) |
| | results["actions_executed"].append({ |
| | "type": "flyio_deploy", |
| | "result": deploy_result |
| | }) |
| | results["success"] = True |
| | results["message"] = f"β
Fly.io deployment initiated (framework: {framework})" |
| | |
| | |
| | elif platform == "kubernetes": |
| | deploy_result = await self.mcp_client.deploy_to_kubernetes( |
| | repo=repo, |
| | framework=framework, |
| | config=deployment_config |
| | ) |
| | results["actions_executed"].append({ |
| | "type": "k8s_deploy", |
| | "result": deploy_result |
| | }) |
| | results["success"] = True |
| | results["message"] = f"β
Kubernetes deployment initiated (framework: {framework})" |
| | |
| | |
| | elif platform == "docker": |
| | deploy_result = await self.mcp_client.deploy_to_docker( |
| | repo=repo, |
| | framework=framework, |
| | config=deployment_config |
| | ) |
| | results["actions_executed"].append({ |
| | "type": "docker_deploy", |
| | "result": deploy_result |
| | }) |
| | results["success"] = True |
| | results["message"] = f"β
Docker deployment initiated (framework: {framework})" |
| | |
| | else: |
| | results["message"] = f"β οΈ Platform '{platform}' deployment via MCP not yet implemented" |
| | results["errors"].append(f"Unsupported platform: {platform}") |
| |
|
| | except Exception as e: |
| | results["errors"].append({ |
| | "platform": platform, |
| | "error": str(e) |
| | }) |
| | results["message"] = f"β Deployment error: {str(e)}" |
| |
|
| | return results |
| |
|
| | async def create_pull_request(self, title: str, body: str, branch: str, files: Dict[str, str]) -> str: |
| | """Create a GitHub Pull Request with the specified changes.""" |
| | token = os.getenv("GITHUB_TOKEN") |
| | repo_name = os.getenv("GITHUB_REPO") |
| | |
| | if not token or not repo_name: |
| | return "β GITHUB_TOKEN or GITHUB_REPO not set." |
| | |
| | try: |
| | from github import Github |
| | g = Github(token) |
| | repo = g.get_repo(repo_name) |
| | |
| | |
| | source_branch = repo.get_branch("main") |
| | |
| | |
| | try: |
| | repo.create_git_ref(ref=f"refs/heads/{branch}", sha=source_branch.commit.sha) |
| | except Exception: |
| | |
| | pass |
| | |
| | |
| | for file_path, content in files.items(): |
| | try: |
| | contents = repo.get_contents(file_path, ref=branch) |
| | repo.update_file(contents.path, f"Update {file_path}", content, contents.sha, branch=branch) |
| | except Exception: |
| | repo.create_file(file_path, f"Create {file_path}", content, branch=branch) |
| | |
| | |
| | pr = repo.create_pull(title=title, body=body, head=branch, base="main") |
| | return f"β
Pull Request Created: {pr.html_url}" |
| | |
| | except ImportError: |
| | return "β PyGithub not installed." |
| | except Exception as e: |
| | return f"β Failed to create PR: {str(e)}" |
| |
|