cli.py 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. import json
  2. import os
  3. import re
  4. import shutil
  5. import subprocess
  6. import click
  7. import pkg_resources
  8. from rich.console import Console
  9. console = Console()
  10. @click.group()
  11. def cli():
  12. pass
  13. def setup_fly_io_app(extra_args):
  14. fly_launch_command = ["fly", "launch", "--region", "sjc", "--no-deploy"] + list(extra_args)
  15. try:
  16. console.print(f"🚀 [bold cyan]Running: {' '.join(fly_launch_command)}[/bold cyan]")
  17. subprocess.run(fly_launch_command, check=True)
  18. console.print("✅ [bold green]'fly launch' executed successfully.[/bold green]")
  19. except subprocess.CalledProcessError as e:
  20. console.print(f"❌ [bold red]An error occurred: {e}[/bold red]")
  21. except FileNotFoundError:
  22. console.print(
  23. "❌ [bold red]'fly' command not found. Please ensure Fly CLI is installed and in your PATH.[/bold red]"
  24. )
  25. def setup_modal_com_app(extra_args):
  26. modal_setup_file = os.path.join(os.path.expanduser("~"), ".modal.toml")
  27. if os.path.exists(modal_setup_file):
  28. console.print(
  29. """✅ [bold green]Modal setup already done. You can now install the dependencies by doing \n
  30. `pip install -r requirements.txt`[/bold green]"""
  31. )
  32. return
  33. modal_setup_cmd = ["modal", "setup"] + list(extra_args)
  34. console.print(f"🚀 [bold cyan]Running: {' '.join(modal_setup_cmd)}[/bold cyan]")
  35. subprocess.run(modal_setup_cmd, check=True)
  36. shutil.move(".env.example", ".env")
  37. console.print("Great! Now you can install the dependencies by doing `pip install -r requirements.txt`")
  38. @cli.command()
  39. @click.option("--template", default="fly.io", help="The template to use.")
  40. @click.argument("extra_args", nargs=-1, type=click.UNPROCESSED)
  41. def create(template, extra_args):
  42. try:
  43. # Determine the installation location of the embedchain package
  44. package_path = pkg_resources.resource_filename("embedchain", "")
  45. except ImportError:
  46. console.print("❌ [bold red]Failed to locate the 'embedchain' package. Is it installed?[/bold red]")
  47. return
  48. # Construct the source path from the embedchain package
  49. src_path = os.path.join(package_path, "deployment", template)
  50. if not os.path.exists(src_path):
  51. console.print(f"❌ [bold red]Template '{template}' not found.[/bold red]")
  52. return
  53. shutil.copytree(src_path, os.getcwd(), dirs_exist_ok=True)
  54. env_sample_path = os.path.join(src_path, ".env.example")
  55. if os.path.exists(env_sample_path):
  56. shutil.copy(env_sample_path, os.path.join(os.getcwd(), ".env"))
  57. console.print(f"✅ [bold green]Successfully created app from template '{template}'.[/bold green]")
  58. if template == "fly.io":
  59. setup_fly_io_app(extra_args)
  60. elif template == "modal.com":
  61. setup_modal_com_app(extra_args)
  62. else:
  63. raise ValueError(f"Unknown template '{template}'.")
  64. embedchain_config = {"provider": template}
  65. with open("embedchain.json", "w") as file:
  66. json.dump(embedchain_config, file, indent=4)
  67. console.print(
  68. f"🎉 [green]All done! Successfully created `embedchain.json` with '{template}' as provider.[/green]"
  69. )
  70. def run_dev_fly_io(debug, host, port):
  71. uvicorn_command = ["uvicorn", "app:app"]
  72. if debug:
  73. uvicorn_command.append("--reload")
  74. uvicorn_command.extend(["--host", host, "--port", str(port)])
  75. try:
  76. console.print(f"🚀 [bold cyan]Running FastAPI app with command: {' '.join(uvicorn_command)}[/bold cyan]")
  77. subprocess.run(uvicorn_command, check=True)
  78. except subprocess.CalledProcessError as e:
  79. console.print(f"❌ [bold red]An error occurred: {e}[/bold red]")
  80. except KeyboardInterrupt:
  81. console.print("\n🛑 [bold yellow]FastAPI server stopped[/bold yellow]")
  82. def run_dev_modal_com():
  83. modal_run_cmd = ["modal", "serve", "app"]
  84. try:
  85. console.print(f"🚀 [bold cyan]Running FastAPI app with command: {' '.join(modal_run_cmd)}[/bold cyan]")
  86. subprocess.run(modal_run_cmd, check=True)
  87. except subprocess.CalledProcessError as e:
  88. console.print(f"❌ [bold red]An error occurred: {e}[/bold red]")
  89. except KeyboardInterrupt:
  90. console.print("\n🛑 [bold yellow]FastAPI server stopped[/bold yellow]")
  91. @cli.command()
  92. @click.option("--debug", is_flag=True, help="Enable or disable debug mode.")
  93. @click.option("--host", default="127.0.0.1", help="The host address to run the FastAPI app on.")
  94. @click.option("--port", default=8000, help="The port to run the FastAPI app on.")
  95. def dev(debug, host, port):
  96. template = ""
  97. with open("embedchain.json", "r") as file:
  98. embedchain_config = json.load(file)
  99. template = embedchain_config["provider"]
  100. if template == "fly.io":
  101. run_dev_fly_io(debug, host, port)
  102. elif template == "modal.com":
  103. run_dev_modal_com()
  104. else:
  105. raise ValueError(f"Unknown template '{template}'.")
  106. def read_env_file(env_file_path):
  107. """
  108. Reads an environment file and returns a dictionary of key-value pairs.
  109. Args:
  110. env_file_path (str): The path to the .env file.
  111. Returns:
  112. dict: Dictionary of environment variables.
  113. """
  114. env_vars = {}
  115. with open(env_file_path, "r") as file:
  116. for line in file:
  117. # Ignore comments and empty lines
  118. if line.strip() and not line.strip().startswith("#"):
  119. # Assume each line is in the format KEY=VALUE
  120. key_value_match = re.match(r"(\w+)=(.*)", line.strip())
  121. if key_value_match:
  122. key, value = key_value_match.groups()
  123. env_vars[key] = value
  124. return env_vars
  125. def deploy_fly():
  126. app_name = ""
  127. with open("fly.toml", "r") as file:
  128. for line in file:
  129. if line.strip().startswith("app ="):
  130. app_name = line.split("=")[1].strip().strip('"')
  131. if not app_name:
  132. console.print("❌ [bold red]App name not found in fly.toml[/bold red]")
  133. return
  134. env_vars = read_env_file(".env")
  135. secrets_command = ["flyctl", "secrets", "set", "-a", app_name] + [f"{k}={v}" for k, v in env_vars.items()]
  136. deploy_command = ["fly", "deploy"]
  137. try:
  138. # Set secrets
  139. console.print(f"🔐 [bold cyan]Setting secrets for {app_name}[/bold cyan]")
  140. subprocess.run(secrets_command, check=True)
  141. # Deploy application
  142. console.print(f"🚀 [bold cyan]Running: {' '.join(deploy_command)}[/bold cyan]")
  143. subprocess.run(deploy_command, check=True)
  144. console.print("✅ [bold green]'fly deploy' executed successfully.[/bold green]")
  145. except subprocess.CalledProcessError as e:
  146. console.print(f"❌ [bold red]An error occurred: {e}[/bold red]")
  147. except FileNotFoundError:
  148. console.print(
  149. "❌ [bold red]'fly' command not found. Please ensure Fly CLI is installed and in your PATH.[/bold red]"
  150. )
  151. def deploy_modal():
  152. modal_deploy_cmd = ["modal", "deploy", "app"]
  153. try:
  154. console.print(f"🚀 [bold cyan]Running: {' '.join(modal_deploy_cmd)}[/bold cyan]")
  155. subprocess.run(modal_deploy_cmd, check=True)
  156. console.print("✅ [bold green]'modal deploy' executed successfully.[/bold green]")
  157. except subprocess.CalledProcessError as e:
  158. console.print(f"❌ [bold red]An error occurred: {e}[/bold red]")
  159. except FileNotFoundError:
  160. console.print(
  161. "❌ [bold red]'modal' command not found. Please ensure Modal CLI is installed and in your PATH.[/bold red]"
  162. )
  163. @cli.command()
  164. def deploy():
  165. # Check for platform-specific files
  166. template = ""
  167. with open("embedchain.json", "r") as file:
  168. embedchain_config = json.load(file)
  169. template = embedchain_config["provider"]
  170. if template == "fly.io":
  171. deploy_fly()
  172. elif template == "modal.com":
  173. deploy_modal()
  174. else:
  175. console.print("❌ [bold red]No recognized deployment platform found.[/bold red]")