Skip to content

Commit 6f5d20e

Browse files
authored
Add links create-index command to the assembler (#499)
1 parent 3dbf8e9 commit 6f5d20e

File tree

5 files changed

+147
-0
lines changed

5 files changed

+147
-0
lines changed

src/Elastic.Markdown/CrossLinks/CrossLinkResolver.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,41 @@
55
using System.Collections.Frozen;
66
using System.Diagnostics.CodeAnalysis;
77
using System.Text.Json;
8+
using System.Text.Json.Serialization;
89
using Elastic.Markdown.IO.Configuration;
910
using Elastic.Markdown.IO.State;
1011
using Microsoft.Extensions.Logging;
1112

1213
namespace Elastic.Markdown.CrossLinks;
1314

15+
16+
public record LinkIndex
17+
{
18+
[JsonPropertyName("repositories")]
19+
public required Dictionary<string, Dictionary<string, LinkIndexEntry>> Repositories { get; init; }
20+
21+
public static LinkIndex Deserialize(string json) =>
22+
JsonSerializer.Deserialize(json, SourceGenerationContext.Default.LinkIndex)!;
23+
24+
public static string Serialize(LinkIndex index) =>
25+
JsonSerializer.Serialize(index, SourceGenerationContext.Default.LinkIndex);
26+
}
27+
28+
public record LinkIndexEntry
29+
{
30+
[JsonPropertyName("repository")]
31+
public required string Repository { get; init; }
32+
33+
[JsonPropertyName("path")]
34+
public required string Path { get; init; }
35+
36+
[JsonPropertyName("branch")]
37+
public required string Branch { get; init; }
38+
39+
[JsonPropertyName("etag")]
40+
public required string ETag { get; init; }
41+
}
42+
1443
public interface ICrossLinkResolver
1544
{
1645
Task FetchLinks();

src/Elastic.Markdown/SourceGenerationContext.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// See the LICENSE file in the project root for more information
44

55
using System.Text.Json.Serialization;
6+
using Elastic.Markdown.CrossLinks;
67
using Elastic.Markdown.IO;
78
using Elastic.Markdown.IO.Discovery;
89
using Elastic.Markdown.IO.State;
@@ -15,4 +16,6 @@ namespace Elastic.Markdown;
1516
[JsonSerializable(typeof(GenerationState))]
1617
[JsonSerializable(typeof(LinkReference))]
1718
[JsonSerializable(typeof(GitCheckoutInformation))]
19+
[JsonSerializable(typeof(LinkIndex))]
20+
[JsonSerializable(typeof(LinkIndexEntry))]
1821
internal partial class SourceGenerationContext : JsonSerializerContext;
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
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.Text;
6+
using Actions.Core.Services;
7+
using Amazon.S3;
8+
using Amazon.S3.Model;
9+
using ConsoleAppFramework;
10+
using Elastic.Markdown.CrossLinks;
11+
using Microsoft.Extensions.Logging;
12+
13+
namespace Documentation.Assembler.Cli;
14+
15+
internal class LinkCommands(ILoggerFactory logger)
16+
{
17+
private void AssignOutputLogger()
18+
{
19+
var log = logger.CreateLogger<Program>();
20+
ConsoleApp.Log = msg => log.LogInformation(msg);
21+
ConsoleApp.LogError = msg => log.LogError(msg);
22+
}
23+
24+
/// <summary>
25+
/// Create an index.json file from all discovered links.json files in our S3 bucket
26+
/// </summary>
27+
/// <param name="ctx"></param>
28+
[Command("links create-index")]
29+
public async Task CreateLinkIndex(Cancel ctx = default)
30+
{
31+
AssignOutputLogger();
32+
33+
IAmazonS3 client = new AmazonS3Client();
34+
var bucketName = "elastic-docs-link-index";
35+
var request = new ListObjectsV2Request { BucketName = bucketName, MaxKeys = 5 };
36+
37+
Console.WriteLine("--------------------------------------");
38+
Console.WriteLine($"Listing the contents of {bucketName}:");
39+
Console.WriteLine("--------------------------------------");
40+
41+
42+
var linkIndex = new LinkIndex { Repositories = new Dictionary<string, Dictionary<string, LinkIndexEntry>>() };
43+
try
44+
{
45+
ListObjectsV2Response response;
46+
do
47+
{
48+
response = await client.ListObjectsV2Async(request, ctx);
49+
foreach (var obj in response.S3Objects)
50+
{
51+
if (!obj.Key.StartsWith("elastic/"))
52+
continue;
53+
54+
var tokens = obj.Key.Split('/');
55+
if (tokens.Length < 3)
56+
continue;
57+
58+
var repository = tokens[1];
59+
var branch = tokens[2];
60+
61+
var entry = new LinkIndexEntry { Repository = repository, Branch = branch, ETag = obj.ETag.Trim('"'), Path = obj.Key };
62+
if (linkIndex.Repositories.TryGetValue(repository, out var existingEntry))
63+
existingEntry[branch] = entry;
64+
else
65+
linkIndex.Repositories.Add(repository, new Dictionary<string, LinkIndexEntry> { { branch, entry } });
66+
Console.WriteLine(entry);
67+
}
68+
69+
// If the response is truncated, set the request ContinuationToken
70+
// from the NextContinuationToken property of the response.
71+
request.ContinuationToken = response.NextContinuationToken;
72+
} while (response.IsTruncated);
73+
}
74+
catch (AmazonS3Exception ex)
75+
{
76+
Console.WriteLine($"Error encountered on server. Message:'{ex.Message}' getting list of objects.");
77+
}
78+
79+
var json = LinkIndex.Serialize(linkIndex);
80+
Console.WriteLine(json);
81+
82+
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(json));
83+
await client.UploadObjectFromStreamAsync(bucketName, "link-index.json", stream, new Dictionary<string, object>(), ctx);
84+
85+
Console.WriteLine("Uploaded latest link-index.json");
86+
}
87+
}

src/docs-assembler/Program.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,47 @@
44

55
using System.Collections.Concurrent;
66
using System.Diagnostics;
7+
using Actions.Core.Extensions;
78
using ConsoleAppFramework;
89
using Documentation.Assembler;
910
using Documentation.Assembler.Cli;
11+
using Elastic.Markdown.Diagnostics;
1012
using Elastic.Markdown.IO;
13+
using Microsoft.Extensions.DependencyInjection;
14+
using Microsoft.Extensions.Logging;
1115
using ProcNet;
1216
using ProcNet.Std;
1317

1418
var configFile = Path.Combine(Paths.Root.FullName, "src/docs-assembler/conf.yml");
1519
var config = AssemblyConfiguration.Deserialize(File.ReadAllText(configFile));
1620

21+
var services = new ServiceCollection();
22+
services.AddGitHubActionsCore();
23+
services.AddLogging(x =>
24+
{
25+
x.ClearProviders();
26+
x.SetMinimumLevel(LogLevel.Information);
27+
x.AddSimpleConsole(c =>
28+
{
29+
c.SingleLine = true;
30+
c.IncludeScopes = true;
31+
c.UseUtcTimestamp = true;
32+
c.TimestampFormat = Environment.UserInteractive ? ":: " : "[yyyy-MM-ddTHH:mm:ss] ";
33+
});
34+
});
35+
services.AddSingleton<DiagnosticsChannel>();
36+
services.AddSingleton<DiagnosticsCollector>();
37+
38+
await using var serviceProvider = services.BuildServiceProvider();
39+
ConsoleApp.ServiceProvider = serviceProvider;
40+
41+
1742
var app = ConsoleApp.Create();
1843
app.UseFilter<StopwatchFilter>();
1944
app.UseFilter<CatchExceptionFilter>();
2045

46+
app.Add<LinkCommands>();
47+
2148
// would love to use libgit2 so there is no git dependency but
2249
// libgit2 is magnitudes slower to clone repositories https://github.com/libgit2/libgit2/issues/4674
2350
app.Add("clone-all", async Task (CancellationToken ctx) =>

src/docs-assembler/docs-assembler.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
</PropertyGroup>
1717

1818
<ItemGroup>
19+
<PackageReference Include="AWSSDK.S3" Version="3.7.414.5" />
1920
<PackageReference Include="ConsoleAppFramework" Version="5.3.3">
2021
<PrivateAssets>all</PrivateAssets>
2122
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

0 commit comments

Comments
 (0)