diff --git a/ECoreNetto.Tools.Tests/ECoreNetto.Tools.Tests.csproj b/ECoreNetto.Tools.Tests/ECoreNetto.Tools.Tests.csproj index b0e7464..2578eee 100644 --- a/ECoreNetto.Tools.Tests/ECoreNetto.Tools.Tests.csproj +++ b/ECoreNetto.Tools.Tests/ECoreNetto.Tools.Tests.csproj @@ -46,4 +46,8 @@ + + + + diff --git a/ECoreNetto.Tools.Tests/Services/VersionCheckerTestFixture.cs b/ECoreNetto.Tools.Tests/Services/VersionCheckerTestFixture.cs new file mode 100644 index 0000000..1b81db7 --- /dev/null +++ b/ECoreNetto.Tools.Tests/Services/VersionCheckerTestFixture.cs @@ -0,0 +1,74 @@ +// ------------------------------------------------------------------------------------------------- +// +// +// Copyright 2017-2024 Starion Group S.A. +// +// 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 ECoreNetto.Tools.Tests.Services +{ + using System; + using System.Net.Http; + using System.Threading.Tasks; + using Microsoft.Extensions.Logging; + using NUnit.Framework; + using Serilog; + using Tools.Services; + + [TestFixture] + public class VersionCheckerTestFixture + { + private VersionChecker versionChecker; + + private ILoggerFactory? loggerFactory; + + [OneTimeSetUp] + public void OneTimeSetUp() + { + Log.Logger = new LoggerConfiguration() + .MinimumLevel.Verbose() + .WriteTo.Console() + .CreateLogger(); + + this.loggerFactory = LoggerFactory.Create(builder => + { + builder.AddSerilog(); + }); + } + + [SetUp] + public void SetUp() + { + var httpClient = new HttpClient(); + httpClient.Timeout = TimeSpan.FromSeconds(5); + + this.versionChecker = new VersionChecker(httpClient, this.loggerFactory); + } + + [Test] + public async Task Verify_that_Query_version_returns_result() + { + var result = await this.versionChecker.QueryLatestRelease(); + + Assert.That(result, Is.Not.Null); + + Log.Logger.Information(result.TagName); + Log.Logger.Information(result.Body); + Log.Logger.Information(result.HtmlUrl); + + } + } +} \ No newline at end of file diff --git a/ECoreNetto.Tools/Commands/MarkdownReportCommand.cs b/ECoreNetto.Tools/Commands/MarkdownReportCommand.cs index 7429f2c..b27a067 100644 --- a/ECoreNetto.Tools/Commands/MarkdownReportCommand.cs +++ b/ECoreNetto.Tools/Commands/MarkdownReportCommand.cs @@ -25,7 +25,7 @@ namespace ECoreNetto.Tools.Commands using System.IO; using ECoreNetto.Reporting.Generators; - + /// /// The that generates a Markdown report /// diff --git a/ECoreNetto.Tools/Commands/ModelInspectionCommand.cs b/ECoreNetto.Tools/Commands/ModelInspectionCommand.cs index c3ae55b..17c5016 100644 --- a/ECoreNetto.Tools/Commands/ModelInspectionCommand.cs +++ b/ECoreNetto.Tools/Commands/ModelInspectionCommand.cs @@ -25,7 +25,7 @@ namespace ECoreNetto.Tools.Commands using System.IO; using ECoreNetto.Reporting.Generators; - + /// /// The that inspects an ECore model and generates /// a text report diff --git a/ECoreNetto.Tools/Commands/ReportHandler.cs b/ECoreNetto.Tools/Commands/ReportHandler.cs index 6ac9a24..0bbc9af 100644 --- a/ECoreNetto.Tools/Commands/ReportHandler.cs +++ b/ECoreNetto.Tools/Commands/ReportHandler.cs @@ -20,8 +20,8 @@ namespace ECoreNetto.Tools.Commands { - using System.CommandLine.Invocation; using System; + using System.CommandLine.Invocation; using System.Diagnostics; using System.IO; using System.Threading; diff --git a/ECoreNetto.Tools/ECoreNetto.Tools.csproj b/ECoreNetto.Tools/ECoreNetto.Tools.csproj index fd6fd98..fb8cca9 100644 --- a/ECoreNetto.Tools/ECoreNetto.Tools.csproj +++ b/ECoreNetto.Tools/ECoreNetto.Tools.csproj @@ -45,6 +45,7 @@ + diff --git a/ECoreNetto.Tools/Middlewares/VersionCheckerMiddleware.cs b/ECoreNetto.Tools/Middlewares/VersionCheckerMiddleware.cs new file mode 100644 index 0000000..d4594f1 --- /dev/null +++ b/ECoreNetto.Tools/Middlewares/VersionCheckerMiddleware.cs @@ -0,0 +1,95 @@ +// ------------------------------------------------------------------------------------------------- +// +// +// Copyright 2017-2024 Starion Group S.A. +// +// 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 ECoreNetto.Tools.Middlewares +{ + using System; + using System.CommandLine.Builder; + using System.CommandLine.Invocation; + using System.Net.Http; + using System.Reflection; + using System.Threading.Tasks; + + using ECoreNetto.Tools.Services; + + using Spectre.Console; + + /// + /// A middleware that checks whether a newer version is available + /// + internal static class VersionCheckerMiddleware + { + /// + /// Configures the application to show check for a new version + /// + /// + /// A command line builder. + /// + /// + /// The same instance of . + /// + public static CommandLineBuilder UseVersionChecker(this CommandLineBuilder builder) + { + return builder.AddMiddleware(async (context, next) => + { + var httpClient = new HttpClient(); + httpClient.Timeout = TimeSpan.FromSeconds(2); + var versionChecker = new VersionChecker(httpClient); + + try + { + var payload = await versionChecker.QueryLatestRelease(); + + if (payload != null) + { + var currentVersion = Assembly.GetExecutingAssembly().GetName().Version; + var publishedVersion = new Version(payload.TagName); + + if (currentVersion < publishedVersion) + { + AnsiConsole.WriteLine(""); + AnsiConsole.MarkupLine($"[Green] a newer version is available at {payload.HtmlUrl} [/]"); + AnsiConsole.MarkupLine($"[Green] {payload.Body} [/]"); + AnsiConsole.WriteLine(""); + } + else + { + AnsiConsole.WriteLine(""); + AnsiConsole.MarkupLine($"[Green] you are using the most recent version. [/]"); + AnsiConsole.WriteLine(""); + } + } + + await next(context); + } + catch (TaskCanceledException ex) + { + AnsiConsole.WriteLine(""); + AnsiConsole.MarkupLine($"[Red] Checking version at GitHub API timed out. [/]"); + AnsiConsole.WriteLine(""); + } + catch (Exception ex) + { + throw; + } + }, MiddlewareOrder.ExceptionHandler); + } + } +} \ No newline at end of file diff --git a/ECoreNetto.Tools/Program.cs b/ECoreNetto.Tools/Program.cs index 6d6e918..4cb16e3 100644 --- a/ECoreNetto.Tools/Program.cs +++ b/ECoreNetto.Tools/Program.cs @@ -38,6 +38,8 @@ namespace ECoreNetto.Tools using Spectre.Console; using ECoreNetto.Reporting.Generators; + using ECoreNetto.Tools.Services; + using Middlewares; /// /// Main entry point for the command line application @@ -65,6 +67,7 @@ public static int Main(string[] args) services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + }) .UseCommandHandler() .UseCommandHandler() @@ -101,7 +104,8 @@ private static CommandLineBuilder BuildCommandLine() AnsiConsole.Markup($"[blue]{ResourceLoader.QueryLogo()}[/]"); } )); - }); + }) + .UseVersionChecker(); } /// diff --git a/ECoreNetto.Tools/Services/GitHubRelease.cs b/ECoreNetto.Tools/Services/GitHubRelease.cs new file mode 100644 index 0000000..a46518a --- /dev/null +++ b/ECoreNetto.Tools/Services/GitHubRelease.cs @@ -0,0 +1,48 @@ +// ------------------------------------------------------------------------------------------------- +// +// +// Copyright 2017-2024 Starion Group S.A. +// +// 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 ECoreNetto.Tools.Services +{ + using System.Text.Json.Serialization; + + /// + /// The class represents a response from the GitHb API + /// + public class GitHubRelease + { + /// + /// Gets or sets the url of the release page + /// + [JsonPropertyName("html_url")] + public string HtmlUrl { get; set; } + + /// + /// Gets or sets the name of the tag + /// + [JsonPropertyName("tag_name")] + public string TagName{ get; set; } + + /// + /// Gets or sets the description of the release + /// + [JsonPropertyName("body")] + public string Body { get; set; } + } +} \ No newline at end of file diff --git a/ECoreNetto.Tools/Services/VersionChecker.cs b/ECoreNetto.Tools/Services/VersionChecker.cs new file mode 100644 index 0000000..6efe2b7 --- /dev/null +++ b/ECoreNetto.Tools/Services/VersionChecker.cs @@ -0,0 +1,98 @@ +// ------------------------------------------------------------------------------------------------- +// +// +// Copyright 2017-2024 Starion Group S.A. +// +// 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 ECoreNetto.Tools.Services +{ + using System; + using System.Net.Http; + using System.Text.Json; + using System.Threading.Tasks; + + using Microsoft.Extensions.Logging; + using Microsoft.Extensions.Logging.Abstractions; + + /// + /// The purpose of the is to check whether a newer version is available + /// + public class VersionChecker + { + /// + /// The (injected) used to check the latest version that is available + /// + private readonly HttpClient httpClient; + + /// + /// The used to log + /// + private readonly ILogger logger; + + /// + /// Initializes a new instance of the + /// + /// + /// The (injected) used to check the latest version that is available + /// + /// + /// The (injected) used to set up logging + /// + public VersionChecker(HttpClient httpClient, ILoggerFactory loggerFactory = null) + { + this.httpClient = httpClient; + this.logger = loggerFactory == null ? NullLogger.Instance : loggerFactory.CreateLogger(); + } + + /// + /// Queries the latest version from the GitHub API + /// + /// + /// an instance of or null if not found or a connection + /// error occured + /// + public async Task QueryLatestRelease() + { + var requestUrl = "https://api.github.com/repos/STARIONGROUP/EcoreNetto/releases/latest"; + + try + { + this.httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("ECoreNetto.Tools"); + + var response = await this.httpClient.GetAsync(requestUrl); + + if (response.IsSuccessStatusCode) + { + var jsonResponse = await response.Content.ReadAsStringAsync(); + var release = JsonSerializer.Deserialize(jsonResponse); + + return release; + } + } + catch (TaskCanceledException ex) + { + this.logger.LogWarning("Contacting the GitGub API at {url} timed out", requestUrl); + } + catch (Exception ex) + { + this.logger.LogError(ex, ""); + } + + return null; + } + } +} \ No newline at end of file