mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-04-10 13:02:59 +00:00
refactor: migrate from submodules to multi-root workspace for plugins (#198)
* refactor: migrate from submodules to multi-root workspace for plugins - Updated LEDMatrix.code-workspace to include all plugin repos as root folders - Removed symlinks from plugin-repos/ and plugins/ directories - Updated .gitignore to reflect new plugin management approach - Added setup_plugin_repos.py script for managing plugin symlinks (if needed) - Added MULTI_ROOT_WORKSPACE_SETUP.md documentation Plugins are now managed as independent repositories via multi-root workspace, allowing for easier development and independent updates without modifying the LEDMatrix project structure. * Fix MULTI_ROOT_WORKSPACE_SETUP.md and add JSON error handling - Remove deprecated clone_plugin_repos.py command reference - Add language tag to directory tree code fence (fixes MD040) - Add JSONDecodeError handling in setup_plugin_repos.py with user-friendly error messages --------- Co-authored-by: Chuck <chuck@example.com>
This commit is contained in:
27
.gitignore
vendored
27
.gitignore
vendored
@@ -35,29 +35,8 @@ htmlcov/
|
||||
# Cache directory (root level only, not src/cache which is source code)
|
||||
/cache/
|
||||
|
||||
# Development plugins directory (contains symlinks to plugin repos)
|
||||
# Plugins are installed via plugin store, not bundled with main repo
|
||||
# Allow git submodules
|
||||
# Development plugins directory
|
||||
# Plugins are managed as separate repositories via multi-root workspace
|
||||
# See docs/MULTI_ROOT_WORKSPACE_SETUP.md for details
|
||||
plugins/*
|
||||
!plugins/.gitkeep
|
||||
!plugins/odds-ticker/
|
||||
!plugins/clock-simple/
|
||||
!plugins/text-display/
|
||||
!plugins/basketball-scoreboard/
|
||||
!plugins/soccer-scoreboard/
|
||||
!plugins/calendar/
|
||||
!plugins/mqtt-notifications/
|
||||
!plugins/youtube-stats/
|
||||
!plugins/olympics-countdown/
|
||||
!plugins/ledmatrix-stocks/
|
||||
!plugins/ledmatrix-music/
|
||||
!plugins/static-image/
|
||||
!plugins/football-scoreboard/
|
||||
!plugins/hockey-scoreboard/
|
||||
!plugins/baseball-scoreboard/
|
||||
!plugins/christmas-countdown/
|
||||
!plugins/ledmatrix-flights/
|
||||
!plugins/ledmatrix-leaderboard/
|
||||
!plugins/ledmatrix-weather/
|
||||
!plugins/ledmatrix-news/
|
||||
!plugins/ledmatrix-of-the-day/
|
||||
|
||||
@@ -1,7 +1,99 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": "."
|
||||
}
|
||||
]
|
||||
}
|
||||
"folders": [
|
||||
{
|
||||
"path": ".",
|
||||
"name": "LEDMatrix (Main)"
|
||||
},
|
||||
{
|
||||
"path": "../ledmatrix-odds-ticker",
|
||||
"name": "Odds Ticker"
|
||||
},
|
||||
{
|
||||
"path": "../ledmatrix-clock-simple",
|
||||
"name": "Clock Simple"
|
||||
},
|
||||
{
|
||||
"path": "../ledmatrix-text-display",
|
||||
"name": "Text Display"
|
||||
},
|
||||
{
|
||||
"path": "../ledmatrix-basketball-scoreboard",
|
||||
"name": "Basketball Scoreboard"
|
||||
},
|
||||
{
|
||||
"path": "../ledmatrix-soccer-scoreboard",
|
||||
"name": "Soccer Scoreboard"
|
||||
},
|
||||
{
|
||||
"path": "../ledmatrix-calendar",
|
||||
"name": "Calendar"
|
||||
},
|
||||
{
|
||||
"path": "../ledmatrix-olympics-countdown",
|
||||
"name": "Olympics Countdown"
|
||||
},
|
||||
{
|
||||
"path": "../ledmatrix-stocks",
|
||||
"name": "Stocks"
|
||||
},
|
||||
{
|
||||
"path": "../ledmatrix-music",
|
||||
"name": "Music"
|
||||
},
|
||||
{
|
||||
"path": "../ledmatrix-static-image",
|
||||
"name": "Static Image"
|
||||
},
|
||||
{
|
||||
"path": "../ledmatrix-football-scoreboard",
|
||||
"name": "Football Scoreboard"
|
||||
},
|
||||
{
|
||||
"path": "../ledmatrix-hockey-scoreboard",
|
||||
"name": "Hockey Scoreboard"
|
||||
},
|
||||
{
|
||||
"path": "../ledmatrix-baseball-scoreboard",
|
||||
"name": "Baseball Scoreboard"
|
||||
},
|
||||
{
|
||||
"path": "../ledmatrix-christmas-countdown",
|
||||
"name": "Christmas Countdown"
|
||||
},
|
||||
{
|
||||
"path": "../ledmatrix-flights",
|
||||
"name": "Flights"
|
||||
},
|
||||
{
|
||||
"path": "../ledmatrix-leaderboard",
|
||||
"name": "Leaderboard"
|
||||
},
|
||||
{
|
||||
"path": "../ledmatrix-weather",
|
||||
"name": "Weather"
|
||||
},
|
||||
{
|
||||
"path": "../ledmatrix-news",
|
||||
"name": "News"
|
||||
},
|
||||
{
|
||||
"path": "../ledmatrix-of-the-day",
|
||||
"name": "Of The Day"
|
||||
},
|
||||
{
|
||||
"path": "../ledmatrix-youtube-stats",
|
||||
"name": "YouTube Stats"
|
||||
},
|
||||
{
|
||||
"path": "../ledmatrix-plugins",
|
||||
"name": "Plugin Registry"
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
"files.exclude": {
|
||||
"**/.git": true,
|
||||
"**/__pycache__": true,
|
||||
"**/*.pyc": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
169
docs/MULTI_ROOT_WORKSPACE_SETUP.md
Normal file
169
docs/MULTI_ROOT_WORKSPACE_SETUP.md
Normal file
@@ -0,0 +1,169 @@
|
||||
# Multi-Root Workspace Setup Guide
|
||||
|
||||
This document explains how the LEDMatrix project uses a multi-root workspace to manage plugins as separate Git repositories.
|
||||
|
||||
## Overview
|
||||
|
||||
The LEDMatrix project has been migrated from a git submodule implementation to a **multi-root workspace** implementation for managing plugins. This allows:
|
||||
|
||||
- ✅ Plugins to exist as independent Git repositories
|
||||
- ✅ Updates to plugins without modifying the LEDMatrix project
|
||||
- ✅ Easy development workflow with all repos in one workspace
|
||||
- ✅ Plugin system discovers plugins via symlinks in `plugin-repos/`
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```text
|
||||
/home/chuck/Github/
|
||||
├── LEDMatrix/ # Main project
|
||||
│ ├── plugin-repos/ # Symlinks to actual repos (managed automatically)
|
||||
│ │ ├── ledmatrix-clock-simple -> ../../ledmatrix-clock-simple
|
||||
│ │ ├── ledmatrix-weather -> ../../ledmatrix-weather
|
||||
│ │ └── ...
|
||||
│ ├── LEDMatrix.code-workspace # Multi-root workspace configuration
|
||||
│ └── ...
|
||||
├── ledmatrix-clock-simple/ # Plugin repository (actual git repo)
|
||||
├── ledmatrix-weather/ # Plugin repository (actual git repo)
|
||||
├── ledmatrix-football-scoreboard/ # Plugin repository (actual git repo)
|
||||
└── ... # Other plugin repos
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
### 1. Plugin Repositories
|
||||
|
||||
All plugin repositories are cloned to `/home/chuck/Github/` (parent directory of LEDMatrix) as regular Git repositories:
|
||||
|
||||
- `ledmatrix-clock-simple/`
|
||||
- `ledmatrix-weather/`
|
||||
- `ledmatrix-football-scoreboard/`
|
||||
- etc.
|
||||
|
||||
### 2. Symlinks in plugin-repos/
|
||||
|
||||
The `LEDMatrix/plugin-repos/` directory contains symlinks pointing to the actual repositories in the parent directory. This allows the plugin system to discover plugins without modifying the project structure.
|
||||
|
||||
### 3. Multi-Root Workspace
|
||||
|
||||
The `LEDMatrix.code-workspace` file configures VS Code/Cursor to open all plugin repositories as separate workspace roots, allowing easy development across all repos.
|
||||
|
||||
## Setup Scripts
|
||||
|
||||
### Initial Setup
|
||||
|
||||
If you already have plugin repositories cloned, use the setup script:
|
||||
|
||||
```bash
|
||||
cd /home/chuck/Github/LEDMatrix
|
||||
python3 scripts/setup_plugin_repos.py
|
||||
```
|
||||
|
||||
This script:
|
||||
- Reads the workspace configuration
|
||||
- Creates symlinks in `plugin-repos/` pointing to actual repos
|
||||
- Verifies all links are created correctly
|
||||
|
||||
### Updating Plugins
|
||||
|
||||
To update all plugin repositories:
|
||||
|
||||
```bash
|
||||
cd /home/chuck/Github/LEDMatrix
|
||||
python3 scripts/update_plugin_repos.py
|
||||
```
|
||||
|
||||
This script:
|
||||
- Finds all plugins in the workspace
|
||||
- Runs `git pull` on each repository
|
||||
- Reports which plugins were updated
|
||||
|
||||
## Configuration
|
||||
|
||||
The plugin system is configured in `config/config.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"plugin_system": {
|
||||
"plugins_directory": "plugin-repos",
|
||||
"auto_discover": true,
|
||||
"auto_load_enabled": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `plugins_directory` points to `plugin-repos/`, which contains symlinks to the actual repositories.
|
||||
|
||||
## Workflow
|
||||
|
||||
### Daily Development
|
||||
|
||||
1. **Open Workspace**: Open `LEDMatrix.code-workspace` in VS Code/Cursor
|
||||
2. **All Repos Available**: All plugin repos appear as separate folders in the workspace
|
||||
3. **Edit Plugins**: Edit plugin code directly in their repositories
|
||||
4. **Update Plugins**: Run `update_plugin_repos.py` to pull latest changes
|
||||
|
||||
### Adding New Plugins
|
||||
|
||||
1. **Clone Repository**: Clone the new plugin repo to `/home/chuck/Github/`
|
||||
2. **Add to Workspace**: Add the plugin folder to `LEDMatrix.code-workspace`
|
||||
3. **Create Symlink**: Run `setup_plugin_repos.py` to create the symlink
|
||||
|
||||
### Updating Individual Plugins
|
||||
|
||||
Since plugins are regular Git repositories, you can update them individually:
|
||||
|
||||
```bash
|
||||
cd /home/chuck/Github/ledmatrix-weather
|
||||
git pull origin master
|
||||
```
|
||||
|
||||
Or update all at once:
|
||||
|
||||
```bash
|
||||
cd /home/chuck/Github/LEDMatrix
|
||||
python3 scripts/update_plugin_repos.py
|
||||
```
|
||||
|
||||
## Benefits
|
||||
|
||||
1. **No Submodule Hassle**: No need to update `.gitmodules` or run `git submodule update`
|
||||
2. **Independent Updates**: Update plugins independently without touching LEDMatrix
|
||||
3. **Clean Separation**: Each plugin is a separate repository with its own history
|
||||
4. **Easy Development**: Multi-root workspace makes it easy to work across repos
|
||||
5. **Automatic Discovery**: Plugin system automatically discovers plugins via symlinks
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Symlinks Not Working
|
||||
|
||||
If plugins aren't being discovered:
|
||||
|
||||
```bash
|
||||
cd /home/chuck/Github/LEDMatrix
|
||||
python3 scripts/setup_plugin_repos.py
|
||||
```
|
||||
|
||||
This will recreate all symlinks.
|
||||
|
||||
### Missing Plugins
|
||||
|
||||
If a plugin is in the workspace but not found:
|
||||
|
||||
1. Check if the repo exists in `/home/chuck/Github/`
|
||||
2. Check if the symlink exists in `plugin-repos/`
|
||||
3. Run `setup_plugin_repos.py` to recreate symlinks
|
||||
|
||||
### Plugin Updates Not Showing
|
||||
|
||||
If changes to plugins aren't appearing:
|
||||
|
||||
1. Verify the symlink points to the correct directory: `ls -la plugin-repos/ledmatrix-weather`
|
||||
2. Check that you're editing in the actual repo, not a copy
|
||||
3. Restart the LEDMatrix service if running
|
||||
|
||||
## Notes
|
||||
|
||||
- The `plugin-repos/` directory is tracked in git, but only contains symlinks
|
||||
- Actual plugin code lives in `/home/chuck/Github/ledmatrix-*/`
|
||||
- Each plugin repo can be updated independently via `git pull`
|
||||
- The LEDMatrix project doesn't need to be updated when plugins change
|
||||
Submodule plugins/baseball-scoreboard deleted from 67e5596e52
Submodule plugins/basketball-scoreboard deleted from d626e538c0
Submodule plugins/calendar deleted from 940a045a3b
Submodule plugins/christmas-countdown deleted from a01bb1b8ff
Submodule plugins/clock-simple deleted from 228e1f2fcf
Submodule plugins/football-scoreboard deleted from 8d36803e3b
Submodule plugins/hockey-scoreboard deleted from 9fef0cd209
Submodule plugins/ledmatrix-flights deleted from 56eb42c30a
Submodule plugins/ledmatrix-leaderboard deleted from 6ecc6cd183
Submodule plugins/ledmatrix-music deleted from ded4fe91e4
Submodule plugins/ledmatrix-news deleted from cdc1b118ad
Submodule plugins/ledmatrix-of-the-day deleted from c06c4a3e4c
Submodule plugins/ledmatrix-stocks deleted from 39715c38e0
Submodule plugins/ledmatrix-weather deleted from 7be0ef7258
Submodule plugins/mqtt-notifications deleted from 5c23a842d8
Submodule plugins/odds-ticker deleted from b8c0e87748
Submodule plugins/olympics-countdown deleted from bf31bd8b4b
Submodule plugins/soccer-scoreboard deleted from e22a16da38
Submodule plugins/static-image deleted from 70e1b4b693
Submodule plugins/text-display deleted from b3a423beaf
Submodule plugins/youtube-stats deleted from a305591b06
151
scripts/setup_plugin_repos.py
Executable file
151
scripts/setup_plugin_repos.py
Executable file
@@ -0,0 +1,151 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Setup plugin repository references for multi-root workspace.
|
||||
|
||||
This script creates symlinks in plugin-repos/ pointing to the actual
|
||||
plugin repositories in the parent directory, allowing the system to
|
||||
find plugins without modifying the LEDMatrix project structure.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# Paths
|
||||
PROJECT_ROOT = Path(__file__).parent.parent
|
||||
PLUGIN_REPOS_DIR = PROJECT_ROOT / "plugin-repos"
|
||||
GITHUB_DIR = PROJECT_ROOT.parent
|
||||
CONFIG_FILE = PROJECT_ROOT / "config" / "config.json"
|
||||
|
||||
|
||||
def get_workspace_plugins():
|
||||
"""Get list of plugins from workspace file."""
|
||||
workspace_file = PROJECT_ROOT / "LEDMatrix.code-workspace"
|
||||
if not workspace_file.exists():
|
||||
return []
|
||||
|
||||
try:
|
||||
with open(workspace_file, 'r') as f:
|
||||
workspace = json.load(f)
|
||||
except json.JSONDecodeError as e:
|
||||
print(f"Error: Failed to parse workspace file {workspace_file}: {e}")
|
||||
print("Please check that the workspace file contains valid JSON.")
|
||||
return []
|
||||
|
||||
plugins = []
|
||||
for folder in workspace.get('folders', []):
|
||||
path = folder.get('path', '')
|
||||
if path.startswith('../') and path != '../ledmatrix-plugins':
|
||||
plugin_name = path.replace('../', '')
|
||||
plugins.append({
|
||||
'name': plugin_name,
|
||||
'workspace_path': path,
|
||||
'actual_path': GITHUB_DIR / plugin_name,
|
||||
'link_path': PLUGIN_REPOS_DIR / plugin_name
|
||||
})
|
||||
|
||||
return plugins
|
||||
|
||||
|
||||
def create_symlinks():
|
||||
"""Create symlinks in plugin-repos/ pointing to actual repos."""
|
||||
plugins = get_workspace_plugins()
|
||||
|
||||
if not plugins:
|
||||
print("No plugins found in workspace configuration")
|
||||
return False
|
||||
|
||||
# Ensure plugin-repos directory exists
|
||||
PLUGIN_REPOS_DIR.mkdir(exist_ok=True)
|
||||
|
||||
created = 0
|
||||
skipped = 0
|
||||
errors = 0
|
||||
|
||||
print(f"Setting up plugin repository links...")
|
||||
print(f" Source: {GITHUB_DIR}")
|
||||
print(f" Links: {PLUGIN_REPOS_DIR}")
|
||||
print()
|
||||
|
||||
for plugin in plugins:
|
||||
actual_path = plugin['actual_path']
|
||||
link_path = plugin['link_path']
|
||||
|
||||
if not actual_path.exists():
|
||||
print(f" ⚠️ {plugin['name']} - source not found: {actual_path}")
|
||||
errors += 1
|
||||
continue
|
||||
|
||||
# Remove existing link/file if it exists
|
||||
if link_path.exists() or link_path.is_symlink():
|
||||
if link_path.is_symlink():
|
||||
# Check if it points to the right place
|
||||
try:
|
||||
if link_path.resolve() == actual_path.resolve():
|
||||
print(f" ✓ {plugin['name']} - link already exists")
|
||||
skipped += 1
|
||||
continue
|
||||
else:
|
||||
# Remove old symlink pointing elsewhere
|
||||
link_path.unlink()
|
||||
except Exception as e:
|
||||
print(f" ⚠️ {plugin['name']} - error checking link: {e}")
|
||||
link_path.unlink()
|
||||
else:
|
||||
# It's a directory/file, not a symlink
|
||||
print(f" ⚠️ {plugin['name']} - {link_path.name} exists but is not a symlink")
|
||||
print(f" Skipping (manual cleanup required)")
|
||||
skipped += 1
|
||||
continue
|
||||
|
||||
# Create symlink
|
||||
try:
|
||||
# Use relative path for symlink portability
|
||||
relative_path = os.path.relpath(actual_path, link_path.parent)
|
||||
link_path.symlink_to(relative_path)
|
||||
print(f" ✓ {plugin['name']} - linked")
|
||||
created += 1
|
||||
except Exception as e:
|
||||
print(f" ✗ {plugin['name']} - failed to create link: {e}")
|
||||
errors += 1
|
||||
|
||||
print()
|
||||
print(f"✅ Created {created} links, skipped {skipped}, errors {errors}")
|
||||
|
||||
return errors == 0
|
||||
|
||||
|
||||
def update_config_path():
|
||||
"""Update config to use absolute path to parent directory (alternative approach)."""
|
||||
# This is an alternative - set plugins_directory to absolute path
|
||||
# Currently not implemented as symlinks are preferred
|
||||
pass
|
||||
|
||||
|
||||
def main():
|
||||
"""Main function."""
|
||||
print("🔗 Setting up plugin repository symlinks...")
|
||||
print()
|
||||
|
||||
if not GITHUB_DIR.exists():
|
||||
print(f"Error: GitHub directory not found: {GITHUB_DIR}")
|
||||
return 1
|
||||
|
||||
success = create_symlinks()
|
||||
|
||||
if success:
|
||||
print()
|
||||
print("✅ Plugin repository setup complete!")
|
||||
print()
|
||||
print("Plugins are now accessible via symlinks in plugin-repos/")
|
||||
print("You can update plugins independently in their git repos.")
|
||||
return 0
|
||||
else:
|
||||
print()
|
||||
print("⚠️ Setup completed with some errors. Check output above.")
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user