dotnet?命令行工具解決方案?PomeloCli詳解
PomeloCli 是什么
我們已經(jīng)有相當(dāng)多的命令行工具實(shí)現(xiàn)或解析類庫(kù),PomeloCli 并不是替代版本,它基于 Nate McMaster 的杰出工作 CommandLineUtils、DotNetCorePlugins 實(shí)現(xiàn)了一整套的命令行開發(fā)、管理、維護(hù)方案,在此特別鳴謝 Nate。
為什么實(shí)現(xiàn)
作者述職于 devOps 部門,編寫、維護(hù) CLI 工具并將其部署到各個(gè)服務(wù)器節(jié)點(diǎn)上是很常規(guī)的需求,但是又常常面臨一系列問題。
太多的工具太少的規(guī)范
命令行工具開發(fā)自由度過高,隨之而來(lái)的是迥異的開發(fā)和使用體驗(yàn):
- 依賴和配置管理混亂;
- 沒有一致的參數(shù)、選項(xiàng)標(biāo)準(zhǔn),缺失幫助命令;
- 永遠(yuǎn)找不到版本對(duì)號(hào)的說(shuō)明文檔;
基于二進(jìn)制拷貝分發(fā)難以為繼
工具開發(fā)完了還需要部署到計(jì)算節(jié)點(diǎn)上,但是對(duì)運(yùn)維人員極其不友好:
- 永遠(yuǎn)不知道哪些機(jī)器有沒有安裝,安裝了什么版本;
- 需要進(jìn)入工具目錄配置運(yùn)行參數(shù);
快速開始
你可以直接開始,但是在此之前理解命令、參數(shù)和選項(xiàng)仍然有很大的幫助。相關(guān)內(nèi)容可以參考 Introduction.
1. 引用 PomeloCli 開發(fā)命令行應(yīng)用
引用 PomeloCli 來(lái)快速創(chuàng)建自己的命令行應(yīng)用
$ dotnet new console -n SampleApp $ cd SampleApp $ dotnet add package PomeloCli -v 1.3.0
在入口程序添加必要的處理邏輯,文件內(nèi)容見于 docs/sample/3-sample-app/Program.cs。這里使用了依賴注入管理命令,相關(guān)參考見 .NET 依賴項(xiàng)注入。
using System; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using PomeloCli; class Program { static async Task<int> Main(string[] avg) { var services = new ServiceCollection() .AddTransient<ICommand, EchoCommand>() .AddTransient<ICommand, HeadCommand>() .BuildServiceProvider(); var application = ApplicationFactory.ConstructFrom(services); return await application.ExecuteAsync(args); } }
這里有兩個(gè)命令:EchoCommand,是對(duì) echo 命令的模擬,文件內(nèi)容見于 docs/sample/3-sample-app/EchoCommand.cs
#nullable disable using System; using System.Threading; using System.Threading.Tasks; using McMaster.Extensions.CommandLineUtils; using PomeloCli; [Command("echo", Description = "display a line of text")] class EchoCommand : Command { [Argument(0, "input")] public String Input { get; set; } [Option("-n|--newline", CommandOptionType.NoValue, Description = "do not output the trailing newline")] public Boolean? Newline { get; set; } protected override Task<int> OnExecuteAsync(CancellationToken cancellationToken) { if (Newline.HasValue) { Console.WriteLine(Input); } else { Console.Write(Input); } return Task.FromResult(0); } }
HeadCommand是對(duì) head 命令的模擬,文件內(nèi)容見于 docs/sample/3-sample-app/HeadCommand.cs。
#nullable disable using System; using System.ComponentModel.DataAnnotations; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using McMaster.Extensions.CommandLineUtils; using PomeloCli; [Command("head", Description = "Print the first 10 lines of each FILE to standard output")] class HeadCommand : Command { [Required] [Argument(0)] public String Path { get; set; } [Option("-n|--line", CommandOptionType.SingleValue, Description = "print the first NUM lines instead of the first 10")] public Int32 Line { get; set; } = 10; protected override Task<int> OnExecuteAsync(CancellationToken cancellationToken) { if (!File.Exists(Path)) { throw new FileNotFoundException($"file '{Path}' not found"); } var lines = File.ReadLines(Path).Take(Line); foreach (var line in lines) { Console.WriteLine(line); } return Task.FromResult(0); } }
進(jìn)入目錄 SampleApp 后,既可以通過 dotnet run -- --help
查看包含的 echo
和 head
命令及使用說(shuō)明。
$ dotnet run -- --help Usage: SampleApp [command] [options] Options: -?|-h|--help Show help information. Commands: echo display a line of text head Print the first 10 lines of each FILE to standard output Run 'SampleApp [command] -?|-h|--help' for more information about a command. $ dotnet run -- echo --help display a line of text Usage: SampleApp echo [options] <input> Arguments: input Options: -n|--newline do not output the trailing newline -?|-h|--help Show help information.
也可以編譯使用可執(zhí)行的 SampleApp.exe 。
$ ./bin/Debug/net8.0/SampleApp.exe --help Usage: SampleApp [command] [options] Options: -?|-h|--help Show help information. Commands: echo display a line of text head Print the first 10 lines of each FILE to standard output Run 'SampleApp [command] -?|-h|--help' for more information about a command. $ ./bin/Debug/net8.0/SampleApp.exe echo --help display a line of text Usage: SampleApp echo [options] <input> Arguments: input Options: -n|--newline do not output the trailing newline -?|-h|--help Show help information.
BRAVO 很簡(jiǎn)單對(duì)吧。
2. 引用 PomeloCli 開發(fā)命令行插件
如果只是提供命令行應(yīng)用的創(chuàng)建能力,作者大可不必發(fā)布這樣一個(gè)項(xiàng)目,因?yàn)?nbsp;McMaster.Extensions.CommandLineUtils 本身已經(jīng)做得足夠好了。如上文"為什么實(shí)現(xiàn)章節(jié)"所說(shuō),作者還希望解決命令行工具的分發(fā)維護(hù)問題。
為了實(shí)現(xiàn)這一目標(biāo),PomeloCli 繼續(xù)基于 McMaster.NETCore.Plugins 實(shí)現(xiàn)了一套插件系統(tǒng)或者說(shuō)架構(gòu):
- 將命令行工具拆分成宿主和插件兩部分功能;
- 宿主負(fù)責(zé)安裝、卸載、加載插件,作為命令行入口將參數(shù)轉(zhuǎn)交給對(duì)應(yīng)的插件;
- 插件負(fù)責(zé)具體的業(yè)務(wù)功能的實(shí)現(xiàn);
- 宿主和插件均打包成標(biāo)準(zhǔn)的 nuget 制品;
插件加載示意
命令行參數(shù)傳遞示意
通過將宿主的維護(hù)交由 dotnet tool 處理、將插件的維護(hù)交由宿主處理,我們希望解決命令行工具的分發(fā)維護(hù)問題:
- 開發(fā)人員
- 開發(fā)插件
- 使用
dotnet nuget push
發(fā)布插件
- 運(yùn)維/使用人員
- 使用
dotnet tool
安裝、更新、卸載宿主 - 使用
pomelo-cli install/uninstall
安裝、更新、卸載插件
- 使用
現(xiàn)在現(xiàn)在我們來(lái)開發(fā)一個(gè)插件應(yīng)用。
開發(fā)命令行插件
引用 PomeloCli 來(lái)創(chuàng)建自己的命令行插件
$ dotnet new classlib -n SamplePlugin $ cd SamplePlugin $ dotnet add package PomeloCli -v 1.3.0
我們把上文提到的 EchoCommand 和 HeadCommand 復(fù)制到該項(xiàng)目,再添加依賴注入文件 ServiceCollectionExtensions.cs,文件內(nèi)容見于 docs/sample/4-sample-plugin/ServiceCollectionExtensions.cs
using System; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using PomeloCli; public static class ServiceCollectionExtensions { /// <summary> /// pomelo-cli load plugin by this method, see /// <see cref="PomeloCli.Plugins.Runtime.PluginResolver.Loading()" /> /// </summary> /// <param name="services"></param> /// <returns></returns> public static IServiceCollection AddCommands(this IServiceCollection services) { return services .AddTransient<ICommand, EchoCommand>() .AddTransient<ICommand, HeadCommand>(); } }
為了能夠使得插件運(yùn)行起來(lái),我們還需要在打包時(shí)將依賴添加到 nupkg 文件中。為此需要修改 csproj 添加打包配置,參考 docs/sample/4-sample-plugin/SamplePlugin.csproj,相關(guān)原理見出處 How to include package reference files in your nuget
搭建私有 nuget 服務(wù)
為了托管我們的工具與插件,我們這里使用 BaGet 搭建輕量的 nuget 服務(wù),docker-compose.yaml 已經(jīng)提供見 baget。docker 等工具使用請(qǐng)自行查閱。
version: "3.3" services: baget: image: loicsharma/baget container_name: baget ports: - "8000:80" volumes: - $PWD/data:/var/baget
我們使用 docker-compose up -d
將其運(yùn)行起來(lái),baget 將在地址 http://localhost:8000/ 上提供服務(wù)。
發(fā)布命令行插件
現(xiàn)我們?cè)谟辛瞬寮?nuget 服務(wù),可以發(fā)布插件了。
$ cd SamplePlugin $ dotnet pack -o nupkgs -c Debug $ dotnet nuget push -s http://localhost:8000/v3/index.json nupkgs/SamplePlugin.1.0.0.nupkg
3. 使用 PomeloCli 集成已發(fā)布插件
pomelo-cli 是一個(gè) dotnet tool 應(yīng)用,可以看作命令行宿主,它包含了一組 plugin 命令用來(lái)管理我們的命令行插件。
安裝命令行宿主
我們使用標(biāo)準(zhǔn)的 dotnet tool CLI 命令安裝 PomeloCli,相關(guān)參考見 How to manage .NET tools
$ dotnet tool install PomeloCli.Host --version 1.3.0 -g $ pomelo-cli --help Usage: PomeloCli.Host [command] [options] Options: -?|-h|--help Show help information. Commands: config plugin version Run 'PomeloCli.Host [command] -?|-h|--help' for more information about a command.
可以看到 pomelo-cli 內(nèi)置了部分命令。
集成命令行插件
pomelo-cli 內(nèi)置了一組插件,包含了其他插件的管理命令
$ pomelo-cli plugin --help Usage: PomeloCli.Host plugin [command] [options] Options: -?|-h|--help Show help information. Commands: install list uninstall Run 'plugging [command] -?|-h|--help' for more information about a command.
我們用 plugin install
命令安裝剛剛發(fā)布的插件 SamplePlugin
$ pomelo-cli plugin install SamplePlugin -v 1.0.0 -s http://localhost:8000/v3/index.json $ pomelo-cli --help Usage: PomeloCli.Host [command] [options] Options: -?|-h|--help Show help information. Commands: config echo display a line of text head Print the first 10 lines of each FILE to standard output plugin version Run 'PomeloCli.Host [command] -?|-h|--help' for more information about a command. $ pomelo-cli echo --help display a line of text Usage: PomeloCli.Host echo [options] <input> Arguments: input Options: -n|--newline do not output the trailing newline -?|-h|--help Show help information.
可以看到 SamplePlugin 包含的 echo 和 head 命令已經(jīng)被顯示在子命令列表中。
卸載命令行插件
pomelo-cli 當(dāng)然也可以卸載其他插件
$ pomelo-cli plugin uninstall SamplePlugin
卸載命令行宿主
我們使用標(biāo)準(zhǔn)的 dotnet tool CLI 命令卸載 PomeloCli
$ dotnet tool uninstall PomeloCli.Host -g
4. 引用 PomeloCli 開發(fā)命令行宿主
你可能需要自己的命令行宿主,這也很容易。
$ dotnet new console -n SampleHost $ cd SampleHost/ $ dotnet add package PomeloCli $ dotnet add package PomeloCli.Plugins
修改 Program.cs 替換為以下內(nèi)容
using System; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using PomeloCli; using PomeloCli.Plugins; class Program { static async Task<int> Main(string[] args) { var services = new ServiceCollection() .AddPluginSupport() .BuildServiceProvider(); var applicationFactory = new ApplicationFactory(services); var application = applicationFactory.ConstructRootApp(); return await application.ExecuteAsync(args); } }
現(xiàn)在你得到了一個(gè)命令宿主,你可以運(yùn)行它,甚至用它安裝插件
$ dotnet build $ ./bin/Debug/net8.0/SampleHost.exe --help Usage: SampleHost [command] [options] Options: -?|-h|--help Show help information. Commands: plugin Run 'SampleHost [command] -?|-h|--help' for more information about a command. $ ./bin/Debug/net8.0/SampleHost.exe plugin install SamplePlugin -v 1.0.0 -s http://localhost:8000/v3/index.json ... $ ./bin/Debug/net8.0/SampleHost.exe --help Usage: SampleHost [command] [options] Options: -?|-h|--help Show help information. Commands: echo display a line of text head Print the first 10 lines of each FILE to standard output plugin Run 'SampleHost [command] -?|-h|--help' for more information about a command.
其他:異常 NU1102 的處理
當(dāng)安裝插件失敗且錯(cuò)誤碼是NU1102 時(shí),表示未找到對(duì)應(yīng)版本,可以執(zhí)行命令 $ dotnet nuget locals http-cache --clear
以清理 HTTP 緩存。
info : Restoring packages for C:\Users\leon\.PomeloCli.Host\Plugin.csproj... info : GET http://localhost:8000/v3/package/sampleplugin/index.json info : OK http://localhost:8000/v3/package/sampleplugin/index.json 2ms error: NU1102: Unable to find package Sample Plugin with version (>= 1.1.0) error: - Found 7 version(s) in http://localhost:8000/v3/index.json [ Nearest version: 1.0.0 ] error: Package 'SamplePlugin' is incompatible with 'user specified' frameworks in project 'C:\Users\leon\.PomeloCli.Host\Plugin.csproj'.
其他事項(xiàng)
已知問題
- refit 支持存在問題
路線圖
- 業(yè)務(wù)插件配置
到此這篇關(guān)于dotnet 命令行工具解決方案 PomeloCli的文章就介紹到這了,更多相關(guān)dotnet 命令行工具解決方案 PomeloCli內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Asp.net把圖片存入數(shù)據(jù)庫(kù)和讀取圖片的方法
網(wǎng)上關(guān)于ASP.NET上傳圖片到數(shù)據(jù)庫(kù)的資料非常多,常用的如下:存儲(chǔ)圖片類型數(shù)據(jù)有以下幾種方式2013-07-07ASP.NET?Core獲取正確查詢字符串參數(shù)示例
這篇文章主要為大家介紹了ASP.NET?Core正確獲取查詢字符串參數(shù),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05Asp.Net服務(wù)器發(fā)送HTTP標(biāo)頭后無(wú)法設(shè)置內(nèi)容類型的問題解決
這篇文章主要給大家介紹了Asp.Net服務(wù)器發(fā)送HTTP標(biāo)頭后無(wú)法設(shè)置內(nèi)容類型問題的解決方法,文中介紹的非常詳細(xì),對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起看看吧。2017-05-05asp.net 讀取xml文件里面的內(nèi)容,綁定到dropdownlist中
asp.net 讀取xml文件里面的內(nèi)容,綁定到dropdownlist中的實(shí)現(xiàn)代碼。2009-05-05阿里云上從ASP.NET線程角度對(duì)“黑色30秒”問題的全新分析
在這篇博文中,我們拋開對(duì)阿里云的懷疑,完全從ASP.NET的角度進(jìn)行分析,看能不能找到針對(duì)問題現(xiàn)象的更合理的解釋2015-09-09ASP.NET數(shù)據(jù)庫(kù)編程之Access連接失敗
ASP.NET數(shù)據(jù)庫(kù)編程之Access連接失敗...2006-09-09jQuery 插件autocomplete自動(dòng)完成應(yīng)用(自動(dòng)補(bǔ)全)(asp.net后臺(tái))
項(xiàng)目中有時(shí)會(huì)用到自動(dòng)補(bǔ)全查詢,就像Google搜索框、淘寶商品搜索功能,輸入漢字或字母,則以該漢字或字母開頭的相關(guān)條目會(huì)顯示出來(lái)供用戶選擇, autocomplete插件就是完成這樣的功能2011-10-10Asp.net保持頁(yè)面滾動(dòng)條位置(頁(yè)面提交后不變)
ASP.net:保持頁(yè)面中滾動(dòng)條的位置,使得頁(yè)面提交后還在原來(lái)的位置,對(duì)于頁(yè)面中DIV等等的滾動(dòng)條控制,我在看了別人代碼后,寫了一個(gè),感興趣的朋友可以參考下哈2013-04-04