build.py 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. """Building the app and initializing all prerequisites."""
  2. from __future__ import annotations
  3. import json
  4. import os
  5. import random
  6. import subprocess
  7. from pathlib import Path
  8. from typing import (
  9. TYPE_CHECKING,
  10. Optional,
  11. )
  12. from pynecone import constants
  13. from pynecone.config import get_config
  14. from pynecone.utils import path_ops, prerequisites
  15. if TYPE_CHECKING:
  16. from pynecone.app import App
  17. def set_pynecone_project_hash():
  18. """Write the hash of the Pynecone project to a PCVERSION_APP_FILE."""
  19. with open(constants.PCVERSION_APP_FILE) as f: # type: ignore
  20. pynecone_json = json.load(f)
  21. pynecone_json["project_hash"] = random.getrandbits(128)
  22. with open(constants.PCVERSION_APP_FILE, "w") as f:
  23. json.dump(pynecone_json, f, ensure_ascii=False)
  24. def generate_sitemap(deploy_url: str):
  25. """Generate the sitemap config file.
  26. Args:
  27. deploy_url: The URL of the deployed app.
  28. """
  29. # Import here to avoid circular imports.
  30. from pynecone.compiler import templates
  31. config = json.dumps(
  32. {
  33. "siteUrl": deploy_url,
  34. "generateRobotsTxt": True,
  35. }
  36. )
  37. with open(constants.SITEMAP_CONFIG_FILE, "w") as f:
  38. f.write(templates.SITEMAP_CONFIG(config=config))
  39. def export_app(
  40. app: App,
  41. backend: bool = True,
  42. frontend: bool = True,
  43. zip: bool = False,
  44. deploy_url: Optional[str] = None,
  45. ):
  46. """Zip up the app for deployment.
  47. Args:
  48. app: The app.
  49. backend: Whether to zip up the backend app.
  50. frontend: Whether to zip up the frontend app.
  51. zip: Whether to zip the app.
  52. deploy_url: The URL of the deployed app.
  53. """
  54. # Force compile the app.
  55. app.compile(force_compile=True)
  56. # Remove the static folder.
  57. path_ops.rm(constants.WEB_STATIC_DIR)
  58. # Generate the sitemap file.
  59. if deploy_url is not None:
  60. generate_sitemap(deploy_url)
  61. # Export the Next app.
  62. subprocess.run(
  63. [prerequisites.get_package_manager(), "run", "export"], cwd=constants.WEB_DIR
  64. )
  65. # Zip up the app.
  66. if zip:
  67. if os.name == "posix":
  68. posix_export(backend, frontend)
  69. if os.name == "nt":
  70. nt_export(backend, frontend)
  71. def nt_export(backend: bool = True, frontend: bool = True):
  72. """Export for nt (Windows) systems.
  73. Args:
  74. backend: Whether to zip up the backend app.
  75. frontend: Whether to zip up the frontend app.
  76. """
  77. cmd = r""
  78. if frontend:
  79. cmd = r'''powershell -Command "Set-Location .web/_static; Compress-Archive -Path .\* -DestinationPath ..\..\frontend.zip -Force"'''
  80. os.system(cmd)
  81. if backend:
  82. cmd = r'''powershell -Command "Get-ChildItem -File | Where-Object { $_.Name -notin @('.web', 'assets', 'frontend.zip', 'backend.zip') } | Compress-Archive -DestinationPath backend.zip -Update"'''
  83. os.system(cmd)
  84. def posix_export(backend: bool = True, frontend: bool = True):
  85. """Export for posix (Linux, OSX) systems.
  86. Args:
  87. backend: Whether to zip up the backend app.
  88. frontend: Whether to zip up the frontend app.
  89. """
  90. cmd = r""
  91. if frontend:
  92. cmd = r"cd .web/_static && zip -r ../../frontend.zip ./*"
  93. os.system(cmd)
  94. if backend:
  95. cmd = r"zip -r backend.zip ./* -x .web/\* ./assets\* ./frontend.zip\* ./backend.zip\*"
  96. os.system(cmd)
  97. def setup_frontend(root: Path, disable_telemetry: bool = True):
  98. """Set up the frontend.
  99. Args:
  100. root: root path of the project.
  101. disable_telemetry: Whether to disable the Next telemetry.
  102. """
  103. # Initialize the web directory if it doesn't exist.
  104. web_dir = prerequisites.create_web_directory(root)
  105. # Install frontend packages.
  106. prerequisites.install_frontend_packages(web_dir)
  107. # Copy asset files to public folder.
  108. path_ops.mkdir(str(root / constants.WEB_ASSETS_DIR))
  109. path_ops.cp(
  110. src=str(root / constants.APP_ASSETS_DIR),
  111. dest=str(root / constants.WEB_ASSETS_DIR),
  112. )
  113. # Disable the Next telemetry.
  114. if disable_telemetry:
  115. subprocess.Popen(
  116. [
  117. prerequisites.get_package_manager(),
  118. "run",
  119. "next",
  120. "telemetry",
  121. "disable",
  122. ],
  123. cwd=constants.WEB_DIR,
  124. env=os.environ,
  125. stdout=subprocess.DEVNULL,
  126. stderr=subprocess.STDOUT,
  127. )
  128. def setup_backend():
  129. """Set up backend.
  130. Specifically ensures backend database is updated when running --no-frontend.
  131. """
  132. # Import here to avoid circular imports.
  133. from pynecone.model import Model
  134. config = get_config()
  135. if config.db_url is not None:
  136. Model.create_all()