From f18b040c00efbb2c5e4a7998803c535616310a6b Mon Sep 17 00:00:00 2001 From: Mirko Da Corte Date: Tue, 5 Mar 2024 18:40:13 +0100 Subject: [PATCH] Implement Program class and basic architecture --- .editorconfig | 3 + EthernaGatewayCli.sln.DotSettings | 4 +- README.md | 15 +- .../Commands/DownloadCommand.cs | 37 ++++ src/EthernaGatewayCli/Commands/ICommand.cs | 29 +++ .../Commands/PostageCommand.cs | 37 ++++ .../Commands/ResourceCommand.cs | 37 ++++ .../Commands/UploadCommand.cs | 37 ++++ src/EthernaGatewayCli/CommonConsts.cs | 26 +++ .../EthernaGatewayCli.csproj | 48 ++++- .../Models/GitHubDto/GitReleaseVersionDto.cs | 33 ++++ src/EthernaGatewayCli/Program.cs | 187 +++++++++++++++++- .../ServiceCollectionExtensions.cs | 52 +++++ .../Services/GatewayService.cs | 22 +++ .../Services/IGatewayService.cs | 23 +++ .../Utilities/EthernaVersionControl.cs | 99 ++++++++++ 16 files changed, 677 insertions(+), 12 deletions(-) create mode 100644 src/EthernaGatewayCli/Commands/DownloadCommand.cs create mode 100644 src/EthernaGatewayCli/Commands/ICommand.cs create mode 100644 src/EthernaGatewayCli/Commands/PostageCommand.cs create mode 100644 src/EthernaGatewayCli/Commands/ResourceCommand.cs create mode 100644 src/EthernaGatewayCli/Commands/UploadCommand.cs create mode 100644 src/EthernaGatewayCli/CommonConsts.cs create mode 100644 src/EthernaGatewayCli/Models/GitHubDto/GitReleaseVersionDto.cs create mode 100644 src/EthernaGatewayCli/ServiceCollectionExtensions.cs create mode 100644 src/EthernaGatewayCli/Services/GatewayService.cs create mode 100644 src/EthernaGatewayCli/Services/IGatewayService.cs create mode 100644 src/EthernaGatewayCli/Utilities/EthernaVersionControl.cs diff --git a/.editorconfig b/.editorconfig index 3a8ac05..355cdfe 100644 --- a/.editorconfig +++ b/.editorconfig @@ -16,6 +16,9 @@ indent_size = 2 # C# files [*.cs] +# CA1056: Uri properties should not be strings +dotnet_diagnostic.CA1056.severity = none + # CA1303: Do not pass literals as localized parameters dotnet_diagnostic.CA1303.severity = none # Don't need translated exceptions diff --git a/EthernaGatewayCli.sln.DotSettings b/EthernaGatewayCli.sln.DotSettings index 2172c3b..b0426c8 100644 --- a/EthernaGatewayCli.sln.DotSettings +++ b/EthernaGatewayCli.sln.DotSettings @@ -11,4 +11,6 @@ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and - limitations under the License. \ No newline at end of file + limitations under the License. + True + True \ No newline at end of file diff --git a/README.md b/README.md index efc7573..72a238a 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,20 @@ Etherna Gateway CLI requires at least [.NET 8 Runtime](https://dotnet.microsoft. ### How to use ``` - +Usage: etherna [OPTIONS] COMMAND + +Commands: + download Download a resource from Swarm + postage Manage postage batches + resource Manage resources on Gateway + upload Upload a resource to Swarm + +General Options: + -k, --api-key Api Key (optional) + -i, --ignore-update Ignore new version of EthernaGatewayCli + +Run 'etherna -h' or 'etherna --help' to print help. +Run 'etherna COMMAND -h' or 'etherna COMMAND --help' for more information on a command. ``` # Issue reports diff --git a/src/EthernaGatewayCli/Commands/DownloadCommand.cs b/src/EthernaGatewayCli/Commands/DownloadCommand.cs new file mode 100644 index 0000000..fbc8b3d --- /dev/null +++ b/src/EthernaGatewayCli/Commands/DownloadCommand.cs @@ -0,0 +1,37 @@ +// Copyright 2024-present Etherna SA +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Threading.Tasks; + +namespace Etherna.GatewayCli.Commands +{ + public class DownloadCommand : ICommand + { + // Properties. + public string Description => "Download a resource from Swarm"; + public string Name => "download"; + + // Methods. + public void PrintHelp() + { + throw new NotImplementedException(); + } + + public Task RunAsync(string[] args) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/src/EthernaGatewayCli/Commands/ICommand.cs b/src/EthernaGatewayCli/Commands/ICommand.cs new file mode 100644 index 0000000..48fb0d1 --- /dev/null +++ b/src/EthernaGatewayCli/Commands/ICommand.cs @@ -0,0 +1,29 @@ +// Copyright 2024-present Etherna SA +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Threading.Tasks; + +namespace Etherna.GatewayCli.Commands +{ + public interface ICommand + { + // Properties. + string Description { get; } + string Name { get; } + + // Methods. + void PrintHelp(); + Task RunAsync(string[] args); + } +} \ No newline at end of file diff --git a/src/EthernaGatewayCli/Commands/PostageCommand.cs b/src/EthernaGatewayCli/Commands/PostageCommand.cs new file mode 100644 index 0000000..b58d2af --- /dev/null +++ b/src/EthernaGatewayCli/Commands/PostageCommand.cs @@ -0,0 +1,37 @@ +// Copyright 2024-present Etherna SA +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Threading.Tasks; + +namespace Etherna.GatewayCli.Commands +{ + public class PostageCommand : ICommand + { + // Properties. + public string Description => "Manage postage batches"; + public string Name => "postage"; + + // Methods. + public void PrintHelp() + { + throw new NotImplementedException(); + } + + public Task RunAsync(string[] args) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/src/EthernaGatewayCli/Commands/ResourceCommand.cs b/src/EthernaGatewayCli/Commands/ResourceCommand.cs new file mode 100644 index 0000000..36abd87 --- /dev/null +++ b/src/EthernaGatewayCli/Commands/ResourceCommand.cs @@ -0,0 +1,37 @@ +// Copyright 2024-present Etherna SA +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Threading.Tasks; + +namespace Etherna.GatewayCli.Commands +{ + public class ResourceCommand : ICommand + { + // Properties. + public string Description => "Manage resources on Gateway"; + public string Name => "resource"; + + // Methods. + public void PrintHelp() + { + throw new NotImplementedException(); + } + + public Task RunAsync(string[] args) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/src/EthernaGatewayCli/Commands/UploadCommand.cs b/src/EthernaGatewayCli/Commands/UploadCommand.cs new file mode 100644 index 0000000..859f34d --- /dev/null +++ b/src/EthernaGatewayCli/Commands/UploadCommand.cs @@ -0,0 +1,37 @@ +// Copyright 2024-present Etherna SA +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Threading.Tasks; + +namespace Etherna.GatewayCli.Commands +{ + public class UploadCommand : ICommand + { + // Properties. + public string Description => "Upload a resource to Swarm"; + public string Name => "upload"; + + // Methods. + public void PrintHelp() + { + throw new NotImplementedException(); + } + + public Task RunAsync(string[] args) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/src/EthernaGatewayCli/CommonConsts.cs b/src/EthernaGatewayCli/CommonConsts.cs new file mode 100644 index 0000000..d61d8af --- /dev/null +++ b/src/EthernaGatewayCli/CommonConsts.cs @@ -0,0 +1,26 @@ +// Copyright 2024-present Etherna SA +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Etherna.BeeNet.Clients.GatewayApi; + +namespace Etherna.GatewayCli +{ + public static class CommonConsts + { + public const GatewayApiVersion BeeNodeGatewayVersion = GatewayApiVersion.v5_0_0; + public const string EthernaGatewayCliClientId = "689efb99-e2a3-4cb5-ba86-d1e07a71991f"; + public const string EthernaGatewayUrl = "https://gateway.etherna.io/"; + public const string EthernaSsoUrl = "https://sso.etherna.io/"; + } +} \ No newline at end of file diff --git a/src/EthernaGatewayCli/EthernaGatewayCli.csproj b/src/EthernaGatewayCli/EthernaGatewayCli.csproj index 2f4fc77..793962b 100644 --- a/src/EthernaGatewayCli/EthernaGatewayCli.csproj +++ b/src/EthernaGatewayCli/EthernaGatewayCli.csproj @@ -1,10 +1,46 @@  - - Exe - net8.0 - enable - enable - + + Exe + net8.0 + Etherna.GatewayCli + + Etherna SA + A CLI interface to the Etherna Gateway + + true + enable + true + AllEnabledByDefault + + true + + etherna + https://github.com/Etherna/etherna-gateway-cli + git + true + true + snupkg + LICENSE + true + true + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + diff --git a/src/EthernaGatewayCli/Models/GitHubDto/GitReleaseVersionDto.cs b/src/EthernaGatewayCli/Models/GitHubDto/GitReleaseVersionDto.cs new file mode 100644 index 0000000..4d97cfe --- /dev/null +++ b/src/EthernaGatewayCli/Models/GitHubDto/GitReleaseVersionDto.cs @@ -0,0 +1,33 @@ +// Copyright 2024-present Etherna SA +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; + +namespace Etherna.GatewayCli.Models.GitHubDto +{ + public class GitReleaseVersionDto + { + // Properties. + public string Assets_url { get; set; } = default!; + public DateTime Created_at { get; set; } + public bool Draft { get; set; } + public string Html_url { get; set; } = default!; + public int Id { get; set; } = default!; + public string Name { get; set; } = default!; + public bool Prerelease { get; set; } + public DateTime Published_at { get; set; } + public string Tag_name { get; set; } = default!; + public string Url { get; set; } = default!; + } +} \ No newline at end of file diff --git a/src/EthernaGatewayCli/Program.cs b/src/EthernaGatewayCli/Program.cs index 42e0f91..6cc8617 100644 --- a/src/EthernaGatewayCli/Program.cs +++ b/src/EthernaGatewayCli/Program.cs @@ -12,12 +12,191 @@ // See the License for the specific language governing permissions and // limitations under the License. -namespace EthernaGatewayCli; +using Etherna.GatewayCli.Commands; +using Etherna.GatewayCli.Utilities; +using Etherna.Sdk.Users.Native; +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; -class Program +namespace Etherna.GatewayCli { - static void Main(string[] args) + internal sealed class Program { - Console.WriteLine("Hello, World!"); + // Consts. + private static readonly string[] ApiScopes = ["userApi.gateway"]; + private const string CommandsNamespace = "Etherna.GatewayCli.Commands"; + private const string HttpClientName = "ethernaAuthnHttpClient"; + + // Public methods. + static async Task Main(string[] args) + { + // Parse arguments. + string? apiKey = null; + bool ignoreUpdate = false; + bool printHelp = false; + + //print help + if (args.Length == 0) + { + printHelp = true; + } + else if (args.Length == 1) + { + switch (args[0]) + { + case "-h": + case "--help": + printHelp = true; + break; + } + } + + //general options + var generalOptionsArgsCount = 0; + for (; generalOptionsArgsCount < args.Length && args[generalOptionsArgsCount].StartsWith('-'); generalOptionsArgsCount++) + { + switch (args[generalOptionsArgsCount]) + { + case "-k": + case "--api-key": + if (args.Length == generalOptionsArgsCount + 1) + throw new ArgumentException("Api Key is missing"); + apiKey = args[++generalOptionsArgsCount]; + break; + + case "-i": + case "--ignore-update": + ignoreUpdate = true; + break; + + default: + throw new ArgumentException(args[generalOptionsArgsCount] + " is not a valid general option"); + } + } + + // Check for new versions. + var newVersionAvailable = await EthernaVersionControl.CheckNewVersionAsync(); + if (newVersionAvailable && !ignoreUpdate) + return; + + // Register etherna service clients. + var services = new ServiceCollection(); + IEthernaUserClientsBuilder ethernaClientsBuilder; + if (apiKey is null) //"code" grant flow + { + ethernaClientsBuilder = services.AddEthernaUserClientsWithCodeAuth( + CommonConsts.EthernaSsoUrl, + CommonConsts.EthernaGatewayCliClientId, + null, + 11430, + ApiScopes, + HttpClientName, + c => + { + c.Timeout = TimeSpan.FromMinutes(30); + }); + } + else //"password" grant flow + { + ethernaClientsBuilder = services.AddEthernaUserClientsWithApiKeyAuth( + CommonConsts.EthernaSsoUrl, + apiKey, + ApiScopes, + HttpClientName, + c => + { + c.Timeout = TimeSpan.FromMinutes(30); + }); + } + ethernaClientsBuilder.AddEthernaGatewayClient(new Uri(CommonConsts.EthernaGatewayUrl)); + + // Setup DI. + var availableCommandTypes = typeof(Program).GetTypeInfo().Assembly.GetTypes() + .Where(t => t is { IsClass: true, Namespace: CommandsNamespace }) + .Where(t => t.GetInterfaces().Contains(typeof(ICommand))) + .OrderBy(t => t.Name); + + services.AddCoreServices( + availableCommandTypes, + HttpClientName); + + var serviceProvider = services.BuildServiceProvider(); + + // Run command. + var availableCommands = + availableCommandTypes.Select(t => (ICommand)serviceProvider.GetRequiredService(t)); + + if (printHelp) + { + PrintHelp(availableCommands); + } + else //select and run command + { + var commandName = args[0]; + var commandArgs = args[1..]; + + var selectedCommand = availableCommands.FirstOrDefault(c => c.Name == commandName); + if (selectedCommand is null) + Console.WriteLine($"etherna: '{commandName}' is not an etherna command."); + else + await selectedCommand.RunAsync(commandArgs); + } + } + + // Helpers. + [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")] + [SuppressMessage("Performance", "CA1851:Possible multiple enumerations of \'IEnumerable\' collection")] + private static void PrintHelp( + IEnumerable availableCommands) + { + var strBuilder = new StringBuilder(); + + // Add usage. + strBuilder.AppendLine( + $$""" + Usage: etherna [OPTIONS] COMMAND + """); + strBuilder.AppendLine(); + + // Add commands. + strBuilder.AppendLine("Commands:"); + var descriptionShift = availableCommands.Select(c => c.Name.Length).Max() + 4; + foreach (var command in availableCommands) + { + strBuilder.Append(" "); + strBuilder.Append(command.Name); + for (int i = 0; i < descriptionShift - command.Name.Length; i++) + strBuilder.Append(' '); + strBuilder.AppendLine(command.Description); + } + strBuilder.AppendLine(); + + // Add general options. + strBuilder.AppendLine( + $$""" + General Options: + -k, --api-key Api Key (optional) + -i, --ignore-update Ignore new version of EthernaGatewayCli + """); + strBuilder.AppendLine(); + + // Add print help. + strBuilder.AppendLine( + $$""" + Run 'etherna -h' or 'etherna --help' to print help. + Run 'etherna COMMAND -h' or 'etherna COMMAND --help' for more information on a command. + """); + strBuilder.AppendLine(); + + // Print it. + var helpOutput = strBuilder.ToString(); + Console.Write(helpOutput); + } } } \ No newline at end of file diff --git a/src/EthernaGatewayCli/ServiceCollectionExtensions.cs b/src/EthernaGatewayCli/ServiceCollectionExtensions.cs new file mode 100644 index 0000000..99a019e --- /dev/null +++ b/src/EthernaGatewayCli/ServiceCollectionExtensions.cs @@ -0,0 +1,52 @@ +// Copyright 2024-present Etherna SA +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Etherna.BeeNet; +using Etherna.BeeNet.Clients.GatewayApi; +using Etherna.GatewayCli.Services; +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Collections.Generic; +using System.Net.Http; + +namespace Etherna.GatewayCli +{ + internal static class ServiceCollectionExtensions + { + public static void AddCoreServices( + this IServiceCollection services, + IEnumerable availableCommandTypes, + string httpClientName) + { + // Add commands. + foreach (var commandType in availableCommandTypes) + services.AddTransient(commandType); + + // Add transient services. + services.AddTransient(); + + // Add singleton services. + //bee.net + services.AddSingleton((sp) => + { + var httpClientFactory = sp.GetRequiredService(); + return new BeeGatewayClient( + httpClientFactory.CreateClient(httpClientName), + new Uri(CommonConsts.EthernaGatewayUrl), + CommonConsts.BeeNodeGatewayVersion); + }); + services.AddSingleton(); + } + } +} \ No newline at end of file diff --git a/src/EthernaGatewayCli/Services/GatewayService.cs b/src/EthernaGatewayCli/Services/GatewayService.cs new file mode 100644 index 0000000..aa7ff81 --- /dev/null +++ b/src/EthernaGatewayCli/Services/GatewayService.cs @@ -0,0 +1,22 @@ +// Copyright 2024-present Etherna SA +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Etherna.GatewayCli.Services +{ + public class GatewayService : IGatewayService + + { + + } +} \ No newline at end of file diff --git a/src/EthernaGatewayCli/Services/IGatewayService.cs b/src/EthernaGatewayCli/Services/IGatewayService.cs new file mode 100644 index 0000000..0ded74a --- /dev/null +++ b/src/EthernaGatewayCli/Services/IGatewayService.cs @@ -0,0 +1,23 @@ +// Copyright 2024-present Etherna SA +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Etherna.GatewayCli.Services +{ +#pragma warning disable CA1040 + public interface IGatewayService +#pragma warning restore CA1040 + { + + } +} \ No newline at end of file diff --git a/src/EthernaGatewayCli/Utilities/EthernaVersionControl.cs b/src/EthernaGatewayCli/Utilities/EthernaVersionControl.cs new file mode 100644 index 0000000..aca68a2 --- /dev/null +++ b/src/EthernaGatewayCli/Utilities/EthernaVersionControl.cs @@ -0,0 +1,99 @@ +// Copyright 2024-present Etherna SA +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Etherna.GatewayCli.Models.GitHubDto; +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Net.Http; +using System.Net.Http.Json; +using System.Reflection; +using System.Threading.Tasks; + +namespace Etherna.GatewayCli.Utilities +{ + public static class EthernaVersionControl + { + // Fields. + private static Version? _currentVersion; + + // Properties. + [SuppressMessage("Design", "CA1065:Do not raise exceptions in unexpected locations")] + public static Version CurrentVersion + { + get + { + if (_currentVersion is null) + { + var assemblyVersion = Assembly.GetExecutingAssembly().GetCustomAttribute()?.Version ?? + throw new InvalidOperationException("Invalid assembly version"); + _currentVersion = new Version(assemblyVersion); + } + return _currentVersion; + } + } + + // Public methods. + public static async Task CheckNewVersionAsync() + { + // Get current version. + Console.WriteLine(); + Console.WriteLine($"Etherna Gateway CLI (v{CurrentVersion})"); + Console.WriteLine(); + + // Get last version form github releases. + try + { + using HttpClient httpClient = new(); + httpClient.DefaultRequestHeaders.Add("User-Agent", "EthernaImportClient"); + var gitUrl = "https://api.github.com/repos/Etherna/etherna-gateway-cli/releases"; + var response = await httpClient.GetAsync(gitUrl); + var gitReleaseVersionsDto = await response.Content.ReadFromJsonAsync>(); + + if (gitReleaseVersionsDto is null || gitReleaseVersionsDto.Count == 0) + return false; + + var lastVersion = gitReleaseVersionsDto + .Select(git => new + { + Version = new Version(git.Tag_name.Replace("v", "", StringComparison.OrdinalIgnoreCase)), + Url = git.Html_url + }) + .OrderByDescending(v => v.Version) + .First(); + + if (lastVersion.Version > CurrentVersion) + { + Console.WriteLine($"A new release is available: {lastVersion.Version}"); + Console.WriteLine($"Upgrade now, or check out the release page at:"); + Console.WriteLine($" {lastVersion.Url}"); + return true; + } + else + return false; + } +#pragma warning disable CA1031 + catch (Exception ex) +#pragma warning restore CA1031 + { + Console.ForegroundColor = ConsoleColor.DarkRed; + Console.WriteLine("Unable to check last version on GitHub"); + Console.WriteLine($"Error: {ex.Message}"); + Console.ResetColor(); + return false; + } + } + } +} \ No newline at end of file