Skip to content

Commit 5b7a700

Browse files
authored
Advertise docs-builder updates when running from command line (#276)
* Advertise docs-builder updates when running from the command line * Advertise at the end of command invocation * remove up to date message * include installation documentation in the output
1 parent 3166716 commit 5b7a700

File tree

3 files changed

+99
-1
lines changed

3 files changed

+99
-1
lines changed
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
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+
}

src/docs-builder/Cli/Commands.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ internal class Commands(ILoggerFactory logger, ICoreService githubActionsService
2525
/// <param name="port">Port to serve the documentation.</param>
2626
/// <param name="ctx"></param>
2727
[Command("serve")]
28+
[ConsoleAppFilter<CheckForUpdatesFilter>]
2829
public async Task Serve(string? path = null, int port = 3000, Cancel ctx = default)
2930
{
3031
var host = new DocumentationWebHost(path, port, logger, new FileSystem());
@@ -45,6 +46,7 @@ public async Task Serve(string? path = null, int port = 3000, Cancel ctx = defau
4546
[Command("generate")]
4647
[ConsoleAppFilter<StopwatchFilter>]
4748
[ConsoleAppFilter<CatchExceptionFilter>]
49+
[ConsoleAppFilter<CheckForUpdatesFilter>]
4850
public async Task<int> Generate(
4951
string? path = null,
5052
string? output = null,
@@ -89,6 +91,7 @@ public async Task<int> Generate(
8991
[Command("")]
9092
[ConsoleAppFilter<StopwatchFilter>]
9193
[ConsoleAppFilter<CatchExceptionFilter>]
94+
[ConsoleAppFilter<CheckForUpdatesFilter>]
9295
public async Task<int> GenerateDefault(
9396
string? path = null,
9497
string? output = null,

src/docs-builder/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@
2727
services.AddSingleton<DiagnosticsChannel>();
2828
services.AddSingleton<DiagnosticsCollector>();
2929

30-
3130
await using var serviceProvider = services.BuildServiceProvider();
3231
var logger = serviceProvider.GetRequiredService<ILogger<Program>>();
3332
ConsoleApp.ServiceProvider = serviceProvider;
33+
3434
var isHelp = args.Contains("-h") || args.Contains("--help");
3535
if (!isHelp)
3636
ConsoleApp.Log = msg => logger.LogInformation(msg);

0 commit comments

Comments
 (0)