pc.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. """Pynecone CLI to create, run, and deploy apps."""
  2. import os
  3. import httpx
  4. import typer
  5. from pynecone import constants, utils
  6. # Create the app.
  7. cli = typer.Typer()
  8. @cli.command()
  9. def version():
  10. """Get the Pynecone version."""
  11. utils.console.print(constants.VERSION)
  12. @cli.command()
  13. def init():
  14. """Initialize a new Pynecone app in the current directory."""
  15. app_name = utils.get_default_app_name()
  16. # Make sure they don't name the app "pynecone".
  17. if app_name == constants.MODULE_NAME:
  18. utils.console.print(
  19. f"[red]The app directory cannot be named [bold]{constants.MODULE_NAME}."
  20. )
  21. raise typer.Exit()
  22. with utils.console.status(f"[bold]Initializing {app_name}"):
  23. # Set up the web directory.
  24. utils.install_bun()
  25. utils.initialize_web_directory()
  26. # Set up the app directory, only if the config doesn't exist.
  27. if not os.path.exists(constants.CONFIG_FILE):
  28. utils.create_config(app_name)
  29. utils.initialize_app_directory(app_name)
  30. # Initialize the .gitignore.
  31. utils.initialize_gitignore()
  32. # Finish initializing the app.
  33. utils.console.log(f"[bold green]Finished Initializing: {app_name}")
  34. @cli.command()
  35. def run(
  36. env: constants.Env = typer.Option(
  37. constants.Env.DEV, help="The environment to run the app in."
  38. ),
  39. frontend: bool = typer.Option(True, help="Whether to run the frontend."),
  40. backend: bool = typer.Option(True, help="Whether to run the backend."),
  41. loglevel: constants.LogLevel = typer.Option(
  42. constants.LogLevel.ERROR, help="The log level to use."
  43. ),
  44. ):
  45. """Run the app in the current directory."""
  46. # Check that the app is initialized.
  47. if frontend and not utils.is_initialized():
  48. utils.console.print(
  49. "[red]The app is not initialized. Run [bold]pc init[/bold] first."
  50. )
  51. raise typer.Exit()
  52. # Check that the template is up to date.
  53. if frontend and not utils.is_latest_template():
  54. utils.console.print(
  55. "[red]The base app template has updated. Run [bold]pc init[/bold] again."
  56. )
  57. raise typer.Exit()
  58. # Get the app module.
  59. utils.console.rule("[bold]Starting Pynecone App")
  60. app = utils.get_app()
  61. # Get the frontend and backend commands, based on the environment.
  62. frontend_cmd = backend_cmd = None
  63. if env == constants.Env.DEV:
  64. frontend_cmd, backend_cmd = utils.run_frontend, utils.run_backend
  65. if env == constants.Env.PROD:
  66. frontend_cmd, backend_cmd = utils.run_frontend_prod, utils.run_backend_prod
  67. assert frontend_cmd and backend_cmd, "Invalid env"
  68. # Run the frontend and backend.
  69. if frontend:
  70. frontend_cmd(app.app)
  71. if backend:
  72. backend_cmd(app.__name__, loglevel=loglevel)
  73. @cli.command()
  74. def deploy(dry_run: bool = typer.Option(False, help="Whether to run a dry run.")):
  75. """Deploy the app to the Pynecone hosting service."""
  76. # Get the app config.
  77. config = utils.get_config()
  78. config.api_url = utils.get_production_backend_url()
  79. # Check if the deploy url is set.
  80. if config.deploy_url is None:
  81. typer.echo("This feature is coming soon!")
  82. return
  83. # Compile the app in production mode.
  84. typer.echo("Compiling production app")
  85. app = utils.get_app().app
  86. utils.export_app(app, zip=True)
  87. # Exit early if this is a dry run.
  88. if dry_run:
  89. return
  90. # Deploy the app.
  91. data = {"userId": config.username, "projectId": config.app_name}
  92. original_response = httpx.get(config.deploy_url, params=data)
  93. response = original_response.json()
  94. print("response", response)
  95. frontend = response["frontend_resources_url"]
  96. backend = response["backend_resources_url"]
  97. # Upload the frontend and backend.
  98. with open(constants.FRONTEND_ZIP, "rb") as f:
  99. response = httpx.put(frontend, data=f) # type: ignore
  100. with open(constants.BACKEND_ZIP, "rb") as f:
  101. response = httpx.put(backend, data=f) # type: ignore
  102. main = cli
  103. if __name__ == "__main__":
  104. main()