Bun version validation (#1002)
This commit is contained in:
parent
5b3d0a51e8
commit
7e8a4930ba
@ -18,7 +18,7 @@ VERSION = pkg_resources.get_distribution(PACKAGE_NAME).version
|
||||
MIN_NODE_VERSION = "16.6.0"
|
||||
|
||||
# Valid bun versions.
|
||||
MIN_BUN_VERSION = "0.5.8"
|
||||
MIN_BUN_VERSION = "0.5.9"
|
||||
MAX_BUN_VERSION = "0.5.9"
|
||||
INVALID_BUN_VERSIONS = ["0.5.5", "0.5.6", "0.5.7"]
|
||||
|
||||
@ -70,8 +70,10 @@ FRONTEND_PORT = "3000"
|
||||
BACKEND_PORT = "8000"
|
||||
# The backend api url.
|
||||
API_URL = "http://localhost:8000"
|
||||
# bun root location
|
||||
BUN_ROOT_PATH = "$HOME/.bun"
|
||||
# The default path where bun is installed.
|
||||
BUN_PATH = "$HOME/.bun/bin/bun"
|
||||
BUN_PATH = f"{BUN_ROOT_PATH}/bin/bun"
|
||||
# Command to install bun.
|
||||
INSTALL_BUN = f"curl -fsSL https://bun.sh/install | bash -s -- bun-v{MAX_BUN_VERSION}"
|
||||
# Default host in dev mode.
|
||||
|
@ -38,25 +38,25 @@ def init(
|
||||
)
|
||||
raise typer.Exit()
|
||||
|
||||
with console.status(f"[bold]Initializing {app_name}"):
|
||||
# Set up the web directory.
|
||||
prerequisites.install_bun()
|
||||
prerequisites.initialize_web_directory()
|
||||
console.rule(f"[bold]Initializing {app_name}")
|
||||
# Set up the web directory.
|
||||
prerequisites.validate_and_install_bun()
|
||||
prerequisites.initialize_web_directory()
|
||||
|
||||
# Set up the app directory, only if the config doesn't exist.
|
||||
if not os.path.exists(constants.CONFIG_FILE):
|
||||
prerequisites.create_config(app_name)
|
||||
prerequisites.initialize_app_directory(app_name, template)
|
||||
build.set_pynecone_project_hash()
|
||||
telemetry.send("init", get_config().telemetry_enabled)
|
||||
else:
|
||||
build.set_pynecone_project_hash()
|
||||
telemetry.send("reinit", get_config().telemetry_enabled)
|
||||
# Set up the app directory, only if the config doesn't exist.
|
||||
if not os.path.exists(constants.CONFIG_FILE):
|
||||
prerequisites.create_config(app_name)
|
||||
prerequisites.initialize_app_directory(app_name, template)
|
||||
build.set_pynecone_project_hash()
|
||||
telemetry.send("init", get_config().telemetry_enabled)
|
||||
else:
|
||||
build.set_pynecone_project_hash()
|
||||
telemetry.send("reinit", get_config().telemetry_enabled)
|
||||
|
||||
# Initialize the .gitignore.
|
||||
prerequisites.initialize_gitignore()
|
||||
# Finish initializing the app.
|
||||
console.log(f"[bold green]Finished Initializing: {app_name}")
|
||||
# Initialize the .gitignore.
|
||||
prerequisites.initialize_gitignore()
|
||||
# Finish initializing the app.
|
||||
console.log(f"[bold green]Finished Initializing: {app_name}")
|
||||
|
||||
|
||||
@cli.command()
|
||||
|
@ -48,18 +48,21 @@ def rule(title: str) -> None:
|
||||
_console.rule(title)
|
||||
|
||||
|
||||
def ask(question: str, choices: Optional[List[str]] = None) -> str:
|
||||
def ask(
|
||||
question: str, choices: Optional[List[str]] = None, default: Optional[str] = None
|
||||
) -> str:
|
||||
"""Takes a prompt question and optionally a list of choices
|
||||
and returns the user input.
|
||||
|
||||
Args:
|
||||
question (str): The question to ask the user.
|
||||
choices (Optional[List[str]]): A list of choices to select from
|
||||
choices (Optional[List[str]]): A list of choices to select from.
|
||||
default(Optional[str]): The default option selected.
|
||||
|
||||
Returns:
|
||||
A string
|
||||
"""
|
||||
return Prompt.ask(question, choices=choices)
|
||||
return Prompt.ask(question, choices=choices, default=default) # type: ignore
|
||||
|
||||
|
||||
def status(msg: str) -> Status:
|
||||
|
@ -38,6 +38,9 @@ def run_frontend(app: App, root: Path, port: str):
|
||||
root: root path of the project.
|
||||
port: port of the app.
|
||||
"""
|
||||
# validate bun version
|
||||
prerequisites.validate_and_install_bun(initialize=False)
|
||||
|
||||
# Set up the frontend.
|
||||
setup_frontend(root)
|
||||
|
||||
|
@ -52,7 +52,9 @@ def get_bun_version() -> Optional[version.Version]:
|
||||
try:
|
||||
# Run the bun -v command and capture the output
|
||||
result = subprocess.run(
|
||||
["bun", "-v"], stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
||||
[os.path.expandvars(get_config().bun_path), "-v"],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
)
|
||||
return version.parse(result.stdout.decode().strip())
|
||||
except Exception:
|
||||
@ -213,12 +215,16 @@ def initialize_web_directory():
|
||||
json.dump(pynecone_json, f, ensure_ascii=False)
|
||||
|
||||
|
||||
def install_bun():
|
||||
"""Install bun onto the user's system.
|
||||
def validate_and_install_bun(initialize=True):
|
||||
"""Check that bun version requirements are met. If they are not,
|
||||
ask user whether to install required version.
|
||||
|
||||
Args:
|
||||
initialize: whether this function is called on `pc init` or `pc run`.
|
||||
|
||||
Raises:
|
||||
FileNotFoundError: If the required packages are not installed.
|
||||
Exit: If the bun version is not supported.
|
||||
|
||||
"""
|
||||
bun_version = get_bun_version()
|
||||
if bun_version is not None and (
|
||||
@ -229,11 +235,37 @@ def install_bun():
|
||||
console.print(
|
||||
f"""[red]Bun version {bun_version} is not supported by Pynecone. Please change your to bun version to be between {constants.MIN_BUN_VERSION} and {constants.MAX_BUN_VERSION}."""
|
||||
)
|
||||
console.print(
|
||||
f"""[red]Upgrade by running the following command:[/red]\n\n{constants.INSTALL_BUN}"""
|
||||
action = console.ask(
|
||||
"Enter 'yes' to install the latest supported bun version or 'no' to exit.",
|
||||
choices=["yes", "no"],
|
||||
default="no",
|
||||
)
|
||||
raise typer.Exit()
|
||||
|
||||
if action == "yes":
|
||||
remove_existing_bun_installation()
|
||||
install_bun()
|
||||
return
|
||||
else:
|
||||
raise typer.Exit()
|
||||
|
||||
if initialize:
|
||||
install_bun()
|
||||
|
||||
|
||||
def remove_existing_bun_installation():
|
||||
"""Remove existing bun installation."""
|
||||
package_manager = get_package_manager()
|
||||
if os.path.exists(package_manager):
|
||||
console.log("Removing bun...")
|
||||
path_ops.rm(os.path.expandvars(constants.BUN_ROOT_PATH))
|
||||
|
||||
|
||||
def install_bun():
|
||||
"""Install bun onto the user's system.
|
||||
|
||||
Raises:
|
||||
FileNotFoundError: if unzip or curl packages are not found.
|
||||
"""
|
||||
# Bun is not supported on Windows.
|
||||
if platform.system() == "Windows":
|
||||
console.log("Skipping bun installation on Windows.")
|
||||
|
@ -2,10 +2,16 @@ import typing
|
||||
from typing import Any, List, Union
|
||||
|
||||
import pytest
|
||||
from packaging import version
|
||||
|
||||
from pynecone.utils import build, format, imports, prerequisites, types
|
||||
from pynecone.vars import Var
|
||||
|
||||
V055 = version.parse("0.5.5")
|
||||
V059 = version.parse("0.5.9")
|
||||
V056 = version.parse("0.5.6")
|
||||
V0510 = version.parse("0.5.10")
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"input,output",
|
||||
@ -231,6 +237,78 @@ def test_format_route(route: str, expected: bool):
|
||||
assert format.format_route(route) == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"bun_version,is_valid, prompt_input",
|
||||
[
|
||||
(V055, False, "yes"),
|
||||
(V059, True, None),
|
||||
(V0510, False, "yes"),
|
||||
],
|
||||
)
|
||||
def test_bun_validate_and_install(mocker, bun_version, is_valid, prompt_input):
|
||||
"""Test that the bun version on host system is validated properly. Also test that
|
||||
the required bun version is installed should the user opt for it.
|
||||
|
||||
Args:
|
||||
mocker: Pytest mocker object.
|
||||
bun_version: The bun version.
|
||||
is_valid: Whether bun version is valid for running pynecone.
|
||||
prompt_input: The input from user on whether to install bun.
|
||||
"""
|
||||
mocker.patch(
|
||||
"pynecone.utils.prerequisites.get_bun_version", return_value=bun_version
|
||||
)
|
||||
mocker.patch("pynecone.utils.prerequisites.console.ask", return_value=prompt_input)
|
||||
|
||||
bun_install = mocker.patch("pynecone.utils.prerequisites.install_bun")
|
||||
remove_existing_bun_installation = mocker.patch(
|
||||
"pynecone.utils.prerequisites.remove_existing_bun_installation"
|
||||
)
|
||||
|
||||
prerequisites.validate_and_install_bun()
|
||||
if not is_valid:
|
||||
remove_existing_bun_installation.assert_called_once()
|
||||
bun_install.assert_called_once()
|
||||
|
||||
|
||||
def test_bun_validation_exception(mocker):
|
||||
"""Test that an exception is thrown and program exists when user selects no when asked
|
||||
whether to install bun or not.
|
||||
|
||||
Args:
|
||||
mocker: Pytest mocker.
|
||||
"""
|
||||
mocker.patch("pynecone.utils.prerequisites.get_bun_version", return_value=V056)
|
||||
mocker.patch("pynecone.utils.prerequisites.console.ask", return_value="no")
|
||||
|
||||
with pytest.raises(RuntimeError):
|
||||
prerequisites.validate_and_install_bun()
|
||||
|
||||
|
||||
def test_remove_existing_bun_installation(mocker, tmp_path):
|
||||
"""Test that existing bun installation is removed.
|
||||
|
||||
Args:
|
||||
mocker: Pytest mocker.
|
||||
tmp_path: test path.
|
||||
"""
|
||||
bun_location = tmp_path / ".bun"
|
||||
bun_location.mkdir()
|
||||
|
||||
mocker.patch(
|
||||
"pynecone.utils.prerequisites.get_package_manager",
|
||||
return_value=str(bun_location),
|
||||
)
|
||||
mocker.patch(
|
||||
"pynecone.utils.prerequisites.os.path.expandvars",
|
||||
return_value=str(bun_location),
|
||||
)
|
||||
|
||||
prerequisites.remove_existing_bun_installation()
|
||||
|
||||
assert not bun_location.exists()
|
||||
|
||||
|
||||
def test_setup_frontend(tmp_path, mocker):
|
||||
"""Test checking if assets content have been
|
||||
copied into the .web/public folder.
|
||||
|
Loading…
Reference in New Issue
Block a user