From 7badcc333ba69ddb591c55d1206a8d8db95f5c32 Mon Sep 17 00:00:00 2001 From: mdnapo Date: Fri, 19 Jul 2024 15:05:20 +0200 Subject: [PATCH] Added command plugin documentation and renamed subCommands to commands in api command code --- MycroForge.CLI/Commands/MycroForge.Api.cs | 6 +- docs/docs.sln | 25 ++++ docs/docs/command_plugins.md | 171 +++++++++++++++++++++- 3 files changed, 198 insertions(+), 4 deletions(-) create mode 100644 docs/docs.sln diff --git a/MycroForge.CLI/Commands/MycroForge.Api.cs b/MycroForge.CLI/Commands/MycroForge.Api.cs index 5580a3c..fba1c87 100644 --- a/MycroForge.CLI/Commands/MycroForge.Api.cs +++ b/MycroForge.CLI/Commands/MycroForge.Api.cs @@ -7,11 +7,11 @@ public partial class MycroForge { public partial class Api : Command, ISubCommandOf { - public Api(IEnumerable> subCommands) : + public Api(IEnumerable> commands) : base("api", "API related commands") { - foreach (var subCommandOf in subCommands) - AddCommand((subCommandOf as Command)!); + foreach (var command in commands) + AddCommand((command as Command)!); } } } \ No newline at end of file diff --git a/docs/docs.sln b/docs/docs.sln new file mode 100644 index 0000000..69d0bbb --- /dev/null +++ b/docs/docs.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.002.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyTest.Plugin", "MyTest.Plugin\MyTest.Plugin.csproj", "{C93CD889-7228-4DA2-B0E2-5273F2FAAFE6}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C93CD889-7228-4DA2-B0E2-5273F2FAAFE6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C93CD889-7228-4DA2-B0E2-5273F2FAAFE6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C93CD889-7228-4DA2-B0E2-5273F2FAAFE6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C93CD889-7228-4DA2-B0E2-5273F2FAAFE6}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {241D0F32-CE9B-40CA-BEA2-A2554CA22824} + EndGlobalSection +EndGlobal diff --git a/docs/docs/command_plugins.md b/docs/docs/command_plugins.md index f95d460..e490fc1 100644 --- a/docs/docs/command_plugins.md +++ b/docs/docs/command_plugins.md @@ -2,4 +2,173 @@ sidebar_position: 6 --- -# Command plugins \ No newline at end of file +# Command plugins + +MycroForge has a plugin system that allows you to extend the CLI with your own commands. +This section will guide you through the process of creating your own extension to the `m4g` command. +MycroForge is written in C# sharp and this is the same for plugins, so decent knowledge about `C#` & `.NET` is required. +In this tutorial we will create a command plugin that extens the `m4g` command with a `dotenv` sub command. +What this command will do is generate a `.env` file in the current directory and print a message to the console. + +## Setup + +To start creating command plugins for MycroFroge, make sure you've added the devdisciples package repository. +This can be done by running the following command. + +```bash +dotnet nuget add source --name devdisciples https://git.devdisciples.com/api/packages/devdisciples/nuget/index.json +``` + +Run the following command to add the `MycroForge.PluginTemplate.Package`. + +```bash +dotnet add package --source devdisciples --version 1.0.0 MycroForge.PluginTemplate.Package +``` + +## Initialize a plugin package + +Generate a template plugin project by running the following command. + +```bash +m4g plugin init My.Dotenv.Plugin +``` + +This should generate the following folder structure. + +``` +Env.Plugin + ┣ 📜HelloWorldCommand.cs + ┣ 📜HelloWorldCommandPlugin.cs + ┗ 📜My.Dotenv.Plugin.csproj +``` + +Rename the following files. Also rename the classes in these files, the easiest way in `vscode` is the right click the class name and select the `Rename symbol` action. Note that this action does not (necessarily) rename the files! + +``` +HelloWorldCommand.cs => DotenvCommand.cs +HelloWorldCommandPlugin.cs => DotenvCommandPlugin.cs +``` + +Modify `Name` property in `DotenvCommandPlugin.cs`. + +```C# +// Before +public class DotenvCommandPlugin : ICommandPlugin +{ + public string Name => "My.Plugin"; + + public void RegisterServices(IServiceCollection services) + { + services.AddScoped, HelloWorldCommand>(); + } +} + +// After +public class DotenvCommandPlugin : ICommandPlugin +{ + public string Name => "My.Dotenv.Plugin"; + + public void RegisterServices(IServiceCollection services) + { + services.AddScoped, HelloWorldCommand>(); + } +} +``` + +Modify `DotenvCommand.cs`. + +```C# +// Before +public class DotenvCommand : Command, ISubCommandOf +{ + private readonly Argument NameArgument = + new(name: "name", description: "The name of the person to greet"); + + private readonly Option AllCapsOption = + new(aliases: ["-a", "--all-caps"], description: "Print the name in all caps"); + + private readonly ProjectContext _context; + + public DotenvCommand(ProjectContext context) : + base("hello", "An example command generated by dotnet new using the m4gp template") + { + _context = context; + AddArgument(NameArgument); + AddOption(AllCapsOption); + this.SetHandler(ExecuteAsync, NameArgument, AllCapsOption); + } + + private async Task ExecuteAsync(string name, bool allCaps) + { + name = allCaps ? name.ToUpper() : name; + + await _context.CreateFile("hello_world.txt", + $"Hello {name}!", + "This file was generated by your custom command!" + ); + } +} + +// After +public class DotenvCommand : Command, ISubCommandOf +{ + private readonly Argument VarsArgument = + new(name: "vars", description: "Env vars to include in the .env file separated by ';'"); + + private readonly Option PrintOption = + new(aliases: ["-o", "--overwrite"], description: "Overwrite the .env file if it exists"); + + private readonly ProjectContext _context; + + public DotenvCommand(ProjectContext context) : + // dotenv = the name of the sub command that will be added to the m4g command + base("dotenv", "Generate a .env file in the current directory") + { + _context = context; + AddArgument(VarsArgument); + AddOption(PrintOption); + this.SetHandler(ExecuteAsync, VarsArgument, PrintOption); + } + + private async Task ExecuteAsync(string vars, bool overwrite) + { + var path = Path.Join(Environment.CurrentDirectory, ".env"); + var exists = File.Exists(path); + + if (exists && !overwrite) + { + Console.WriteLine($"File {path} already exists, add the -o or --overwrite flag to overwrite it."); + return; + } + + var content = string.Join(Environment.NewLine, vars.Split(';')); + await _context.CreateFile(".env", content); + } +} +``` + +## Install the plugin + +Open a terminal an make sure you're in the root directory of the plugin, i.e. the `My.Dotenv.Plugin` folder. +Run the following command to install the plugin. + +```bash +m4g plugin install --platform +``` + +Make sure to choose the right platform option for your machine. +If everything went well then running `m4g` should now also show a `dotenv` command. + +## Test the plugin + +Try running `m4g dotenv "FIRSTNAME=JOHN;LASTNAME=JOE"`, this should generate a `.env` in the current directory with the vars you specified. + +## Uninstall the plugin + +Uninstall the plugin by running `m4g plugin install My.Dotenv.Plugin`. + +## Resources + +For examples of how the core commands are implemented, you can take a look at the commands in the [MycroForge.CLI.Commands](https://git.devdisciples.com/devdisciples/mycroforge/src/branch/main/MycroForge.CLI/Commands) namespace. + +The MycroForge.CLI project uses [SystemCommand.Line](https://learn.microsoft.com/en-us/dotnet/standard/commandline/get-started-tutorial) for the CLI support, check out the Microsoft documentation for more info.