|
| 1 | +// Licensed to Elasticsearch B.V under one or more agreements. |
| 2 | +// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. |
| 3 | +// See the LICENSE file in the project root for more information |
| 4 | + |
| 5 | +using System.Reflection; |
| 6 | +using ConsoleAppFramework; |
| 7 | +using Elastic.Markdown.Helpers; |
| 8 | + |
| 9 | +namespace Documentation.Builder.Cli; |
| 10 | + |
| 11 | +internal class CheckForUpdatesFilter : ConsoleAppFilter |
| 12 | +{ |
| 13 | + private readonly FileInfo _stateFile; |
| 14 | + |
| 15 | + public CheckForUpdatesFilter(ConsoleAppFilter next) : base(next) |
| 16 | + { |
| 17 | + // ~/Library/Application\ Support/ on osx |
| 18 | + // XDG_DATA_HOME or home/.local/share on linux |
| 19 | + // %LOCAL_APPLICATION_DATA% windows |
| 20 | + var localPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); |
| 21 | + var elasticPath = Path.Combine(localPath, "elastic"); |
| 22 | + _stateFile = new FileInfo(Path.Combine(elasticPath, "docs-build-check.state")); |
| 23 | + } |
| 24 | + |
| 25 | + public override async Task InvokeAsync(ConsoleAppContext context, Cancel ctx) |
| 26 | + { |
| 27 | + await Next.InvokeAsync(context, ctx); |
| 28 | + var latestVersionUrl = await GetLatestVersion(ctx); |
| 29 | + if (latestVersionUrl is null) |
| 30 | + ConsoleApp.LogError("Unable to determine latest version"); |
| 31 | + else |
| 32 | + CompareWithAssemblyVersion(latestVersionUrl); |
| 33 | + } |
| 34 | + |
| 35 | + private void CompareWithAssemblyVersion(Uri latestVersionUrl) |
| 36 | + { |
| 37 | + var versionPath = latestVersionUrl.AbsolutePath.Split('/').Last(); |
| 38 | + if (!SemVersion.TryParse(versionPath, out var latestVersion)) |
| 39 | + { |
| 40 | + ConsoleApp.LogError($"Unable to parse latest version from {latestVersionUrl}"); |
| 41 | + return; |
| 42 | + } |
| 43 | + |
| 44 | + var assemblyVersion = Assembly.GetExecutingAssembly().GetCustomAttributes<AssemblyInformationalVersionAttribute>() |
| 45 | + .FirstOrDefault()?.InformationalVersion; |
| 46 | + if (SemVersion.TryParse(assemblyVersion ?? "", out var currentSemVersion)) |
| 47 | + { |
| 48 | + if (latestVersion <= currentSemVersion) |
| 49 | + return; |
| 50 | + ConsoleApp.Log(""); |
| 51 | + ConsoleApp.Log($"A new version of docs-builder is available: {latestVersion} currently on version {currentSemVersion}"); |
| 52 | + ConsoleApp.Log(""); |
| 53 | + ConsoleApp.Log($" {latestVersionUrl}"); |
| 54 | + ConsoleApp.Log(""); |
| 55 | + ConsoleApp.Log("Read more about updating here:"); |
| 56 | + ConsoleApp.Log(" https://elastic.github.io/docs-builder/contribute/locally.html#step-one "); |
| 57 | + ConsoleApp.Log(""); |
| 58 | + return; |
| 59 | + } |
| 60 | + |
| 61 | + ConsoleApp.LogError($"Unable to parse current version from docs-builder binary"); |
| 62 | + } |
| 63 | + |
| 64 | + private async ValueTask<Uri?> GetLatestVersion(CancellationToken ctx) |
| 65 | + { |
| 66 | + if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("CI"))) |
| 67 | + return null; |
| 68 | + |
| 69 | + // only check for new versions once per hour |
| 70 | + if (_stateFile.Exists && _stateFile.LastWriteTimeUtc >= DateTime.UtcNow.Subtract(TimeSpan.FromHours(1))) |
| 71 | + { |
| 72 | + var url = await File.ReadAllTextAsync(_stateFile.FullName, ctx); |
| 73 | + if (Uri.TryCreate(url, UriKind.Absolute, out var uri)) |
| 74 | + return uri; |
| 75 | + } |
| 76 | + |
| 77 | + try |
| 78 | + { |
| 79 | + var httpClient = new HttpClient(new HttpClientHandler { AllowAutoRedirect = false }); |
| 80 | + var response = await httpClient.GetAsync("https://github.com/elastic/docs-builder/releases/latest", ctx); |
| 81 | + var redirectUrl = response.Headers.Location; |
| 82 | + if (redirectUrl is not null && _stateFile.Directory is not null) |
| 83 | + { |
| 84 | + // ensure the 'elastic' folder exists. |
| 85 | + if (!Directory.Exists(_stateFile.Directory.FullName)) |
| 86 | + Directory.CreateDirectory(_stateFile.Directory.FullName); |
| 87 | + await File.WriteAllTextAsync(_stateFile.FullName, redirectUrl.ToString(), ctx); |
| 88 | + } |
| 89 | + return redirectUrl; |
| 90 | + } |
| 91 | + // ReSharper disable once RedundantEmptyFinallyBlock |
| 92 | + // ignore on purpose |
| 93 | + finally { } |
| 94 | + } |
| 95 | +} |
0 commit comments