Skip to content

Commit a307d45

Browse files
committed
feat(plugins): List plugins with errors
Before now, plugins with errors would not appear on the `plugin:list`, now such plugins would appear with status error and user can use `--debug` flag to get more information. Signed-off-by: nfebe <fenn25.fn@gmail.com>
1 parent 2606f40 commit a307d45

File tree

2 files changed

+173
-34
lines changed

2 files changed

+173
-34
lines changed

app/Console/Commands/Plugin/ListCommand.php

Lines changed: 67 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,34 +4,90 @@
44

55
class ListCommand extends PluginCommand
66
{
7-
protected $signature = 'plugin:list';
7+
/**
8+
* The name and signature of the console command.
9+
*
10+
* @var string
11+
*/
12+
protected $signature = 'plugin:list
13+
{--debug : Show detailed error information for problematic plugins}';
814

9-
protected $description = 'List all available plugins';
15+
/**
16+
* The console command description.
17+
*
18+
* @var string
19+
*/
20+
protected $description = 'List all available plugins.';
1021

22+
/**
23+
* Execute the console command.
24+
*
25+
* @return int
26+
*/
1127
public function handle()
1228
{
1329
$plugins = $this->pluginManager->discover();
30+
$debug = $this->option('debug');
31+
$hasErrors = false;
32+
$errorMessages = [];
1433

1534
if ($plugins->isEmpty()) {
1635
$this->info('No plugins found.');
1736

1837
return 0;
1938
}
2039

21-
$rows = $plugins->map(function ($plugin) {
22-
return [
40+
$rows = [];
41+
42+
foreach ($plugins as $plugin) {
43+
$status = $plugin['enabled'] ? '<fg=green>Enabled</>' : '<fg=red>Disabled</>';
44+
$error = null;
45+
46+
// Check for common plugin issues
47+
if (isset($plugin['error'])) {
48+
$hasErrors = true;
49+
$status = '<fg=yellow>Error</>';
50+
$error = $plugin['error'];
51+
$errorMessages[] = "Plugin {$plugin['id']}: {$error}";
52+
} elseif ($debug) {
53+
// Additional debug checks
54+
if (! class_exists($plugin['provider'] ?? '')) {
55+
$error = "Provider class not found: {$plugin['provider']}";
56+
$status = '<fg=yellow>Error</>';
57+
$errorMessages[] = $error;
58+
}
59+
}
60+
61+
$rows[] = [
2362
'id' => $plugin['id'] ?? '',
24-
'name' => $plugin['name'],
63+
'name' => $plugin['name'] ?? 'Unknown',
2564
'version' => $plugin['manifest']['version'] ?? '1.0.0',
26-
'status' => $plugin['enabled'] ? '<fg=green>Enabled</>' : '<fg=red>Disabled</>',
65+
'status' => $status,
2766
'description' => $plugin['manifest']['description'] ?? 'No description',
67+
'error' => $debug ? ($error ?? '') : '',
2868
];
29-
})->toArray();
69+
}
70+
71+
$headers = ['ID', 'Name', 'Version', 'Status', 'Description'];
72+
if ($debug) {
73+
$headers[] = 'Error';
74+
}
75+
76+
$this->table($headers, $rows);
77+
78+
if ($hasErrors) {
79+
$this->error('Some plugins have issues. Use --debug for more details.');
3080

31-
$this->table(
32-
['ID', 'Name', 'Version', 'Status', 'Description'],
33-
$rows
34-
);
81+
if ($debug) {
82+
$this->line('');
83+
$this->warn('Error Details:');
84+
foreach ($errorMessages as $message) {
85+
$this->line(" - $message");
86+
}
87+
}
88+
89+
return 1;
90+
}
3591

3692
return 0;
3793
}

app/Services/PluginManager.php

Lines changed: 106 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -125,59 +125,118 @@ public function discover(): Collection
125125
protected function loadPlugin(string $path): ?array
126126
{
127127
$manifestPath = "{$path}/plugin.json";
128+
$pluginDir = basename($path);
128129

129130
if (! File::exists($manifestPath)) {
130-
Log::warning("Plugin manifest not found: {$manifestPath}");
131+
$error = "Plugin manifest not found: {$manifestPath}";
132+
Log::error($error);
131133

132-
return null;
134+
return [
135+
'id' => $pluginDir,
136+
'name' => $pluginDir,
137+
'path' => $path,
138+
'error' => $error,
139+
'enabled' => false,
140+
];
133141
}
134142

135143
Log::debug("Loading plugin from: {$path}");
136144

137145
try {
138146
$manifest = $this->readManifest($manifestPath);
139147
if (! $manifest) {
140-
Log::warning("Plugin at {$path} is missing plugin.json");
141-
142-
return null;
148+
$error = "Plugin at {$path} has an invalid plugin.json file";
149+
Log::warning($error);
150+
151+
return [
152+
'id' => $pluginDir,
153+
'name' => $pluginDir,
154+
'path' => $path,
155+
'error' => $error,
156+
'enabled' => false,
157+
];
143158
}
144159

145160
if (! isset($manifest['namespace']) || ! isset($manifest['provider'])) {
146-
Log::warning("Plugin at {$path} is missing required fields in plugin.json");
147-
148-
return null;
161+
$error = 'Plugin is missing required fields in plugin.json (namespace and provider are required)';
162+
Log::warning("Plugin at {$path}: {$error}");
163+
164+
return [
165+
'id' => $manifest['id'] ?? $pluginDir,
166+
'name' => $manifest['name'] ?? $pluginDir,
167+
'path' => $path,
168+
'manifest' => $manifest,
169+
'error' => $error,
170+
'enabled' => false,
171+
];
149172
}
150173

151174
if (! isset($manifest['id'])) {
152-
Log::warning("Plugin manifest missing required 'id' field: {$manifestPath}");
153-
154-
return null;
175+
$error = "Plugin manifest missing required 'id' field";
176+
Log::warning("{$manifestPath}: {$error}");
177+
178+
return [
179+
'id' => $pluginDir,
180+
'name' => $manifest['name'] ?? $pluginDir,
181+
'path' => $path,
182+
'manifest' => $manifest,
183+
'error' => $error,
184+
'enabled' => $manifest['enabled'] ?? false,
185+
];
155186
}
156187

157188
$providerClass = $manifest['provider'];
158-
$namespace = rtrim($manifest['namespace'] ?? basename($path), '\\').'\\';
189+
$namespace = rtrim($manifest['namespace'], '\\').'\\';
159190

160191
$this->registerPluginAutoloading($namespace, $path);
161192

162193
if (! class_exists($providerClass)) {
163-
Log::warning("Plugin provider class not found: {$providerClass} in {$path}");
164-
165-
return null;
194+
$error = "Provider class not found: {$providerClass}";
195+
Log::warning("Plugin {$manifest['id']}: {$error}");
196+
197+
return [
198+
'id' => $manifest['id'],
199+
'name' => $manifest['name'] ?? $pluginDir,
200+
'path' => $path,
201+
'namespace' => $namespace,
202+
'manifest' => $manifest,
203+
'provider' => $providerClass,
204+
'error' => $error,
205+
'enabled' => $manifest['enabled'] ?? false,
206+
];
166207
}
167208

168209
return [
169210
'id' => $manifest['id'],
170-
'name' => $manifest['name'] ?? basename($path),
211+
'name' => $manifest['name'] ?? $pluginDir,
171212
'path' => $path,
172213
'namespace' => $namespace,
173214
'manifest' => $manifest,
174215
'provider' => $providerClass,
175216
'enabled' => $manifest['enabled'] ?? true,
176217
];
177218
} catch (\JsonException $e) {
178-
Log::error("Failed to parse plugin manifest: {$manifestPath} - ".$e->getMessage());
219+
$error = 'Failed to parse plugin manifest: '.$e->getMessage();
220+
Log::error("{$manifestPath}: {$error}");
179221

180-
return null;
222+
return [
223+
'id' => $pluginDir,
224+
'name' => $pluginDir,
225+
'path' => $path,
226+
'error' => $error,
227+
'enabled' => false,
228+
];
229+
} catch (\Exception $e) {
230+
$error = 'Error loading plugin: '.$e->getMessage();
231+
Log::error("{$path}: {$error}");
232+
233+
return [
234+
'id' => $pluginDir,
235+
'name' => $pluginDir,
236+
'path' => $path,
237+
'error' => $error,
238+
'enabled' => false,
239+
];
181240
}
182241
}
183242

@@ -394,21 +453,43 @@ protected function readManifest(string $manifestPath): ?array
394453
}
395454

396455
/**
397-
* Check if a plugin is enabled by its ID
456+
* Check if a plugin is enabled and valid.
457+
*
458+
* @param string $pluginId The plugin ID to check
459+
* @return bool True if the plugin is enabled and valid, false otherwise
398460
*/
399461
public function isPluginEnabled(string $pluginId): bool
400462
{
401463
$plugin = $this->findPlugin($pluginId);
464+
465+
// Plugin not found
402466
if (! $plugin) {
403467
return false;
404468
}
405469

470+
// Plugin has errors
471+
if (isset($plugin['error'])) {
472+
return false;
473+
}
474+
475+
// Check if provider exists and is valid
476+
if (! isset($plugin['provider']) || empty($plugin['provider'])) {
477+
return false;
478+
}
479+
480+
if (! class_exists($plugin['provider'])) {
481+
Log::warning("Plugin provider class not found: {$plugin['provider']}");
482+
483+
return false;
484+
}
485+
406486
$manifestPath = $plugin['path'].'/plugin.json';
407487

408488
try {
409489
$manifest = $this->readManifest($manifestPath);
410490

411-
return $manifest ? ($manifest['enabled'] ?? true) : false;
491+
return $manifest ? (bool) ($manifest['enabled'] ?? true) : false;
492+
412493
} catch (\RuntimeException $e) {
413494
Log::error('Error reading plugin manifest', [
414495
'plugin' => $pluginId,
@@ -429,13 +510,15 @@ public function registerPlugins(): void
429510
Log::debug("Registered plugin service provider: {$plugin['provider']}");
430511
} catch (\Exception $e) {
431512
Log::error('Failed to register plugin service provider', [
432-
'provider' => $plugin['provider'] ?? 'unknown',
513+
'plugin' => $plugin['id'],
514+
'provider' => $plugin['provider'],
433515
'error' => $e->getMessage(),
434516
]);
435-
throw $e;
517+
// Don't throw the exception to allow other plugins to load
436518
}
437519
} else {
438-
Log::debug("Skipping disabled plugin: {$plugin['id']}");
520+
$reason = isset($plugin['error']) ? "error: {$plugin['error']}" : 'disabled';
521+
Log::debug("Skipping plugin {$plugin['id']}: {$reason}");
439522
}
440523
});
441524
}

0 commit comments

Comments
 (0)