mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-10-04 22:35:08 +00:00
fix(plugin-system): load/enable failures, atomic state files, pip lock, test-double parity (#645)
- load_plugin: an on_enable() that raises unregisters the instance, so the next load retries instead of returning True "already loaded". - get_plugin_info: guard plugin.get_info(); one plugin raising no longer breaks /api/v3/plugins/installed. - plugin_state.json and the operation history are written with atomic_write_text under their lock. - plugin_loader: module-level lock serialises pip installs across the parallel startup loaders. - store_manager._install_via_download: extract dir cleanup moved to finally. - Test doubles: draw_image() warns (DeprecationWarning; the real DisplayManager has none), MockDisplayManager.draw_text accepts the real signature's optional params, VisualTestDisplayManager logs draw errors at WARNING. - Docs/comments: compatibility.py method name, PluginState.LOADED meaning, brittle schema count, why _report_skip_once uses setdefault. - Remove unused PluginOperationQueue.get_active_operations(). Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1994,6 +1994,7 @@ class PluginStoreManager:
|
||||
tmp_file.write(chunk)
|
||||
tmp_zip_path = tmp_file.name
|
||||
|
||||
temp_extract = None
|
||||
try:
|
||||
# Extract zip
|
||||
with zipfile.ZipFile(tmp_zip_path, 'r') as zip_ref:
|
||||
@@ -2015,7 +2016,6 @@ class PluginStoreManager:
|
||||
f"Zip-slip detected: member {member!r} resolves outside "
|
||||
f"temp directory, aborting"
|
||||
)
|
||||
shutil.rmtree(temp_extract, ignore_errors=True)
|
||||
return False
|
||||
zip_ref.extractall(temp_extract)
|
||||
|
||||
@@ -2027,10 +2027,6 @@ class PluginStoreManager:
|
||||
else:
|
||||
# No root dir, move everything
|
||||
shutil.move(str(temp_extract), str(target_path))
|
||||
|
||||
# Cleanup temp extract dir
|
||||
if temp_extract.exists():
|
||||
shutil.rmtree(temp_extract, ignore_errors=True)
|
||||
|
||||
return True
|
||||
|
||||
@@ -2038,6 +2034,12 @@ class PluginStoreManager:
|
||||
# Remove temporary zip file
|
||||
if os.path.exists(tmp_zip_path):
|
||||
os.remove(tmp_zip_path)
|
||||
# Cleanup temp extract dir here rather than on the success
|
||||
# path, so a failed extract or move doesn't leave a copy of
|
||||
# the plugin in /tmp on every attempt. (Gone already when the
|
||||
# whole dir was moved into place.)
|
||||
if temp_extract is not None and temp_extract.exists():
|
||||
shutil.rmtree(temp_extract, ignore_errors=True)
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"Download failed: {e}")
|
||||
|
||||
Reference in New Issue
Block a user