"""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") # Format: owner/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 # Check if deployment workflow exists deployment_config["actions"].append({ "type": "check_workflow", "message": f"Checking for deployment workflow in {github_repo}", "actionable": True }) # Prepare deployment PR 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 }) # Trigger deployment workflow 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: # Deploy to Vercel via MCP if platform == "vercel": if repo: # Use Vercel MCP to deploy 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." # Deploy to Netlify via MCP 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." # Deploy to AWS via MCP 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})" # Deploy to GCP via MCP 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})" # Deploy to Azure via MCP 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})" # Deploy to Railway via MCP 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})" # Deploy to Render via MCP 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})" # Deploy to Fly.io via MCP 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})" # Kubernetes deployment 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})" # Docker deployment 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) # Get main branch source_branch = repo.get_branch("main") # Create new branch try: repo.create_git_ref(ref=f"refs/heads/{branch}", sha=source_branch.commit.sha) except Exception: # Branch might already exist pass # Commit files 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) # Create PR 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)}"