mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-10-04 22:35:08 +00:00
* refactor(plugins): split PluginStoreManager into mixins src/plugin_system/store_manager.py (2,977 lines) keeps the class, its shared state, locks, the uninstall registry, directory lookup and uninstall; its methods are split by area into: - store_registry.py (_RegistryMixin): registry, GitHub metadata, search, manifest validation - store_install.py (_InstallMixin): install paths and dependencies - store_update.py (_UpdateMixin): updates, rollback, local git state Pure move: all 56 members are byte-identical (checked with ast) and the assembled class has exactly the same attributes as before (checked at runtime). PluginStoreManager is imported from store_manager.py as before. Tests that patched shared modules (subprocess, requests, tempfile, shutil) through store_manager now reach them through the module whose code they exercise; a source-text contract test reads all store_*.py modules. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: annotate findings the split moved into new store modules subprocess imports and a list-form git clone (no shell), and the config template's placeholder token string -- existing code that Codacy reported as new because it moved. Annotated with the repo's nosec/nosemgrep style. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: annotate the default-branch git clone the split moved Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
163 lines
6.0 KiB
Markdown
163 lines
6.0 KiB
Markdown
# Plugin Dependency Installation Guide
|
|
|
|
## Overview
|
|
|
|
A plugin lists its Python packages in its `requirements.txt`. LEDMatrix
|
|
installs them for you when a plugin is installed, updated or loaded. This
|
|
guide explains where they end up and what to do when a plugin can't import a
|
|
package.
|
|
|
|
The rule to remember: **packages must be importable by `ledmatrix.service`,
|
|
which runs as root.** Anything installed only into another user's
|
|
`~/.local/` is invisible to it.
|
|
|
|
## Who Runs What
|
|
|
|
| Service | Runs as | Set by |
|
|
|---------|---------|--------|
|
|
| `ledmatrix.service` (display) | `root` | `systemd/ledmatrix.service` |
|
|
| `ledmatrix-web.service` (web UI) | the user who ran the installer (e.g. `ledpi`) | `User=__USER__` in `systemd/ledmatrix-web.service`, filled in by `scripts/install/install_service.sh` |
|
|
|
|
## How Dependencies Get Installed
|
|
|
|
### 1. Installing or updating a plugin from the web UI
|
|
|
|
The web interface is not root, so it installs through a narrow sudo helper:
|
|
|
|
1. `PluginStoreManager._install_dependencies()`
|
|
(`src/plugin_system/store_install.py`) calls
|
|
`install_requirements_file()` (`src/common/permission_utils.py`).
|
|
2. That runs `sudo -n bash scripts/fix_perms/safe_pip_install.sh <plugin>/requirements.txt`.
|
|
The helper checks the path is the project's own `requirements.txt` or a
|
|
`requirements.txt` under `plugin-repos/` or `plugins/`, then runs
|
|
`python3 -m pip install --break-system-packages --ignore-installed -r ...`
|
|
**as root**, so the display service can import the packages.
|
|
3. The sudoers rule that allows this is written by the installer
|
|
(`first_time_install.sh`) or by `scripts/install/configure_web_sudo.sh`.
|
|
|
|
If sudo refuses (the rule isn't installed), `install_requirements_file()`
|
|
falls back to installing with the web process's own interpreter, as the web
|
|
user, and prefixes the pip output with a note like:
|
|
|
|
```
|
|
[Root install unavailable (...); installed for the current process's user only.
|
|
Packages may not be visible to ledmatrix.service if it runs as a different
|
|
user — run scripts/install/configure_web_sudo.sh to fix this.]
|
|
```
|
|
|
|
Fix it by running `./scripts/install/configure_web_sudo.sh` as the web
|
|
user (not with `sudo`; it asks for your password itself), then
|
|
reinstall the plugin (or use the manual install below).
|
|
|
|
The **Reinstall Plugin Deps** button on the web UI's Tools tab goes
|
|
through the same helper for every installed plugin.
|
|
|
|
### 2. Loading a plugin
|
|
|
|
When a plugin loads, `PluginLoader.install_dependencies()`
|
|
(`src/plugin_system/plugin_loader.py`) checks its `requirements.txt`. If the
|
|
requirements are already satisfied it does nothing; otherwise it runs
|
|
`python3 -m pip install --break-system-packages -r requirements.txt` with the
|
|
interpreter of the process doing the loading (retrying with
|
|
`--ignore-installed` when a system package without a pip RECORD file is in
|
|
the way).
|
|
|
|
In `ledmatrix.service` that process is root, so restarting the display
|
|
service installs anything missing system-wide:
|
|
|
|
```bash
|
|
sudo systemctl restart ledmatrix
|
|
```
|
|
|
|
If you run `python3 run.py` by hand as a normal user instead, pip cannot
|
|
write to the system site-packages and installs into your `~/.local/`. That
|
|
works for your manual run but not for the service.
|
|
|
|
## Common Scenarios
|
|
|
|
### Installing plugins from the web UI (recommended)
|
|
|
|
Use the **Plugin Manager** tab. Dependencies are installed as root through
|
|
the sudo helper and the display service can use them.
|
|
|
|
### Running the display manually for debugging
|
|
|
|
```bash
|
|
cd ~/LEDMatrix
|
|
sudo python3 run.py # same user as the service
|
|
```
|
|
|
|
Running as your own user works for plugins whose packages are already
|
|
installed system-wide, but any *missing* package lands in `~/.local/`.
|
|
|
|
### A plugin works when run manually but fails in the service
|
|
|
|
Its packages were installed for your user only. Install them as root (see
|
|
below) and restart the service.
|
|
|
|
## Manual Installation
|
|
|
|
### All plugins
|
|
|
|
```bash
|
|
sudo ~/LEDMatrix/scripts/install_plugin_dependencies.sh
|
|
sudo systemctl restart ledmatrix
|
|
```
|
|
|
|
The script installs every `requirements.txt` found in the plugins directory
|
|
configured by `plugin_system.plugins_directory` in `config/config.json`
|
|
(default `plugin-repos/`). Run it with `sudo` so the packages are installed
|
|
system-wide.
|
|
|
|
### One plugin
|
|
|
|
```bash
|
|
cd ~/LEDMatrix/plugin-repos/PLUGIN-NAME # or your configured plugins directory
|
|
sudo python3 -m pip install --break-system-packages --no-cache-dir -r requirements.txt
|
|
sudo systemctl restart ledmatrix
|
|
```
|
|
|
|
`--no-cache-dir` avoids errors about `/root/.cache/pip` not being writable.
|
|
|
|
## Troubleshooting
|
|
|
|
### Permission denied when installing dependencies
|
|
|
|
```
|
|
ERROR: Could not install packages due to an OSError: [Errno 13] Permission denied: '/root/.local'
|
|
WARNING: The directory '/root/.cache/pip' or its parent directory is not owned or is not writable
|
|
```
|
|
|
|
Use one of the manual installs above (they pass `--no-cache-dir`).
|
|
|
|
### Checking where a package is installed
|
|
|
|
```bash
|
|
# How the service sees it
|
|
sudo python3 -c "import package_name; print(package_name.__file__)"
|
|
|
|
# A path under /home/<user>/.local/ means it was installed for that user only
|
|
python3 -m pip show -f package_name
|
|
```
|
|
|
|
For more, see the [Plugin Dependency Troubleshooting Guide](PLUGIN_DEPENDENCY_TROUBLESHOOTING.md).
|
|
|
|
## For Plugin Authors
|
|
|
|
1. Keep `requirements.txt` minimal and pin only what you need.
|
|
2. Test that it installs the way the Pi will install it:
|
|
```bash
|
|
sudo python3 -m pip install --break-system-packages --no-cache-dir -r requirements.txt
|
|
```
|
|
3. Note any `apt` packages your plugin needs in its README.
|
|
|
|
## Files to Reference
|
|
|
|
- Service units: `systemd/ledmatrix.service`, `systemd/ledmatrix-web.service`
|
|
- Store installs: `src/plugin_system/store_install.py` (`_install_dependencies`)
|
|
- Root install helper: `src/common/permission_utils.py` (`install_requirements_file`), `scripts/fix_perms/safe_pip_install.sh`
|
|
- Load-time installs: `src/plugin_system/plugin_loader.py` (`install_dependencies`)
|
|
- Sudo rules: `scripts/install/lib_sudoers.sh` (written by `first_time_install.sh`
|
|
and `scripts/install/configure_web_sudo.sh`)
|
|
- Manual installer: `scripts/install_plugin_dependencies.sh`
|