diff --git a/.gitignore b/.gitignore index 420d421c..090657d7 100644 --- a/.gitignore +++ b/.gitignore @@ -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/ diff --git a/LEDMatrix.code-workspace b/LEDMatrix.code-workspace index ef9f5d27..2fe510be 100644 --- a/LEDMatrix.code-workspace +++ b/LEDMatrix.code-workspace @@ -1,7 +1,99 @@ { - "folders": [ - { - "path": "." - } - ] -} \ No newline at end of file + "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 + } + } +} diff --git a/docs/MULTI_ROOT_WORKSPACE_SETUP.md b/docs/MULTI_ROOT_WORKSPACE_SETUP.md new file mode 100644 index 00000000..2fc0a833 --- /dev/null +++ b/docs/MULTI_ROOT_WORKSPACE_SETUP.md @@ -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 diff --git a/plugins/baseball-scoreboard b/plugins/baseball-scoreboard deleted file mode 160000 index 67e5596e..00000000 --- a/plugins/baseball-scoreboard +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 67e5596e52b91c97e30b23a26a27b17d60a0448f diff --git a/plugins/basketball-scoreboard b/plugins/basketball-scoreboard deleted file mode 160000 index d626e538..00000000 --- a/plugins/basketball-scoreboard +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d626e538c0fbfcb92cbc3f1c351ffe6d2c6c00bb diff --git a/plugins/calendar b/plugins/calendar deleted file mode 160000 index 940a045a..00000000 --- a/plugins/calendar +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 940a045a3b790bd54ba1e93e9e4449400f945257 diff --git a/plugins/christmas-countdown b/plugins/christmas-countdown deleted file mode 160000 index a01bb1b8..00000000 --- a/plugins/christmas-countdown +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a01bb1b8ff5bd596183ed9257fa57afe2ba7da93 diff --git a/plugins/clock-simple b/plugins/clock-simple deleted file mode 160000 index 228e1f2f..00000000 --- a/plugins/clock-simple +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 228e1f2fcf876f15268261acc9e7a09b6d2dde60 diff --git a/plugins/football-scoreboard b/plugins/football-scoreboard deleted file mode 160000 index 8d36803e..00000000 --- a/plugins/football-scoreboard +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8d36803e3b4288eaf3ba5feec8659e4d084ca024 diff --git a/plugins/hockey-scoreboard b/plugins/hockey-scoreboard deleted file mode 160000 index 9fef0cd2..00000000 --- a/plugins/hockey-scoreboard +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 9fef0cd2099c371b80a2637b0a648e3d1fa0de9c diff --git a/plugins/ledmatrix-flights b/plugins/ledmatrix-flights deleted file mode 160000 index 56eb42c3..00000000 --- a/plugins/ledmatrix-flights +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 56eb42c30a454890ad62d5151b7bfd8295eea33d diff --git a/plugins/ledmatrix-leaderboard b/plugins/ledmatrix-leaderboard deleted file mode 160000 index 6ecc6cd1..00000000 --- a/plugins/ledmatrix-leaderboard +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 6ecc6cd18348f7bed5e86c5c158d1b16b70dc035 diff --git a/plugins/ledmatrix-music b/plugins/ledmatrix-music deleted file mode 160000 index ded4fe91..00000000 --- a/plugins/ledmatrix-music +++ /dev/null @@ -1 +0,0 @@ -Subproject commit ded4fe91e4fa337c53641bc3174197eac86d246a diff --git a/plugins/ledmatrix-news b/plugins/ledmatrix-news deleted file mode 160000 index cdc1b118..00000000 --- a/plugins/ledmatrix-news +++ /dev/null @@ -1 +0,0 @@ -Subproject commit cdc1b118add3eee9e15966a2c146b7e9a8f77f3a diff --git a/plugins/ledmatrix-of-the-day b/plugins/ledmatrix-of-the-day deleted file mode 160000 index c06c4a3e..00000000 --- a/plugins/ledmatrix-of-the-day +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c06c4a3e4c59c5bb43c1d19e0e92d35218caa30c diff --git a/plugins/ledmatrix-stocks b/plugins/ledmatrix-stocks deleted file mode 160000 index 39715c38..00000000 --- a/plugins/ledmatrix-stocks +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 39715c38e0d6456329899655c2c60c8a51d5f23a diff --git a/plugins/ledmatrix-weather b/plugins/ledmatrix-weather deleted file mode 160000 index 7be0ef72..00000000 --- a/plugins/ledmatrix-weather +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 7be0ef72583c993392024224c2e2dcba908189fa diff --git a/plugins/mqtt-notifications b/plugins/mqtt-notifications deleted file mode 160000 index 5c23a842..00000000 --- a/plugins/mqtt-notifications +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 5c23a842d8efc6e1253bc3dfc67c65ce37a193cc diff --git a/plugins/odds-ticker b/plugins/odds-ticker deleted file mode 160000 index b8c0e877..00000000 --- a/plugins/odds-ticker +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b8c0e87748fad4c5bc7cedf65234e370a660a506 diff --git a/plugins/olympics-countdown b/plugins/olympics-countdown deleted file mode 160000 index bf31bd8b..00000000 --- a/plugins/olympics-countdown +++ /dev/null @@ -1 +0,0 @@ -Subproject commit bf31bd8b4b57e407cfa9770b5dd13cf8502ed3e5 diff --git a/plugins/soccer-scoreboard b/plugins/soccer-scoreboard deleted file mode 160000 index e22a16da..00000000 --- a/plugins/soccer-scoreboard +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e22a16da38606f232bc6cd67f9fc102ab0cca9ca diff --git a/plugins/static-image b/plugins/static-image deleted file mode 160000 index 70e1b4b6..00000000 --- a/plugins/static-image +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 70e1b4b69305b1f1aa6cd8ae0f3d8ab2322f23da diff --git a/plugins/text-display b/plugins/text-display deleted file mode 160000 index b3a423be..00000000 --- a/plugins/text-display +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b3a423beafaa81c8b77fe9b62821d62e82606373 diff --git a/plugins/youtube-stats b/plugins/youtube-stats deleted file mode 160000 index a305591b..00000000 --- a/plugins/youtube-stats +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a305591b069d9332a773b1e16c107d48e0c45566 diff --git a/scripts/setup_plugin_repos.py b/scripts/setup_plugin_repos.py new file mode 100755 index 00000000..1d8f0bb4 --- /dev/null +++ b/scripts/setup_plugin_repos.py @@ -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())