diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7d040e9..4ac2db8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -35,16 +35,35 @@ jobs: sudo apt-get update sudo apt-get install -y chromium-browser chromium-chromedriver xvfb + - name: Set Chromium binary (Ubuntu) + if: matrix.os == 'ubuntu-latest' + run: | + if [ -x /usr/bin/chromium-browser ]; then + echo "CHROME_BINARY=/usr/bin/chromium-browser" >> $GITHUB_ENV + elif [ -x /usr/bin/chromium ]; then + echo "CHROME_BINARY=/usr/bin/chromium" >> $GITHUB_ENV + fi + - name: Install browser for testing (macOS) if: matrix.os == 'macos-latest' run: | brew install --cask chromium chromedriver + - name: Set Chromium binary (macOS) + if: matrix.os == 'macos-latest' + run: | + echo "CHROME_BINARY=/Applications/Chromium.app/Contents/MacOS/Chromium" >> $GITHUB_ENV + - name: Install browser for testing (Windows) if: matrix.os == 'windows-latest' run: | choco install chromium chromedriver + - name: Set Chromium binary (Windows) + if: matrix.os == 'windows-latest' + run: | + "CHROME_BINARY=C:\Program Files\Chromium\Application\chrome.exe" | Out-File -FilePath $env:GITHUB_ENV -Append + - name: Start virtual display (Ubuntu) if: matrix.os == 'ubuntu-latest' run: | @@ -53,7 +72,7 @@ jobs: - name: Run tests run: | - python -m pytest tests/ -v --cov=dash_vite_plugin --cov-report=xml + python -m pytest tests/ -v --headless --cov=dash_vite_plugin --cov-report=xml env: DISPLAY: :99 diff --git a/README-zh_CN.md b/README-zh_CN.md index 8a46f10..27edf1e 100644 --- a/README-zh_CN.md +++ b/README-zh_CN.md @@ -131,6 +131,7 @@ VitePlugin 是负责管理 Vite 构建过程的主要插件类。 | download_node | bool | False | 如果未找到是否下载 Node.js | | node_version | str | '18.17.0' | 要下载的 Node.js 版本 | | clean_after | bool | False | 构建后是否清理生成的文件 | +| log_show_mode | Literal['all', 'slim', 'hide'] | 'all' | 日志显示模式 ('all' 显示所有日志, 'slim' 显示精简日志, 'hide' 不显示日志) | | skip_build | bool | False | 是否跳过构建执行 | | skip_build_if_recent | bool | True | 如果构建文件是最近生成的是否跳过构建 | | skip_build_time_threshold | int | 5 | 考虑构建文件为最近的时间阈值(秒)| diff --git a/README.md b/README.md index 97cca68..a111731 100644 --- a/README.md +++ b/README.md @@ -130,6 +130,7 @@ VitePlugin is the main plugin class responsible for managing the Vite build proc | download_node | bool | False | Whether to download Node.js if not found | | node_version | str | '18.17.0' | Node.js version to download | | clean_after | bool | False | Whether to clean up generated files after build | +| log_show_mode | Literal['all', 'slim', 'hide'] | 'all' | Log show mode ('all' for verbose, 'slim' for minimal output, 'hide' for no logs) | | skip_build | bool | False | Whether to skip build execution | | skip_build_if_recent | bool | True | Whether to skip build if built file was recently generated | | skip_build_time_threshold | int | 5 | Time threshold in seconds to consider built file as recent | diff --git a/dash_vite_plugin/plugin.py b/dash_vite_plugin/plugin.py index 9d91fea..d9cb64a 100644 --- a/dash_vite_plugin/plugin.py +++ b/dash_vite_plugin/plugin.py @@ -6,7 +6,7 @@ from dash import Dash, hooks from flask import send_from_directory from py_node_manager import get_logger -from typing import List +from typing import List, Literal from .utils import NpmPackage, ViteCommand @@ -29,6 +29,7 @@ def __init__( download_node: bool = False, node_version: str = '18.20.8', clean_after: bool = False, + log_show_mode: Literal['all', 'slim', 'hide'] = 'all', skip_build: bool = False, skip_build_if_recent: bool = True, skip_build_time_threshold: int = 5, @@ -46,6 +47,7 @@ def __init__( download_node (bool): Whether to download Node.js if not found node_version (str): Node.js version to download if download_node is True clean_after (bool): Whether to clean up generated files after build + log_show_mode (Literal['all', 'slim', 'hide']): Log show mode skip_build (bool): Whether to skip build execution skip_build_if_recent (bool): Whether to skip build if built file was recently generated skip_build_time_threshold (int): Time threshold in seconds to consider built file as recent @@ -59,6 +61,7 @@ def __init__( self.download_node = download_node self.node_version = node_version self.clean_after = clean_after + self.log_show_mode = log_show_mode self.skip_build = skip_build self.skip_build_if_recent = skip_build_if_recent self.skip_build_time_threshold = skip_build_time_threshold @@ -71,6 +74,7 @@ def __init__( download_node=download_node, node_version=node_version, is_cli=False, + log_show_mode=log_show_mode, ) self._clean_files = [] self._clean_dirs = [] @@ -205,10 +209,11 @@ def _should_skip_build(self) -> bool: file_mod_time = os.path.getmtime(check_index_path) current_time = time.time() if current_time - file_mod_time < self.skip_build_time_threshold: - logger.info( - f'⚡ Built assets file was generated recently ' - f'({current_time - file_mod_time:.2f}s ago), skipping build...' - ) + if self.log_show_mode in ['all', 'slim']: + logger.info( + f'⚡ Built assets file was generated recently ' + f'({current_time - file_mod_time:.2f}s ago), skipping build...' + ) return True return False diff --git a/dash_vite_plugin/utils.py b/dash_vite_plugin/utils.py index 5700436..5dffe1a 100644 --- a/dash_vite_plugin/utils.py +++ b/dash_vite_plugin/utils.py @@ -38,6 +38,7 @@ def __init__( download_node: bool, node_version: str, is_cli: bool, + log_show_mode: Literal['all', 'slim', 'hide'] = 'all', ): """ Initialize the ViteCommand class @@ -51,8 +52,11 @@ def __init__( download_node (bool): Whether to download Node.js if not found node_version (str): Node.js version to download if download_node is True is_cli (bool): Whether the command is being run from the CLI + log_show_mode (Literal['all', 'slim', 'hide']): Log show mode """ - node_manager = NodeManager(download_node=download_node, node_version=node_version, is_cli=False) + node_manager = NodeManager( + download_node=download_node, node_version=node_version, is_cli=False, log_show_mode=log_show_mode + ) self.node_path = node_manager.node_path self.node_env = node_manager.node_env self.npm_path = node_manager.npm_path @@ -62,6 +66,7 @@ def __init__( self.support_less = support_less self.support_sass = support_sass self.is_cli = is_cli + self.log_show_mode = log_show_mode # Ensure the plugin_tmp_dir directory exists self.plugin_tmp_dir = plugin_tmp_dir if not os.path.exists(self.plugin_tmp_dir): @@ -215,7 +220,8 @@ def _install_vite(self) -> None: Returns: None """ - logger.info('📥 Start installing Vite...') + if self.log_show_mode == 'all': + logger.info('📥 Start installing Vite...') try: if not self._check_vite(): install_cmd = [ @@ -237,10 +243,12 @@ def _install_vite(self) -> None: if result.returncode != 0: raise RuntimeError(result.stderr) - logger.info('✅ Vite installed successfully!') + if self.log_show_mode in ['all', 'slim']: + logger.info('✅ Vite installed successfully!') except Exception as e: - logger.error(f'❌ Error installing Vite: {e}') + if self.log_show_mode in ['all', 'slim']: + logger.error(f'❌ Error installing Vite: {e}') raise e def _install_less(self) -> None: @@ -250,7 +258,8 @@ def _install_less(self) -> None: Returns: None """ - logger.info('📥 Start installing Less...') + if self.log_show_mode == 'all': + logger.info('📥 Start installing Less...') try: if not self._check_less(): install_cmd = [ @@ -270,10 +279,12 @@ def _install_less(self) -> None: if result.returncode != 0: raise RuntimeError(result.stderr) - logger.info('✅ Less installed successfully!') + if self.log_show_mode in ['all', 'slim']: + logger.info('✅ Less installed successfully!') except Exception as e: - logger.error(f'❌ Error installing Less: {e}') + if self.log_show_mode in ['all', 'slim']: + logger.error(f'❌ Error installing Less: {e}') raise e def _install_sass(self) -> None: @@ -283,7 +294,8 @@ def _install_sass(self) -> None: Returns: None """ - logger.info('📥 Start installing Sass...') + if self.log_show_mode == 'all': + logger.info('📥 Start installing Sass...') try: if not self._check_sass(): install_cmd = [ @@ -303,10 +315,12 @@ def _install_sass(self) -> None: if result.returncode != 0: raise RuntimeError(result.stderr) - logger.info('✅ Sass installed successfully!') + if self.log_show_mode in ['all', 'slim']: + logger.info('✅ Sass installed successfully!') except Exception as e: - logger.error(f'❌ Error installing Sass: {e}') + if self.log_show_mode in ['all', 'slim']: + logger.error(f'❌ Error installing Sass: {e}') raise e def _install_npm_packages(self) -> None: @@ -316,7 +330,8 @@ def _install_npm_packages(self) -> None: Returns: None """ - logger.info('📥 Start installing npm packages...') + if self.log_show_mode == 'all': + logger.info('📥 Start installing npm packages...') try: for package in self.npm_packages: install_cmd = [ @@ -336,10 +351,12 @@ def _install_npm_packages(self) -> None: if result.returncode != 0: raise RuntimeError(result.stderr) - logger.info('✅ npm packages installed successfully!') + if self.log_show_mode in ['all', 'slim']: + logger.info('✅ npm packages installed successfully!') except Exception as e: - logger.error(f'❌ Error installing npm packages: {e}') + if self.log_show_mode in ['all', 'slim']: + logger.error(f'❌ Error installing npm packages: {e}') raise e def init(self) -> Self: @@ -349,27 +366,28 @@ def init(self) -> Self: Returns: Self: The ViteCommand instance """ - logger.info('🚀 Start initializing Vite...') + if self.log_show_mode == 'all': + logger.info('🚀 Start initializing Vite...') try: # Create default config if it doesn't exist - if self.is_cli: + if self.is_cli and self.log_show_mode == 'all': logger.info('⚙️ Creating Vite config file...') if not os.path.exists(self.config_js_path): - if self.is_cli: + if self.is_cli and self.log_show_mode == 'all': logger.info(f'🔍 Config file {self.config_js_path} not found. Creating default config file...') self.create_default_vite_config() - if self.is_cli: + if self.is_cli and self.log_show_mode == 'all': logger.info(f'💾 Default config file created at: {self.config_js_path}') - if self.is_cli: + if self.is_cli and self.log_show_mode == 'all': logger.info('⚙️ Creating index.html file...') self.create_default_index_html() - if self.is_cli: + if self.is_cli and self.log_show_mode == 'all': logger.info(f'💾 Default index.html file created at: {self.index_html_path}') if not self._check_npm_init(): @@ -384,11 +402,12 @@ def init(self) -> Self: ) if result.returncode != 0: raise RuntimeError(result.stderr) - - logger.info('✅ Vite initialized successfully!') + if self.log_show_mode in ['all', 'slim']: + logger.info('✅ Vite initialized successfully!') except Exception as e: - logger.error(f'❌ Error initializing Vite: {e}') + if self.log_show_mode in ['all', 'slim']: + logger.error(f'❌ Error initializing Vite: {e}') raise e return self @@ -416,7 +435,8 @@ def build(self) -> Self: Returns: Self: The ViteCommand instance """ - logger.info('🔨 Building assets using Vite...') + if self.log_show_mode == 'all': + logger.info('🔨 Building assets using Vite...') try: build_cmd: List[str] = [self.npx_path, 'vite', 'build'] @@ -427,10 +447,12 @@ def build(self) -> Self: if result.returncode != 0: raise RuntimeError(result.stderr) - logger.info('✅ Build completed successfully!') + if self.log_show_mode in ['all', 'slim']: + logger.info('✅ Build completed successfully!') except Exception as e: - logger.error(f'❌ Error building assets using Vite: {e}') + if self.log_show_mode in ['all', 'slim']: + logger.error(f'❌ Error building assets using Vite: {e}') raise e return self @@ -446,7 +468,8 @@ def clean(self, extra_clean_files: List[str], extra_clean_dirs: List[str]) -> Se Returns: Self: The ViteCommand instance """ - logger.info('🧹 Cleaning up generated files...') + if self.log_show_mode == 'all': + logger.info('🧹 Cleaning up generated files...') try: files_to_remove = [ self.config_js_path, @@ -464,25 +487,29 @@ def clean(self, extra_clean_files: List[str], extra_clean_dirs: List[str]) -> Se if os.path.exists(file_path): try: os.remove(file_path) - if self.is_cli: + if self.is_cli and self.log_show_mode == 'all': logger.info(f'🗑️ Removed {file_path}') except Exception as e: - logger.warning(f'⚠️ Warning: Could not remove {file_path}: {e}') + if self.log_show_mode in ['all', 'slim']: + logger.warning(f'⚠️ Warning: Could not remove {file_path}: {e}') # Remove directories for dir_path in directories_to_remove: if os.path.exists(dir_path): try: shutil.rmtree(dir_path) - if self.is_cli: + if self.is_cli and self.log_show_mode == 'all': logger.info(f'🗑️ Removed {dir_path}') except Exception as e: - logger.warning(f'⚠️ Warning: Could not remove {dir_path}: {e}') + if self.log_show_mode in ['all', 'slim']: + logger.warning(f'⚠️ Warning: Could not remove {dir_path}: {e}') - logger.info('✅ Cleanup completed.') + if self.log_show_mode in ['all', 'slim']: + logger.info('✅ Cleanup completed.') except Exception as e: - logger.error(f'❌ Error cleaning up: {e}') + if self.log_show_mode in ['all', 'slim']: + logger.error(f'❌ Error cleaning up: {e}') raise e return self diff --git a/tests/conftest.py b/tests/conftest.py index 35bc382..46ba578 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -3,6 +3,7 @@ import sys import shutil import tempfile +from selenium.webdriver.chrome.options import Options # Add the project root to the Python path so we can import the package @@ -26,3 +27,11 @@ def temp_working_directory(): yield temp_dir os.chdir(original_cwd) shutil.rmtree(temp_dir, ignore_errors=True) + + +def pytest_setup_options(): + options = Options() + chrome_binary = os.environ.get('CHROME_BINARY') + if chrome_binary: + options.binary_location = chrome_binary + return options