[REF-1586] Use bun as a package manager on windows (#2359)
This commit is contained in:
parent
e377ce70b6
commit
3c8c7c3c46
@ -4,6 +4,7 @@ from .base import (
|
|||||||
COOKIES,
|
COOKIES,
|
||||||
ENV_MODE_ENV_VAR,
|
ENV_MODE_ENV_VAR,
|
||||||
IS_WINDOWS,
|
IS_WINDOWS,
|
||||||
|
IS_WINDOWS_BUN_SUPPORTED_MACHINE, # type: ignore
|
||||||
LOCAL_STORAGE,
|
LOCAL_STORAGE,
|
||||||
POLLING_MAX_HTTP_BUFFER_SIZE,
|
POLLING_MAX_HTTP_BUFFER_SIZE,
|
||||||
PYTEST_CURRENT_TEST,
|
PYTEST_CURRENT_TEST,
|
||||||
@ -86,6 +87,7 @@ __ALL__ = [
|
|||||||
Hooks,
|
Hooks,
|
||||||
Imports,
|
Imports,
|
||||||
IS_WINDOWS,
|
IS_WINDOWS,
|
||||||
|
IS_WINDOWS_BUN_SUPPORTED_MACHINE,
|
||||||
LOCAL_STORAGE,
|
LOCAL_STORAGE,
|
||||||
LogLevel,
|
LogLevel,
|
||||||
MemoizationDisposition,
|
MemoizationDisposition,
|
||||||
|
@ -11,6 +11,11 @@ from types import SimpleNamespace
|
|||||||
from platformdirs import PlatformDirs
|
from platformdirs import PlatformDirs
|
||||||
|
|
||||||
IS_WINDOWS = platform.system() == "Windows"
|
IS_WINDOWS = platform.system() == "Windows"
|
||||||
|
# https://github.com/oven-sh/bun/blob/main/src/cli/install.ps1
|
||||||
|
IS_WINDOWS_BUN_SUPPORTED_MACHINE = IS_WINDOWS and platform.machine() in [
|
||||||
|
"AMD64",
|
||||||
|
"x86_64",
|
||||||
|
] # filter out 32 bit + ARM
|
||||||
|
|
||||||
|
|
||||||
class Dirs(SimpleNamespace):
|
class Dirs(SimpleNamespace):
|
||||||
|
@ -26,6 +26,8 @@ class Ext(SimpleNamespace):
|
|||||||
CSS = ".css"
|
CSS = ".css"
|
||||||
# The extension for zip files.
|
# The extension for zip files.
|
||||||
ZIP = ".zip"
|
ZIP = ".zip"
|
||||||
|
# The extension for executable files on Windows.
|
||||||
|
EXE = ".exe"
|
||||||
|
|
||||||
|
|
||||||
class CompileVars(SimpleNamespace):
|
class CompileVars(SimpleNamespace):
|
||||||
|
@ -35,13 +35,15 @@ class Bun(SimpleNamespace):
|
|||||||
"""Bun constants."""
|
"""Bun constants."""
|
||||||
|
|
||||||
# The Bun version.
|
# The Bun version.
|
||||||
VERSION = "1.0.13"
|
VERSION = "1.1.3"
|
||||||
# Min Bun Version
|
# Min Bun Version
|
||||||
MIN_VERSION = "0.7.0"
|
MIN_VERSION = "0.7.0"
|
||||||
# The directory to store the bun.
|
# The directory to store the bun.
|
||||||
ROOT_PATH = os.path.join(Reflex.DIR, "bun")
|
ROOT_PATH = os.path.join(Reflex.DIR, "bun")
|
||||||
# Default bun path.
|
# Default bun path.
|
||||||
DEFAULT_PATH = os.path.join(ROOT_PATH, "bin", "bun")
|
DEFAULT_PATH = os.path.join(
|
||||||
|
ROOT_PATH, "bin", "bun" if not IS_WINDOWS else "bun.exe"
|
||||||
|
)
|
||||||
# URL to bun install script.
|
# URL to bun install script.
|
||||||
INSTALL_URL = "https://bun.sh/install"
|
INSTALL_URL = "https://bun.sh/install"
|
||||||
|
|
||||||
|
@ -28,6 +28,15 @@ def set_log_level(log_level: LogLevel):
|
|||||||
_LOG_LEVEL = log_level
|
_LOG_LEVEL = log_level
|
||||||
|
|
||||||
|
|
||||||
|
def is_debug() -> bool:
|
||||||
|
"""Check if the log level is debug.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True if the log level is debug.
|
||||||
|
"""
|
||||||
|
return _LOG_LEVEL <= LogLevel.DEBUG
|
||||||
|
|
||||||
|
|
||||||
def print(msg: str, **kwargs):
|
def print(msg: str, **kwargs):
|
||||||
"""Print a message.
|
"""Print a message.
|
||||||
|
|
||||||
@ -45,7 +54,7 @@ def debug(msg: str, **kwargs):
|
|||||||
msg: The debug message.
|
msg: The debug message.
|
||||||
kwargs: Keyword arguments to pass to the print function.
|
kwargs: Keyword arguments to pass to the print function.
|
||||||
"""
|
"""
|
||||||
if _LOG_LEVEL <= LogLevel.DEBUG:
|
if is_debug():
|
||||||
msg_ = f"[blue]Debug: {msg}[/blue]"
|
msg_ = f"[blue]Debug: {msg}[/blue]"
|
||||||
if progress := kwargs.pop("progress", None):
|
if progress := kwargs.pop("progress", None):
|
||||||
progress.console.print(msg_, **kwargs)
|
progress.console.print(msg_, **kwargs)
|
||||||
|
@ -167,16 +167,13 @@ def get_bun_version() -> version.Version | None:
|
|||||||
|
|
||||||
def get_install_package_manager() -> str | None:
|
def get_install_package_manager() -> str | None:
|
||||||
"""Get the package manager executable for installation.
|
"""Get the package manager executable for installation.
|
||||||
Currently on unix systems, bun is used for installation only.
|
Currently, bun is used for installation only.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The path to the package manager.
|
The path to the package manager.
|
||||||
"""
|
"""
|
||||||
# On Windows, we use npm instead of bun.
|
if constants.IS_WINDOWS and not constants.IS_WINDOWS_BUN_SUPPORTED_MACHINE:
|
||||||
if constants.IS_WINDOWS:
|
|
||||||
return get_package_manager()
|
return get_package_manager()
|
||||||
|
|
||||||
# On other platforms, we use bun.
|
|
||||||
return get_config().bun_path
|
return get_config().bun_path
|
||||||
|
|
||||||
|
|
||||||
@ -729,10 +726,10 @@ def install_bun():
|
|||||||
Raises:
|
Raises:
|
||||||
FileNotFoundError: If required packages are not found.
|
FileNotFoundError: If required packages are not found.
|
||||||
"""
|
"""
|
||||||
# Bun is not supported on Windows.
|
if constants.IS_WINDOWS and not constants.IS_WINDOWS_BUN_SUPPORTED_MACHINE:
|
||||||
if constants.IS_WINDOWS:
|
console.warn(
|
||||||
console.debug("Skipping bun installation on Windows.")
|
"Bun for Windows is currently only available for x86 64-bit Windows. Installation will fall back on npm."
|
||||||
return
|
)
|
||||||
|
|
||||||
# Skip if bun is already installed.
|
# Skip if bun is already installed.
|
||||||
if os.path.exists(get_config().bun_path) and get_bun_version() == version.parse(
|
if os.path.exists(get_config().bun_path) and get_bun_version() == version.parse(
|
||||||
@ -742,16 +739,25 @@ def install_bun():
|
|||||||
return
|
return
|
||||||
|
|
||||||
# if unzip is installed
|
# if unzip is installed
|
||||||
unzip_path = path_ops.which("unzip")
|
if constants.IS_WINDOWS:
|
||||||
if unzip_path is None:
|
processes.new_process(
|
||||||
raise FileNotFoundError("Reflex requires unzip to be installed.")
|
["powershell", "-c", f"irm {constants.Bun.INSTALL_URL}.ps1|iex"],
|
||||||
|
env={"BUN_INSTALL": constants.Bun.ROOT_PATH},
|
||||||
|
shell=True,
|
||||||
|
run=True,
|
||||||
|
show_logs=console.is_debug(),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
unzip_path = path_ops.which("unzip")
|
||||||
|
if unzip_path is None:
|
||||||
|
raise FileNotFoundError("Reflex requires unzip to be installed.")
|
||||||
|
|
||||||
# Run the bun install script.
|
# Run the bun install script.
|
||||||
download_and_run(
|
download_and_run(
|
||||||
constants.Bun.INSTALL_URL,
|
constants.Bun.INSTALL_URL,
|
||||||
f"bun-v{constants.Bun.VERSION}",
|
f"bun-v{constants.Bun.VERSION}",
|
||||||
BUN_INSTALL=constants.Bun.ROOT_PATH,
|
BUN_INSTALL=constants.Bun.ROOT_PATH,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _write_cached_procedure_file(payload: str, cache_file: str):
|
def _write_cached_procedure_file(payload: str, cache_file: str):
|
||||||
@ -813,18 +819,22 @@ def install_frontend_packages(packages: set[str], config: Config):
|
|||||||
Example:
|
Example:
|
||||||
>>> install_frontend_packages(["react", "react-dom"], get_config())
|
>>> install_frontend_packages(["react", "react-dom"], get_config())
|
||||||
"""
|
"""
|
||||||
# Install the base packages.
|
# unsupported archs will use npm anyway. so we dont have to run npm twice
|
||||||
process = processes.new_process(
|
fallback_command = (
|
||||||
|
get_package_manager()
|
||||||
|
if constants.IS_WINDOWS and constants.IS_WINDOWS_BUN_SUPPORTED_MACHINE
|
||||||
|
else None
|
||||||
|
)
|
||||||
|
processes.run_process_with_fallback(
|
||||||
[get_install_package_manager(), "install", "--loglevel", "silly"],
|
[get_install_package_manager(), "install", "--loglevel", "silly"],
|
||||||
|
fallback=fallback_command,
|
||||||
|
show_status_message="Installing base frontend packages",
|
||||||
cwd=constants.Dirs.WEB,
|
cwd=constants.Dirs.WEB,
|
||||||
shell=constants.IS_WINDOWS,
|
shell=constants.IS_WINDOWS,
|
||||||
)
|
)
|
||||||
|
|
||||||
processes.show_status("Installing base frontend packages", process)
|
|
||||||
|
|
||||||
if config.tailwind is not None:
|
if config.tailwind is not None:
|
||||||
# install tailwind and tailwind plugins as dev dependencies.
|
processes.run_process_with_fallback(
|
||||||
process = processes.new_process(
|
|
||||||
[
|
[
|
||||||
get_install_package_manager(),
|
get_install_package_manager(),
|
||||||
"add",
|
"add",
|
||||||
@ -832,21 +842,21 @@ def install_frontend_packages(packages: set[str], config: Config):
|
|||||||
constants.Tailwind.VERSION,
|
constants.Tailwind.VERSION,
|
||||||
*((config.tailwind or {}).get("plugins", [])),
|
*((config.tailwind or {}).get("plugins", [])),
|
||||||
],
|
],
|
||||||
|
fallback=fallback_command,
|
||||||
|
show_status_message="Installing tailwind",
|
||||||
cwd=constants.Dirs.WEB,
|
cwd=constants.Dirs.WEB,
|
||||||
shell=constants.IS_WINDOWS,
|
shell=constants.IS_WINDOWS,
|
||||||
)
|
)
|
||||||
processes.show_status("Installing tailwind", process)
|
|
||||||
|
|
||||||
# Install custom packages defined in frontend_packages
|
# Install custom packages defined in frontend_packages
|
||||||
if len(packages) > 0:
|
if len(packages) > 0:
|
||||||
process = processes.new_process(
|
processes.run_process_with_fallback(
|
||||||
[get_install_package_manager(), "add", *packages],
|
[get_install_package_manager(), "add", *packages],
|
||||||
|
fallback=fallback_command,
|
||||||
|
show_status_message="Installing frontend packages from config and components",
|
||||||
cwd=constants.Dirs.WEB,
|
cwd=constants.Dirs.WEB,
|
||||||
shell=constants.IS_WINDOWS,
|
shell=constants.IS_WINDOWS,
|
||||||
)
|
)
|
||||||
processes.show_status(
|
|
||||||
"Installing frontend packages from config and components", process
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def needs_reinit(frontend: bool = True) -> bool:
|
def needs_reinit(frontend: bool = True) -> bool:
|
||||||
@ -953,9 +963,6 @@ def validate_frontend_dependencies(init=True):
|
|||||||
)
|
)
|
||||||
raise typer.Exit(1)
|
raise typer.Exit(1)
|
||||||
|
|
||||||
if constants.IS_WINDOWS:
|
|
||||||
return
|
|
||||||
|
|
||||||
if init:
|
if init:
|
||||||
# we only need bun for package install on `reflex init`.
|
# we only need bun for package install on `reflex init`.
|
||||||
validate_bun()
|
validate_bun()
|
||||||
|
@ -287,3 +287,40 @@ def show_progress(message: str, process: subprocess.Popen, checkpoints: List[str
|
|||||||
def atexit_handler():
|
def atexit_handler():
|
||||||
"""Display a custom message with the current time when exiting an app."""
|
"""Display a custom message with the current time when exiting an app."""
|
||||||
console.log("Reflex app stopped.")
|
console.log("Reflex app stopped.")
|
||||||
|
|
||||||
|
|
||||||
|
def run_process_with_fallback(args, *, show_status_message, fallback=None, **kwargs):
|
||||||
|
"""Run subprocess and retry using fallback command if initial command fails.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
args: A string, or a sequence of program arguments.
|
||||||
|
show_status_message: The status message to be displayed in the console.
|
||||||
|
fallback: The fallback command to run.
|
||||||
|
kwargs: Kwargs to pass to new_process function.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def execute_process(process):
|
||||||
|
if not constants.IS_WINDOWS:
|
||||||
|
show_status(show_status_message, process)
|
||||||
|
else:
|
||||||
|
process.wait()
|
||||||
|
if process.returncode != 0:
|
||||||
|
error_output = process.stderr if process.stderr else process.stdout
|
||||||
|
error_message = f"Error occurred during subprocess execution: {' '.join(args)}\n{error_output.read() if error_output else ''}"
|
||||||
|
# Only show error in debug mode.
|
||||||
|
if console.is_debug():
|
||||||
|
console.error(error_message)
|
||||||
|
|
||||||
|
# retry with fallback command.
|
||||||
|
fallback_args = [fallback, *args[1:]] if fallback else None
|
||||||
|
console.warn(
|
||||||
|
f"There was an error running command: {args}. Falling back to: {fallback_args}."
|
||||||
|
)
|
||||||
|
if fallback_args:
|
||||||
|
process = new_process(fallback_args, **kwargs)
|
||||||
|
execute_process(process)
|
||||||
|
else:
|
||||||
|
show_status(show_status_message, process)
|
||||||
|
|
||||||
|
process = new_process(args, **kwargs)
|
||||||
|
execute_process(process)
|
||||||
|
Loading…
Reference in New Issue
Block a user