feat: plugins

This commit is contained in:
Aleksander Cynarski 2025-04-21 08:15:19 +02:00
parent cea3d11a41
commit e676e35e02
8 changed files with 50 additions and 60 deletions

4
.gitignore vendored
View File

@ -400,6 +400,10 @@ FodyWeavers.xsd
*.sln.iml
# End of https://www.toptal.com/developers/gitignore/api/csharp
.DS_Store
.vscode
.idea
plugins/*
!plugins/.gitkeep

View File

@ -7,7 +7,11 @@
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../Automancer.Common/Automancer.Common.csproj" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.0-preview.3.25171.5" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.0-preview.3.25171.5" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="10.0.0-preview.3.25171.5" />

View File

@ -4,21 +4,16 @@ using Spectre.Console.Cli;
namespace Automancer.Command.Container;
public class Module : ICommandModuleWithRegistry
public class Module : ICommandModule
{
public void Configure(IConfigurator config)
{
// No implementation needed here
}
public string Name => "container";
public string Description => "Container operations";
public void Configure(IConfigurator config, CommandRegistry registry)
{
config.AddBranch("container", container =>
config.AddBranch(Name, container =>
{
var description = "Container operations";
container.SetDescription(description);
registry.Add("container", description);
container.SetDescription(Description);
registry.Add(Name, Description);
container.AddCommand<PsCommand>("ps").WithDescription("List containers");
});

View File

@ -3,18 +3,15 @@ using Spectre.Console.Cli;
namespace Automancer.Command.Image;
public class Module : ICommandModuleWithRegistry
public class Module : ICommandModule
{
public void Configure(IConfigurator config)
{
// No implementation needed here
}
public string Name => "image";
public string Description => "Docker/podman image operations";
public void Configure(IConfigurator config, CommandRegistry registry)
{
config.AddBranch("image", image => {
var description = "Docker/podman image operations";
image.SetDescription(description);
registry.Add("image", description);
config.AddBranch(Name, image => {
image.SetDescription(Description);
registry.Add(Name, Description);
image.AddCommand<BuildCommand>("build").WithDescription("Build a docker image");
image.AddCommand<PushCommand>("push").WithDescription("Push a docker image");

View File

@ -1,13 +0,0 @@
namespace Automancer.Common;
public record CommandInfo(string Path, string? Description);
public class CommandRegistry
{
public List<CommandInfo> Commands { get; } = new();
public void Add(string path, string? description)
{
Commands.Add(new CommandInfo(path, description));
}
}

View File

@ -1,15 +0,0 @@
namespace Automancer.Common;
using Spectre.Console.Cli;
public interface ICommandModule
{
void Configure(IConfigurator config);
}
public interface ICommandModuleWithRegistry : ICommandModule
{
void Configure(IConfigurator config, CommandRegistry registry);
}

View File

@ -9,10 +9,10 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Spectre.Console.Cli;
using Spectre.Console;
using System.Diagnostics.CodeAnalysis;
using Automancer.Commands;
using System.Reflection;
public abstract class Program
{
@ -41,6 +41,7 @@ public abstract class Program
var registry = new CommandRegistry();
services.AddSingleton(registry);
LoadPluginAssemblies("plugins");
RegisterCommandModules(services);
var registrar = new TypeRegistrar(services);
@ -58,15 +59,9 @@ public abstract class Program
foreach (var module in modules)
{
if (module is ICommandModuleWithRegistry withRegistry)
{
withRegistry.Configure(config, registry);
}
else
{
module.Configure(config);
}
module.Configure(config, registry);
}
});
})
.Build();
@ -76,6 +71,7 @@ public abstract class Program
private static void RegisterCommandModules(IServiceCollection services)
{
var moduleType = typeof(ICommandModule);
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
@ -83,7 +79,9 @@ public abstract class Program
.SelectMany(a =>
{
try { return a.GetTypes(); }
catch { return []; }
catch {
return [];
}
})
.Where(t => moduleType.IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract)
.ToList();
@ -94,4 +92,24 @@ public abstract class Program
services.AddSingleton(typeof(ICommandModule), type);
}
}
private static void LoadPluginAssemblies(string pluginDirectory)
{
if (!Directory.Exists(pluginDirectory))
return;
var dlls = Directory.GetFiles(pluginDirectory, "*.dll", SearchOption.AllDirectories);
foreach (var dll in dlls)
{
try
{
var asm = Assembly.LoadFrom(dll);
}
catch (Exception ex)
{
Console.WriteLine($"[WARN] Failed to load plugin '{dll}': {ex.Message}");
}
}
}
}

0
plugins/.gitkeep Normal file
View File