CLI Update + Threading (#1046)

This commit is contained in:
Alek Petuskey 2023-05-18 12:34:37 -07:00 committed by GitHub
parent 6dc2778a15
commit d1703f41b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 80 additions and 59 deletions

View File

@ -378,23 +378,13 @@ class App(Base):
):
self.pages[froute(constants.SLUG_404)] = component
def compile(self, force_compile: bool = False):
"""Compile the app and output it to the pages folder.
If the config environment is set to production, the app will
not be compiled.
Args:
force_compile: Whether to force the app to compile.
"""
def compile(self):
"""Compile the app and output it to the pages folder."""
for render, kwargs in DECORATED_ROUTES:
self.add_page(render, **kwargs)
# Get the env mode.
config = get_config()
if config.env != constants.Env.DEV and not force_compile:
print("Skipping compilation in non-dev mode.")
return
# Update models during hot reload.
if config.db_url is not None:

View File

@ -2,6 +2,7 @@
import os
import platform
import threading
from pathlib import Path
import httpx
@ -125,16 +126,15 @@ def run(
telemetry.send(f"run-{env.value}", get_config().telemetry_enabled)
# Run the frontend and backend.
try:
if frontend:
frontend_cmd(app.app, Path.cwd(), frontend_port)
if backend:
backend_cmd(app.__name__, port=int(backend_port), loglevel=loglevel)
finally:
if frontend:
processes.kill_process_on_port(frontend_port)
if backend:
processes.kill_process_on_port(backend_port)
# try:
if backend:
threading.Thread(
target=backend_cmd, args=(app.__name__, backend_port, loglevel)
).start()
if frontend:
threading.Thread(
target=frontend_cmd, args=(app.app, Path.cwd(), frontend_port)
).start()
@cli.command()

View File

@ -77,6 +77,7 @@ def export_app(
frontend: bool = True,
zip: bool = False,
deploy_url: Optional[str] = None,
loglevel: constants.LogLevel = constants.LogLevel.ERROR,
):
"""Zip up the app for deployment.
@ -86,10 +87,8 @@ def export_app(
frontend: Whether to zip up the frontend app.
zip: Whether to zip the app.
deploy_url: The URL of the deployed app.
loglevel: The log level to use.
"""
# Force compile the app.
app.compile(force_compile=True)
# Remove the static folder.
path_ops.rm(constants.WEB_STATIC_DIR)
@ -106,18 +105,17 @@ def export_app(
# Start the progress bar
with progress:
# Run the subprocess command
process = subprocess.Popen(
export_process = subprocess.Popen(
[prerequisites.get_package_manager(), "run", "export"],
cwd=constants.WEB_DIR,
stderr=subprocess.DEVNULL,
env=os.environ,
stderr=subprocess.STDOUT,
stdout=subprocess.PIPE, # Redirect stdout to a pipe
universal_newlines=True, # Set universal_newlines to True for text mode
)
# Read the output of the subprocess line by line
if process.stdout:
for line in iter(process.stdout.readline, ""):
# Update the progress bar based on the output
if export_process.stdout:
for line in iter(export_process.stdout.readline, ""):
if "Linting and checking " in line:
progress.update(task, advance=100)
elif "Compiled successfully" in line:
@ -127,8 +125,15 @@ def export_app(
elif "automatically rendered as static HTML" in line:
progress.update(task, advance=100)
elif "Export successful" in line:
print("DOOE")
progress.update(task, completed=500)
break # Exit the loop if the completion message is found
elif loglevel == constants.LogLevel.DEBUG:
print(line, end="")
# Wait for the subprocess to complete
export_process.wait()
print("Export process completed.")
# Zip up the app.
if zip:

View File

@ -5,11 +5,10 @@ from __future__ import annotations
import os
import platform
import subprocess
from datetime import datetime
from pathlib import Path
from typing import TYPE_CHECKING
import typer
import uvicorn
from rich import print
from pynecone import constants
@ -32,44 +31,58 @@ def start_watching_assets_folder(root):
asset_watch.start()
def run_process_and_launch_url(run_command: list[str], root: Path):
def run_process_and_launch_url(
run_command: list[str],
root: Path,
loglevel: constants.LogLevel = constants.LogLevel.ERROR,
):
"""Run the process and launch the URL.
Args:
run_command: The command to run.
root: root path of the project.
loglevel: The log level to use.
"""
process = subprocess.Popen(
run_command,
cwd=constants.WEB_DIR,
env=os.environ,
stderr=subprocess.DEVNULL,
stderr=subprocess.STDOUT,
stdout=subprocess.PIPE,
universal_newlines=True,
)
message_found = False
current_time = datetime.now()
if process.stdout:
for line in process.stdout:
if "ready started server on" in line:
url = line.split("url: ")[-1].strip()
print(f"App running at: [bold green]{url}")
typer.launch(url)
message_found = True
break
if not message_found and process.stdout:
for line in process.stdout:
print(line, end="")
if (
"Fast Refresh" in line
and (datetime.now() - current_time).total_seconds() > 1
):
print(
f"[yellow][Updating App][/yellow] Applying changes and refreshing. Time: {current_time}"
)
current_time = datetime.now()
elif loglevel == constants.LogLevel.DEBUG:
print(line, end="")
def run_frontend(app: App, root: Path, port: str):
def run_frontend(
app: App,
root: Path,
port: str,
loglevel: constants.LogLevel = constants.LogLevel.ERROR,
):
"""Run the frontend.
Args:
app: The app.
root: root path of the project.
port: port of the app.
loglevel: The log level to use.
"""
# validate bun version
prerequisites.validate_and_install_bun(initialize=False)
@ -77,33 +90,36 @@ def run_frontend(app: App, root: Path, port: str):
# Set up the frontend.
setup_frontend(root)
# start watching asset folder
# Start watching asset folder.
start_watching_assets_folder(root)
# Compile the frontend.
app.compile(force_compile=True)
# Run the frontend in development mode.
console.rule("[bold green]App Running")
os.environ["PORT"] = get_config().port if port is None else port
run_process_and_launch_url(
[prerequisites.get_package_manager(), "run", "dev"], root
[prerequisites.get_package_manager(), "run", "dev"], root, loglevel
)
def run_frontend_prod(app: App, root: Path, port: str):
def run_frontend_prod(
app: App,
root: Path,
port: str,
loglevel: constants.LogLevel = constants.LogLevel.ERROR,
):
"""Run the frontend.
Args:
app: The app.
root: root path of the project.
port: port of the app.
loglevel: The log level to use.
"""
# Set up the frontend.
setup_frontend(root)
# Export the app.
export_app(app)
export_app(app, loglevel=loglevel)
# Set the port.
os.environ["PORT"] = get_config().port if port is None else port
@ -111,7 +127,7 @@ def run_frontend_prod(app: App, root: Path, port: str):
# Run the frontend in production mode.
console.rule("[bold green]App Running")
run_process_and_launch_url(
[prerequisites.get_package_manager(), "run", "prod"], root
[prerequisites.get_package_manager(), "run", "prod"], root, loglevel
)
@ -127,13 +143,23 @@ def run_backend(
"""
setup_backend()
uvicorn.run(
cmd = [
"uvicorn",
f"{app_name}:{constants.APP_VAR}.{constants.API_VAR}",
host=constants.BACKEND_HOST,
port=port,
log_level=loglevel,
reload=True,
)
"--host",
constants.BACKEND_HOST,
"--port",
str(port),
"--log-level",
loglevel,
"--reload",
]
process = subprocess.Popen(cmd)
try:
process.wait()
except KeyboardInterrupt:
process.terminate()
def run_backend_prod(

View File

@ -15,7 +15,7 @@ while ! nc -z localhost 3000 || ! lsof -i :8000 >/dev/null; do
echo "Error: Server process with PID $pid exited early"
break
fi
if ((wait_time >= 300)); then
if ((wait_time >= 500)); then
echo "Error: Timeout waiting for ports 3000 and 8000 to become available"
exit 1
fi