Skip to content

Commit fa42893

Browse files
authored
Initial scaffolding of the docs-assembler binary (#186)
- Add new project - stage - stage - Update docs-assembler after merging with main.
1 parent 7cc289e commit fa42893

File tree

8 files changed

+395
-4
lines changed

8 files changed

+395
-4
lines changed

build/Targets.fs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ let private pristineCheck (arguments:ParseResults<Build>) =
5757
let private publishBinaries _ =
5858
exec { run "dotnet" "publish" "src/docs-builder/docs-builder.csproj" }
5959
exec { run "dotnet" "publish" "src/docs-generator/docs-generator.csproj" }
60+
exec { run "dotnet" "publish" "src/docs-assembler/docs-assembler.csproj" }
6061
Zip.zip
6162
".artifacts/publish/docs-builder/release"
6263
$"docs-builder-%s{OS.Name}-{OS.Arch}.zip"
@@ -103,6 +104,7 @@ let private publishContainers _ =
103104
exec { run "dotnet" (args @ registry) }
104105
createImage "docs-builder"
105106
createImage "docs-generator"
107+
createImage "docs-assembler"
106108

107109
let private runTests _ =
108110
exec {

docs-builder.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "publish-vercel", "publish-v
4848
EndProject
4949
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "build", "build\build.fsproj", "{10857974-6CF1-42B5-B793-AAA988BD7348}"
5050
EndProject
51+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "docs-assembler", "src\docs-assembler\docs-assembler.csproj", "{28350800-B44B-479B-86E2-1D39E321C0B4}"
52+
EndProject
5153
Global
5254
GlobalSection(SolutionConfigurationPlatforms) = preSolution
5355
Debug|Any CPU = Debug|Any CPU
@@ -82,6 +84,10 @@ Global
8284
{10857974-6CF1-42B5-B793-AAA988BD7348}.Debug|Any CPU.Build.0 = Debug|Any CPU
8385
{10857974-6CF1-42B5-B793-AAA988BD7348}.Release|Any CPU.ActiveCfg = Release|Any CPU
8486
{10857974-6CF1-42B5-B793-AAA988BD7348}.Release|Any CPU.Build.0 = Release|Any CPU
87+
{28350800-B44B-479B-86E2-1D39E321C0B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
88+
{28350800-B44B-479B-86E2-1D39E321C0B4}.Debug|Any CPU.Build.0 = Debug|Any CPU
89+
{28350800-B44B-479B-86E2-1D39E321C0B4}.Release|Any CPU.ActiveCfg = Release|Any CPU
90+
{28350800-B44B-479B-86E2-1D39E321C0B4}.Release|Any CPU.Build.0 = Release|Any CPU
8591
EndGlobalSection
8692
GlobalSection(NestedProjects) = preSolution
8793
{4D198E25-C211-41DC-9E84-B15E89BD7048} = {BE6011CC-1200-4957-B01F-FCCA10C5CF5A}
@@ -91,5 +97,6 @@ Global
9197
{1C340CCF-9AAC-4163-A7BB-60528076E98B} = {245023D2-D3CA-47B9-831D-DAB91A2FFDC7}
9298
{CD2887E3-BDA9-434B-A5BF-9ED38DE20332} = {245023D2-D3CA-47B9-831D-DAB91A2FFDC7}
9399
{A2A34BBC-CB5E-4100-9529-A12B6ECB769C} = {245023D2-D3CA-47B9-831D-DAB91A2FFDC7}
100+
{28350800-B44B-479B-86E2-1D39E321C0B4} = {BE6011CC-1200-4957-B01F-FCCA10C5CF5A}
94101
EndGlobalSection
95102
EndGlobal
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
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 YamlDotNet.Serialization;
6+
7+
namespace Documentation.Assembler;
8+
9+
[YamlStaticContext]
10+
[YamlSerializable(typeof(AssemblyConfiguration))]
11+
[YamlSerializable(typeof(Repository))]
12+
public partial class YamlStaticContext;
13+
14+
public record AssemblyConfiguration
15+
{
16+
public static AssemblyConfiguration Deserialize(string yaml)
17+
{
18+
var input = new StringReader(yaml);
19+
20+
var deserializer = new StaticDeserializerBuilder(new YamlStaticContext())
21+
.IgnoreUnmatchedProperties()
22+
.Build();
23+
24+
try
25+
{
26+
var config = deserializer.Deserialize<AssemblyConfiguration>(input);
27+
return config;
28+
}
29+
catch (Exception e)
30+
{
31+
Console.WriteLine(e);
32+
Console.WriteLine(e.InnerException);
33+
throw;
34+
}
35+
}
36+
37+
[YamlMember(Alias = "repos")]
38+
public Dictionary<string, Repository> Repositories { get; set; } = new();
39+
}
40+
41+
public record Repository
42+
{
43+
[YamlMember(Alias = "repo")]
44+
public string Origin { get; set; } = string.Empty;
45+
46+
[YamlMember(Alias = "branch")]
47+
public string? Branch { get; set; }
48+
49+
}

src/docs-assembler/Cli/Filters.cs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
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.Diagnostics;
6+
using ConsoleAppFramework;
7+
8+
namespace Documentation.Assembler.Cli;
9+
10+
internal class StopwatchFilter(ConsoleAppFilter next) : ConsoleAppFilter(next)
11+
{
12+
public override async Task InvokeAsync(ConsoleAppContext context, Cancel ctx)
13+
{
14+
var isHelpOrVersion = context.Arguments.Any(a => a is "--help" or "-h" or "--version");
15+
var name = string.IsNullOrWhiteSpace(context.CommandName) ? "generate" : context.CommandName;
16+
var startTime = Stopwatch.GetTimestamp();
17+
if (!isHelpOrVersion)
18+
ConsoleApp.Log($"{name} :: Starting...");
19+
try
20+
{
21+
await Next.InvokeAsync(context, ctx);
22+
}
23+
finally
24+
{
25+
var endTime = Stopwatch.GetElapsedTime(startTime);
26+
if (!isHelpOrVersion)
27+
ConsoleApp.Log($"{name} :: Finished in '{endTime}");
28+
}
29+
}
30+
}
31+
32+
internal class CatchExceptionFilter(ConsoleAppFilter next) : ConsoleAppFilter(next)
33+
{
34+
public override async Task InvokeAsync(ConsoleAppContext context, CancellationToken cancellationToken)
35+
{
36+
try
37+
{
38+
await Next.InvokeAsync(context, cancellationToken);
39+
}
40+
catch (Exception ex)
41+
{
42+
if (ex is OperationCanceledException)
43+
{
44+
ConsoleApp.Log("Cancellation requested, exiting.");
45+
return;
46+
}
47+
48+
ConsoleApp.LogError(ex.ToString()); // .ToString() shows stacktrace, .Message can avoid showing stacktrace to user.
49+
}
50+
}
51+
}

src/docs-assembler/Program.cs

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
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.Collections.Concurrent;
6+
using System.Diagnostics;
7+
using ConsoleAppFramework;
8+
using Documentation.Assembler;
9+
using Documentation.Assembler.Cli;
10+
using Elastic.Markdown.IO;
11+
using ProcNet;
12+
using ProcNet.Std;
13+
14+
var configFile = Path.Combine(Paths.Root.FullName, "src/docs-assembler/conf.yml");
15+
var config = AssemblyConfiguration.Deserialize(File.ReadAllText(configFile));
16+
17+
var app = ConsoleApp.Create();
18+
app.UseFilter<StopwatchFilter>();
19+
app.UseFilter<CatchExceptionFilter>();
20+
21+
// would love to use libgit2 so there is no git dependency but
22+
// libgit2 is magnitudes slower to clone repositories https://github.com/libgit2/libgit2/issues/4674
23+
app.Add("clone-all", async Task (CancellationToken ctx) =>
24+
{
25+
Console.WriteLine(config.Repositories.Count);
26+
var dict = new ConcurrentDictionary<string, Stopwatch>();
27+
await Parallel.ForEachAsync(config.Repositories, new ParallelOptions
28+
{
29+
CancellationToken = ctx,
30+
MaxDegreeOfParallelism = Environment.ProcessorCount / 4
31+
}, async (kv, c) =>
32+
{
33+
await Task.Run(() =>
34+
{
35+
var name = kv.Key;
36+
var repository = kv.Value;
37+
var checkoutFolder = Path.Combine(Paths.Root.FullName, $".artifacts/assembly/{name}");
38+
39+
var sw = Stopwatch.StartNew();
40+
dict.AddOrUpdate(name, sw, (_, _) => sw);
41+
Console.WriteLine($"Checkout: {name}\t{repository}\t{checkoutFolder}");
42+
var branch = repository.Branch ?? "main";
43+
var args = new StartArguments(
44+
"git", "clone", repository.Origin, checkoutFolder, "--depth", "1"
45+
, "--single-branch", "--branch", branch
46+
);
47+
Proc.StartRedirected(args, new ConsoleLineHandler(name));
48+
sw.Stop();
49+
}, c);
50+
}).ConfigureAwait(false);
51+
52+
foreach (var kv in dict.OrderBy(kv => kv.Value.Elapsed))
53+
Console.WriteLine($"-> {kv.Key}\ttook: {kv.Value.Elapsed}");
54+
});
55+
app.Add("list", async Task (CancellationToken ctx) =>
56+
{
57+
58+
var assemblyPath = Path.Combine(Paths.Root.FullName, $".artifacts/assembly");
59+
var dir = new DirectoryInfo(assemblyPath);
60+
var dictionary = new Dictionary<string, string>();
61+
foreach (var d in dir.GetDirectories())
62+
{
63+
var checkoutFolder = Path.Combine(assemblyPath, d.Name);
64+
65+
var capture = Proc.Start(
66+
new StartArguments("git", "rev-parse", "--abbrev-ref", "HEAD")
67+
{
68+
WorkingDirectory = checkoutFolder
69+
}
70+
);
71+
dictionary.Add(d.Name, capture.ConsoleOut.FirstOrDefault()?.Line ?? "unknown");
72+
}
73+
foreach (var kv in dictionary.OrderBy(kv => kv.Value))
74+
Console.WriteLine($"-> {kv.Key}\tbranch: {kv.Value}");
75+
76+
await Task.CompletedTask;
77+
});
78+
79+
await app.RunAsync(args);
80+
81+
namespace Documentation.Assembler
82+
{
83+
public class ConsoleLineHandler(string prefix) : IConsoleLineHandler
84+
{
85+
public void Handle(LineOut lineOut) => lineOut.CharsOrString(
86+
r => Console.Write(prefix + ": " + r),
87+
l => Console.WriteLine(prefix + ": " + l));
88+
89+
public void Handle(Exception e) { }
90+
}
91+
}

0 commit comments

Comments
 (0)