From a2d5c6c29f01ec076bda8c7539a417a37110d346 Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Fri, 14 Feb 2025 14:56:07 +0100 Subject: [PATCH] Add Documentation Assembler GitHub Action Introduce a new GitHub Action named "Documentation Assembler" to run specified assembler commands using a Docker image. --- actions/assembler/action.yml | 15 ++++ docs-builder.sln | 6 ++ src/docs-assembler/Cli/LinkCommands.cs | 2 +- src/docs-assembler/Cli/RepositoryCommands.cs | 94 ++++++++++++++++++++ src/docs-assembler/Program.cs | 78 ++-------------- 5 files changed, 123 insertions(+), 72 deletions(-) create mode 100644 actions/assembler/action.yml create mode 100644 src/docs-assembler/Cli/RepositoryCommands.cs diff --git a/actions/assembler/action.yml b/actions/assembler/action.yml new file mode 100644 index 000000000..ae213591d --- /dev/null +++ b/actions/assembler/action.yml @@ -0,0 +1,15 @@ +name: 'Documentation Assembler' +description: 'Run the documenation assembler commands' + +branding: + icon: 'filter' + color: 'red' + +inputs: + command: + description: 'The assembler command to run' + required: true + +runs: + using: 'docker' + image: "docker://ghcr.io/elastic/docs-assembler:edge" diff --git a/docs-builder.sln b/docs-builder.sln index 5a621c48f..1b00ed9a2 100644 --- a/docs-builder.sln +++ b/docs-builder.sln @@ -48,6 +48,11 @@ Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "authoring", "tests\authorin EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elastic.Markdown.Refactor", "src\Elastic.Markdown.Refactor\Elastic.Markdown.Refactor.csproj", "{7D36DDDA-9E0B-4D2C-8033-5D62FF8B6166}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "assembler", "assembler", "{CFEE9FAD-9E0C-4C0E-A0C2-B97D594C14B5}" + ProjectSection(SolutionItems) = preProject + actions\assembler\action.yml = actions\assembler\action.yml + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -100,5 +105,6 @@ Global {28350800-B44B-479B-86E2-1D39E321C0B4} = {BE6011CC-1200-4957-B01F-FCCA10C5CF5A} {018F959E-824B-4664-B345-066784478D24} = {67B576EE-02FA-4F9B-94BC-3630BC09ECE5} {7D36DDDA-9E0B-4D2C-8033-5D62FF8B6166} = {BE6011CC-1200-4957-B01F-FCCA10C5CF5A} + {CFEE9FAD-9E0C-4C0E-A0C2-B97D594C14B5} = {245023D2-D3CA-47B9-831D-DAB91A2FFDC7} EndGlobalSection EndGlobal diff --git a/src/docs-assembler/Cli/LinkCommands.cs b/src/docs-assembler/Cli/LinkCommands.cs index 0ab50b88a..155f1addf 100644 --- a/src/docs-assembler/Cli/LinkCommands.cs +++ b/src/docs-assembler/Cli/LinkCommands.cs @@ -25,7 +25,7 @@ private void AssignOutputLogger() /// Create an index.json file from all discovered links.json files in our S3 bucket /// /// - [Command("links create-index")] + [Command("create-index")] public async Task CreateLinkIndex(Cancel ctx = default) { AssignOutputLogger(); diff --git a/src/docs-assembler/Cli/RepositoryCommands.cs b/src/docs-assembler/Cli/RepositoryCommands.cs new file mode 100644 index 000000000..514ef792f --- /dev/null +++ b/src/docs-assembler/Cli/RepositoryCommands.cs @@ -0,0 +1,94 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. +// See the LICENSE file in the project root for more information + +using System.Collections.Concurrent; +using System.Diagnostics; +using ConsoleAppFramework; +using Elastic.Markdown.IO; +using Microsoft.Extensions.Logging; +using ProcNet; +using ProcNet.Std; + +namespace Documentation.Assembler.Cli; + +public class ConsoleLineHandler(string prefix) : IConsoleLineHandler +{ + public void Handle(LineOut lineOut) => lineOut.CharsOrString( + r => Console.Write(prefix + ": " + r), + l => Console.WriteLine(prefix + ": " + l)); + + public void Handle(Exception e) { } +} + +internal class RepositoryCommands(ILoggerFactory logger) +{ + private void AssignOutputLogger() + { + var log = logger.CreateLogger(); + ConsoleApp.Log = msg => log.LogInformation(msg); + ConsoleApp.LogError = msg => log.LogError(msg); + } + + // would love to use libgit2 so there is no git dependency but + // libgit2 is magnitudes slower to clone repositories https://github.com/libgit2/libgit2/issues/4674 + /// Clones all repositories + /// + [Command("clone-all")] + public async Task CloneAll(Cancel ctx = default) + { + var configFile = Path.Combine(Paths.Root.FullName, "src/docs-assembler/conf.yml"); + var config = AssemblyConfiguration.Deserialize(File.ReadAllText(configFile)); + + Console.WriteLine(config.Repositories.Count); + var dict = new ConcurrentDictionary(); + await Parallel.ForEachAsync(config.Repositories, + new ParallelOptions { CancellationToken = ctx, MaxDegreeOfParallelism = Environment.ProcessorCount / 4 }, async (kv, c) => + { + await Task.Run(() => + { + var name = kv.Key; + var repository = kv.Value; + var checkoutFolder = Path.Combine(Paths.Root.FullName, $".artifacts/assembly/{name}"); + + var sw = Stopwatch.StartNew(); + dict.AddOrUpdate(name, sw, (_, _) => sw); + Console.WriteLine($"Checkout: {name}\t{repository}\t{checkoutFolder}"); + var branch = repository.Branch ?? "main"; + var args = new StartArguments( + "git", "clone", repository.Origin, checkoutFolder, "--depth", "1" + , "--single-branch", "--branch", branch + ); + Proc.StartRedirected(args, new ConsoleLineHandler(name)); + sw.Stop(); + }, c); + }).ConfigureAwait(false); + + foreach (var kv in dict.OrderBy(kv => kv.Value.Elapsed)) + Console.WriteLine($"-> {kv.Key}\ttook: {kv.Value.Elapsed}"); + } + + /// List all checked out repositories + /// + [Command("list")] + public async Task ListRepositories(Cancel ctx = default) + { + var assemblyPath = Path.Combine(Paths.Root.FullName, $".artifacts/assembly"); + var dir = new DirectoryInfo(assemblyPath); + var dictionary = new Dictionary(); + foreach (var d in dir.GetDirectories()) + { + var checkoutFolder = Path.Combine(assemblyPath, d.Name); + + var capture = Proc.Start( + new StartArguments("git", "rev-parse", "--abbrev-ref", "HEAD") { WorkingDirectory = checkoutFolder } + ); + dictionary.Add(d.Name, capture.ConsoleOut.FirstOrDefault()?.Line ?? "unknown"); + } + + foreach (var kv in dictionary.OrderBy(kv => kv.Value)) + Console.WriteLine($"-> {kv.Key}\tbranch: {kv.Value}"); + + await Task.CompletedTask; + } +} diff --git a/src/docs-assembler/Program.cs b/src/docs-assembler/Program.cs index 839f9282b..930f4bcc0 100644 --- a/src/docs-assembler/Program.cs +++ b/src/docs-assembler/Program.cs @@ -5,6 +5,7 @@ using System.Collections.Concurrent; using System.Diagnostics; using Actions.Core.Extensions; +using Actions.Core.Services; using ConsoleAppFramework; using Documentation.Assembler; using Documentation.Assembler.Cli; @@ -38,81 +39,16 @@ await using var serviceProvider = services.BuildServiceProvider(); ConsoleApp.ServiceProvider = serviceProvider; - var app = ConsoleApp.Create(); app.UseFilter(); app.UseFilter(); -app.Add(); - -// would love to use libgit2 so there is no git dependency but -// libgit2 is magnitudes slower to clone repositories https://github.com/libgit2/libgit2/issues/4674 -app.Add("clone-all", async Task (CancellationToken ctx) => -{ - Console.WriteLine(config.Repositories.Count); - var dict = new ConcurrentDictionary(); - await Parallel.ForEachAsync(config.Repositories, new ParallelOptions - { - CancellationToken = ctx, - MaxDegreeOfParallelism = Environment.ProcessorCount / 4 - }, async (kv, c) => - { - await Task.Run(() => - { - var name = kv.Key; - var repository = kv.Value; - var checkoutFolder = Path.Combine(Paths.Root.FullName, $".artifacts/assembly/{name}"); +app.Add("link"); +app.Add("repo"); - var sw = Stopwatch.StartNew(); - dict.AddOrUpdate(name, sw, (_, _) => sw); - Console.WriteLine($"Checkout: {name}\t{repository}\t{checkoutFolder}"); - var branch = repository.Branch ?? "main"; - var args = new StartArguments( - "git", "clone", repository.Origin, checkoutFolder, "--depth", "1" - , "--single-branch", "--branch", branch - ); - Proc.StartRedirected(args, new ConsoleLineHandler(name)); - sw.Stop(); - }, c); - }).ConfigureAwait(false); - - foreach (var kv in dict.OrderBy(kv => kv.Value.Elapsed)) - Console.WriteLine($"-> {kv.Key}\ttook: {kv.Value.Elapsed}"); -}); -app.Add("list", async Task (CancellationToken ctx) => -{ - - var assemblyPath = Path.Combine(Paths.Root.FullName, $".artifacts/assembly"); - var dir = new DirectoryInfo(assemblyPath); - var dictionary = new Dictionary(); - foreach (var d in dir.GetDirectories()) - { - var checkoutFolder = Path.Combine(assemblyPath, d.Name); - - var capture = Proc.Start( - new StartArguments("git", "rev-parse", "--abbrev-ref", "HEAD") - { - WorkingDirectory = checkoutFolder - } - ); - dictionary.Add(d.Name, capture.ConsoleOut.FirstOrDefault()?.Line ?? "unknown"); - } - foreach (var kv in dictionary.OrderBy(kv => kv.Value)) - Console.WriteLine($"-> {kv.Key}\tbranch: {kv.Value}"); - - await Task.CompletedTask; -}); +var githubActions = ConsoleApp.ServiceProvider.GetService(); +var command = githubActions?.GetInput("command"); +if (!string.IsNullOrEmpty(command)) + args = command.Split(' '); await app.RunAsync(args); - -namespace Documentation.Assembler -{ - public class ConsoleLineHandler(string prefix) : IConsoleLineHandler - { - public void Handle(LineOut lineOut) => lineOut.CharsOrString( - r => Console.Write(prefix + ": " + r), - l => Console.WriteLine(prefix + ": " + l)); - - public void Handle(Exception e) { } - } -}