瀏覽代碼

Reinstate bun path (#1552)

Elijah Ahianyo 1 年之前
父節點
當前提交
ed4de0d7ba
共有 4 個文件被更改,包括 73 次插入53 次删除
  1. 3 0
      reflex/config.py
  2. 5 1
      reflex/constants.py
  3. 39 28
      reflex/utils/prerequisites.py
  4. 26 24
      tests/test_utils.py

+ 3 - 0
reflex/config.py

@@ -168,6 +168,9 @@ class Config(Base):
     # Additional frontend packages to install.
     frontend_packages: List[str] = []
 
+    # The bun path
+    bun_path: str = constants.BUN_PATH
+
     # The Admin Dash.
     admin_dash: Optional[AdminDash] = None
 

+ 5 - 1
reflex/constants.py

@@ -65,10 +65,14 @@ JINJA_TEMPLATE_DIR = os.path.join(TEMPLATE_DIR, "jinja")
 # Bun config.
 # The Bun version.
 BUN_VERSION = "0.7.0"
+# Min Bun Version
+MIN_BUN_VERSION = "0.7.0"
 # The directory to store the bun.
 BUN_ROOT_PATH = os.path.join(REFLEX_DIR, ".bun")
+# Default bun path.
+DEFAULT_BUN_PATH = os.path.join(BUN_ROOT_PATH, "bin", "bun")
 # The bun path.
-BUN_PATH = os.path.join(BUN_ROOT_PATH, "bin", "bun")
+BUN_PATH = get_value("BUN_PATH", DEFAULT_BUN_PATH)
 # URL to bun install script.
 BUN_INSTALL_URL = "https://bun.sh/install"
 

+ 39 - 28
reflex/utils/prerequisites.py

@@ -57,7 +57,7 @@ def get_bun_version() -> Optional[version.Version]:
     """
     try:
         # Run the bun -v command and capture the output
-        result = processes.new_process([constants.BUN_PATH, "-v"], run=True)
+        result = processes.new_process([get_config().bun_path, "-v"], run=True)
         return version.parse(result.stdout)  # type: ignore
     except FileNotFoundError:
         return None
@@ -92,7 +92,7 @@ def get_install_package_manager() -> str:
         return get_windows_package_manager()
 
     # On other platforms, we use bun.
-    return constants.BUN_PATH
+    return get_config().bun_path
 
 
 def get_package_manager() -> str:
@@ -245,36 +245,13 @@ def initialize_web_directory():
         json.dump(reflex_json, f, ensure_ascii=False)
 
 
-def initialize_bun():
-    """Check that bun requirements are met, and install if not."""
-    if IS_WINDOWS:
-        # Bun is not supported on Windows.
-        console.debug("Skipping bun installation on Windows.")
-        return
-
-    # Check the bun version.
-    bun_version = get_bun_version()
-    if bun_version != version.parse(constants.BUN_VERSION):
-        console.debug(
-            f"Current bun version ({bun_version}) does not match ({constants.BUN_VERSION})."
-        )
-        remove_existing_bun_installation()
-        install_bun()
-
-
 def remove_existing_bun_installation():
     """Remove existing bun installation."""
     console.debug("Removing existing bun installation.")
-    if os.path.exists(constants.BUN_PATH):
+    if os.path.exists(get_config().bun_path):
         path_ops.rm(constants.BUN_ROOT_PATH)
 
 
-def initialize_node():
-    """Validate nodejs have install or not."""
-    if not check_node_version():
-        install_node()
-
-
 def download_and_run(url: str, *args, show_status: bool = False, **env):
     """Download and run a script.
 
@@ -352,7 +329,7 @@ def install_bun():
         return
 
     # Skip if bun is already installed.
-    if os.path.exists(constants.BUN_PATH):
+    if os.path.exists(get_config().bun_path):
         console.debug("Skipping bun installation as it is already installed.")
         return
 
@@ -435,12 +412,46 @@ def is_latest_template() -> bool:
     return app_version == constants.VERSION
 
 
+def validate_bun():
+    """Validate bun if a custom bun path is specified to ensure the bun version meets requirements.
+
+    Raises:
+        Exit: If custom specified bun does not exist or does not meet requirements.
+    """
+    # if a custom bun path is provided, make sure its valid
+    # This is specific to non-FHS OS
+    bun_path = get_config().bun_path
+    if bun_path != constants.DEFAULT_BUN_PATH:
+        bun_version = get_bun_version()
+        if not bun_version:
+            console.error(
+                "Failed to obtain bun version. Make sure the specified bun path in your config is correct."
+            )
+            raise typer.Exit(1)
+        elif bun_version < version.parse(constants.MIN_BUN_VERSION):
+            console.error(
+                f"Reflex requires bun version {constants.BUN_VERSION} or higher to run, but the detected version is "
+                f"{bun_version}. If you have specified a custom bun path in your config, make sure to provide one "
+                f"that satisfies the minimum version requirement."
+            )
+
+            raise typer.Exit(1)
+
+
+def validate_frontend_dependencies():
+    """Validate frontend dependencies to ensure they meet requirements."""
+    if IS_WINDOWS:
+        return
+    return validate_bun()
+
+
 def initialize_frontend_dependencies():
     """Initialize all the frontend dependencies."""
     # Create the reflex directory.
     if not IS_WINDOWS:
         path_ops.mkdir(constants.REFLEX_DIR)
-
+    # validate dependencies before install
+    validate_frontend_dependencies()
     # Install the frontend dependencies.
     processes.run_concurrently(install_node, install_bun)
 

+ 26 - 24
tests/test_utils.py

@@ -255,36 +255,38 @@ 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),
-        (VMAXPLUS1, False, "yes"),
-    ],
-)
-def test_initialize_bun(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.
+def test_validate_invalid_bun_path(mocker):
+    """Test that an error is thrown when a custom specified bun path is not valid
+    or does not exist.
 
     Args:
         mocker: Pytest mocker object.
-        bun_version: The bun version.
-        is_valid: Whether bun version is valid for running reflex.
-        prompt_input: The input from user on whether to install bun.
     """
-    mocker.patch("reflex.utils.prerequisites.get_bun_version", return_value=bun_version)
-    mocker.patch("reflex.utils.prerequisites.IS_WINDOWS", False)
+    mock = mocker.Mock()
+    mocker.patch.object(mock, "bun_path", return_value="/mock/path")
+    mocker.patch("reflex.utils.prerequisites.get_config", mock)
+    mocker.patch("reflex.utils.prerequisites.get_bun_version", return_value=None)
+
+    with pytest.raises(typer.Exit):
+        prerequisites.validate_bun()
 
-    bun_install = mocker.patch("reflex.utils.prerequisites.install_bun")
-    remove_existing_bun_installation = mocker.patch(
-        "reflex.utils.prerequisites.remove_existing_bun_installation"
+
+def test_validate_bun_path_incompatible_version(mocker):
+    """Test that an error is thrown when the bun version does not meet minimum requirements.
+
+    Args:
+        mocker: Pytest mocker object.
+    """
+    mock = mocker.Mock()
+    mocker.patch.object(mock, "bun_path", return_value="/mock/path")
+    mocker.patch("reflex.utils.prerequisites.get_config", mock)
+    mocker.patch(
+        "reflex.utils.prerequisites.get_bun_version",
+        return_value=version.parse("0.6.5"),
     )
 
-    prerequisites.initialize_bun()
-    if not is_valid:
-        remove_existing_bun_installation.assert_called_once()
-    bun_install.assert_called_once()
+    with pytest.raises(typer.Exit):
+        prerequisites.validate_bun()
 
 
 def test_remove_existing_bun_installation(mocker):
@@ -521,7 +523,7 @@ def test_node_install_windows(mocker):
     mocker.patch("reflex.utils.prerequisites.check_node_version", return_value=False)
 
     with pytest.raises(typer.Exit):
-        prerequisites.initialize_node()
+        prerequisites.install_node()
 
 
 def test_node_install_unix(tmp_path, mocker):