mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-05-30 15:43:33 +00:00
fix(plugin-loader): detect new deps via requirements.txt hash instead of empty marker
The .dependencies_installed marker was an empty file, so adding a new package to requirements.txt (e.g. astral in ledmatrix-weather v2.3.0) never triggered a pip re-install on existing installs — the file existed so the check returned early. The marker now stores a SHA-256 hash of requirements.txt. On every plugin load, the loader compares the current hash to the stored one; a mismatch (or missing marker) triggers pip install and writes the new hash. store_manager._install_dependencies() also writes the hash marker after a store install/update so the loader skips a redundant pip run on next boot. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,7 @@ Handles plugin module imports, dependency installation, and class instantiation.
|
||||
Extracted from PluginManager to improve separation of concerns.
|
||||
"""
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
import importlib
|
||||
import importlib.util
|
||||
@@ -164,11 +165,15 @@ class PluginLoader:
|
||||
if not requirements_file.exists():
|
||||
return True # No dependencies needed
|
||||
marker_path = plugin_dir_resolved / ".dependencies_installed"
|
||||
current_hash = hashlib.sha256(requirements_file.read_bytes()).hexdigest()
|
||||
|
||||
# Check if already installed
|
||||
# Skip if requirements.txt hasn't changed since last install
|
||||
if marker_path.exists():
|
||||
self.logger.debug("Dependencies already installed for %s", plugin_id)
|
||||
stored_hash = marker_path.read_text(encoding='utf-8').strip()
|
||||
if stored_hash == current_hash:
|
||||
self.logger.debug("Dependencies already installed for %s (requirements unchanged)", plugin_id)
|
||||
return True
|
||||
self.logger.info("Requirements changed for %s, reinstalling dependencies", plugin_id)
|
||||
|
||||
try:
|
||||
self.logger.info("Installing dependencies for plugin %s...", plugin_id)
|
||||
@@ -181,9 +186,7 @@ class PluginLoader:
|
||||
)
|
||||
|
||||
if result.returncode == 0:
|
||||
# Mark as installed
|
||||
marker_path.touch()
|
||||
# Set proper file permissions after creating marker
|
||||
marker_path.write_text(current_hash, encoding='utf-8')
|
||||
ensure_file_permissions(marker_path, get_plugin_file_mode())
|
||||
self.logger.info("Dependencies installed successfully for %s", plugin_id)
|
||||
return True
|
||||
@@ -199,7 +202,7 @@ class PluginLoader:
|
||||
"Assuming they are satisfied: %s",
|
||||
plugin_id, stderr.strip()
|
||||
)
|
||||
marker_path.touch()
|
||||
marker_path.write_text(current_hash, encoding='utf-8')
|
||||
ensure_file_permissions(marker_path, get_plugin_file_mode())
|
||||
return True
|
||||
self.logger.warning(
|
||||
|
||||
@@ -5,6 +5,7 @@ Handles plugin discovery, installation, updates, and uninstallation
|
||||
from both the official registry and custom GitHub repositories.
|
||||
"""
|
||||
|
||||
import hashlib
|
||||
import os
|
||||
import json
|
||||
import stat
|
||||
@@ -1755,6 +1756,12 @@ class PluginStoreManager:
|
||||
timeout=300
|
||||
)
|
||||
self.logger.info(f"Dependencies installed successfully for {plugin_path.name}")
|
||||
# Write hash marker so plugin_loader skips redundant pip run on next startup
|
||||
try:
|
||||
current_hash = hashlib.sha256(requirements_file.read_bytes()).hexdigest()
|
||||
(plugin_path / ".dependencies_installed").write_text(current_hash, encoding='utf-8')
|
||||
except OSError as marker_err:
|
||||
self.logger.debug("Could not write dependency marker for %s: %s", plugin_path.name, marker_err)
|
||||
return True
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
|
||||
Reference in New Issue
Block a user