實現一個更新所有 dotnet tool 的 dotnet tool
Intro
dotnet tool 是從 .NET Core 2.1 開始支持的命令行工具,在使用 dotnet tool 比較多了的時候,想要更新所有的 dotnet tool 就比較麻煩,而目前 .NET SDK 還不支持,也有一些人希望能夠支持一次更新所有的 dotnet tool 這樣我們就不需要一個一個地去更新了
Implement
實現思路比較簡單,獲取所有的 dotnet tool 并逐個進行更新
我們可以通過 dotnet tool list -g
command 來獲取我們安裝的所有的 dotnet global tool,輸出結果如下:
第一行是一個標題,第二行是分隔線,從第三行開始是 dotnet tool 數據
第一列是 NuGet package id, 第二列是 package version, 第三列是 dotnet tool 對應的命令,我們可以獲取 dotnet tool list -g
?輸出并從第三行開始讀取數據,只需要讀取第一列數據的 package id 即可
獲取所有 dotnet tool list 的代碼如下:
//?get?dotnet?path
var?dotnetPath?=?ApplicationHelper.GetDotnetPath();
//?execute?`dotnet?tool?list?-g`?to?get?all?dotnet?tool
var?dotnetToolListOutput?=?await?CommandExecutor.ExecuteAndCaptureAsync(dotnetPath,?"tool?list?-g");
//?output
Console.WriteLine("`dotnet?tool?list?-g`?output:");
Console.WriteLine(dotnetToolListOutput.StandardOut);
拿到輸出結果之后,我們就可以得到 dotnet tool list 并開始逐個更新了,實現如下:
var?dotnetToolList?=?dotnetToolListOutput.StandardOut.Split(Environment.NewLine,?StringSplitOptions.RemoveEmptyEntries);
if?(dotnetToolList.Length?>?2)
{foreach?(var?tool?in?dotnetToolList[2..]){var?toolId?=?tool.Split('?',?StringSplitOptions.RemoveEmptyEntries)[0].Trim();if?(fullToolName.Equals(toolId)){continue;}Console.WriteLine($"update?tool?{toolId}...");try{await?CommandExecutor.ExecuteAsync(dotnetPath,?$"tool?update?-g?{toolId}");Console.WriteLine($"update?tool?{toolId}?completed");}catch?(Exception?ex){Console.WriteLine($"update?tool?{toolId}?failed:?{ex}");}}
}
至此我們的核心邏輯就基本完成了,實現效果如下:
如果要發布還要準備一下 nuget package 的一些信息
<PropertyGroup><IsPackable>true</IsPackable><PackAsTool>true</PackAsTool><ToolCommandName>dotnet-update-all-tools</ToolCommandName><PackageId>dotnet-update-all-tools</PackageId><Version>1.0.0</Version>
</PropertyGroup>
PackAsTool
意味著我們要打包一個 dotnet tool
ToolCommandName
是我們 tool 安裝后對應的執行命令
More
上面的 ApplicationHelper.GetDotnetPath()
實現是通過掃描 PATH
找到可執行的dotne path,有些 platform 上需要 full path 才能工作,實現可以參考:https://github.com/WeihanLi/WeihanLi.Common/blob/86b6acc10f50df5867c590ca10039c1e7a8bb740/src/WeihanLi.Common/Helpers/ApplicationHelper.cs#L61
CommandExecutor
實現是起了一個進程并捕獲進程輸出,可以參考:https://github.com/WeihanLi/WeihanLi.Common/blob/86b6acc10f50df5867c590ca10039c1e7a8bb740/src/WeihanLi.Common/Helpers/CommandExecutor.cs#L45
最終的 dotnet tool
?在 System.CommandLine
的基礎上包裝了一下,以為了更好的體驗和擴展的方便
var?command?=?new?Command(toolName);
command.SetHandler(async?()?=>
{//?前面的邏輯在這里
});
await?command.InvokeAsync(args);
自此我們的 dotnet tool 就完成了
可以通過下面的命令來進行安裝
dotnet?tool?install?--global?dotnet-update-all-tools
安裝好之后執行 dotnet-update-all-tools
即可更新所有的 dotnet tool 了
References
https://www.nuget.org/packages/dotnet-update-all-tools
https://github.com/WeihanLi/SamplesInPractice/tree/master/CommandLineAppSamples/UpgradeAllTools
https://github.com/WeihanLi/WeihanLi.Common/blob/86b6acc10f50df5867c590ca10039c1e7a8bb740/src/WeihanLi.Common/Helpers/ApplicationHelper.cs#L61
https://github.com/WeihanLi/WeihanLi.Common/blob/86b6acc10f50df5867c590ca10039c1e7a8bb740/src/WeihanLi.Common/Helpers/CommandExecutor.cs#L45