|
| 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 Actions.Core.Services; |
| 6 | +using ConsoleAppFramework; |
| 7 | +using Documentation.Generator.Domain; |
| 8 | +using Microsoft.Extensions.Logging; |
| 9 | + |
| 10 | +namespace Documentation.Generator.Cli; |
| 11 | + |
| 12 | +internal class Commands(ILoggerFactory logger, ICoreService githubActionsService) |
| 13 | +{ |
| 14 | + private readonly ILogger _logger = logger.CreateLogger<Commands>(); |
| 15 | + |
| 16 | + [Command("")] |
| 17 | + public async Task<int> Generate( |
| 18 | + int? seedFileSystem = null, |
| 19 | + int? seedContent = null, |
| 20 | + string? output = null, |
| 21 | + bool? clear = null, |
| 22 | + Cancel ctx = default |
| 23 | + ) |
| 24 | + { |
| 25 | + output ??= githubActionsService.GetInput("output"); |
| 26 | + var cleanOutputDirectory = clear ?? true; |
| 27 | + var outputFolder = !string.IsNullOrWhiteSpace(output) |
| 28 | + ? new DirectoryInfo(output) |
| 29 | + : new DirectoryInfo(Path.Combine(Paths.Root.FullName, ".artifacts/docs/markdown")); |
| 30 | + var stateFile = new FileInfo(Path.Combine(outputFolder.FullName, "generator.state")); |
| 31 | + |
| 32 | + LoadStateFromFile(stateFile, clear, ref seedFileSystem, ref cleanOutputDirectory); |
| 33 | + |
| 34 | + Determinism.Random = new Determinism(seedFileSystem, seedContent); |
| 35 | + |
| 36 | + _logger.LogInformation( |
| 37 | + $"Running generator with file seed: {Determinism.Random.SeedFileSystem} and content seed: {Determinism.Random.SeedContent}"); |
| 38 | + |
| 39 | + Generators.FolderName.UseSeed(Determinism.Random.SeedFileSystem); |
| 40 | + Generators.File.UseSeed(Determinism.Random.SeedFileSystem); |
| 41 | + Generators.Section.UseSeed(Determinism.Random.SeedContent); |
| 42 | + |
| 43 | + Generators.FolderNames = Generators.FolderName |
| 44 | + .Generate(Determinism.Random.FileSystem.Number(3, 15)) |
| 45 | + .SelectMany(p => Generators.CreateSubPaths(p.Folder, Determinism.Random.FileSystem.Number(0, 3), 0)) |
| 46 | + .Distinct() |
| 47 | + .ToArray(); |
| 48 | + |
| 49 | + var folders = new List<Folder>(); |
| 50 | + foreach (var folder in Generators.FolderNames) |
| 51 | + { |
| 52 | + var mdFolder = new Folder |
| 53 | + { |
| 54 | + Path = folder, |
| 55 | + Files = Generators.File |
| 56 | + .Generate(Determinism.Random.FileSystem.Number(1, 4)) |
| 57 | + .Select(f => |
| 58 | + { |
| 59 | + f.Directory = folder; |
| 60 | + return f; |
| 61 | + }) |
| 62 | + .ToArray() |
| 63 | + }; |
| 64 | + folders.Add(mdFolder); |
| 65 | + } |
| 66 | + |
| 67 | + var files = folders.SelectMany(f => f.Files).ToArray(); |
| 68 | + foreach (var folder in folders) |
| 69 | + { |
| 70 | + foreach (var file in folder.Files) |
| 71 | + { |
| 72 | + var length = Determinism.Random.Contents.Number(1, 10); |
| 73 | + file.Links = Enumerable.Range(0, length) |
| 74 | + .Select(i => files[Determinism.Random.Contents.Number(0, files.Length - 1)]) |
| 75 | + .Select(f => f.GetRandomLink()) |
| 76 | + .ToList(); |
| 77 | + file.RewriteLinksIntoSections(); |
| 78 | + } |
| 79 | + } |
| 80 | + |
| 81 | + _logger.LogInformation($"Writing to {outputFolder.FullName}"); |
| 82 | + |
| 83 | + if (outputFolder.Exists && cleanOutputDirectory) |
| 84 | + Directory.Delete(outputFolder.FullName, true); |
| 85 | + |
| 86 | + var updateFiles = files |
| 87 | + .Where(f => cleanOutputDirectory || f.IncludeInUpdate) |
| 88 | + .ToArray(); |
| 89 | + foreach (var file in updateFiles) |
| 90 | + { |
| 91 | + var directory = Path.Combine(outputFolder.FullName, file.Directory); |
| 92 | + _logger.LogInformation($"Writing to {directory}"); |
| 93 | + Directory.CreateDirectory(directory); |
| 94 | + |
| 95 | + WriteMarkdownFile(outputFolder, file); |
| 96 | + } |
| 97 | + |
| 98 | + var name = $"random-docset-{seedContent}-{seedFileSystem}"; |
| 99 | + WriteIndexMarkdownFile(name, outputFolder); |
| 100 | + |
| 101 | + var docset = Path.Combine(outputFolder.FullName, "docset.yml"); |
| 102 | + File.WriteAllText(docset, $"project: {name}{Environment.NewLine}"); |
| 103 | + File.AppendAllText(docset, $"toc:{Environment.NewLine}"); |
| 104 | + foreach (var folder in folders) |
| 105 | + File.AppendAllText(docset, $" - folder: {folder.Path}{Environment.NewLine}"); |
| 106 | + |
| 107 | + File.AppendAllText(docset, $" - file: index.md{Environment.NewLine}"); |
| 108 | + File.AppendAllText(docset, Environment.NewLine); |
| 109 | + |
| 110 | + File.WriteAllText(stateFile.FullName, $"{Determinism.Random.SeedFileSystem}|{Determinism.Random.SeedContent}"); |
| 111 | + |
| 112 | + return await Task.FromResult(0); |
| 113 | + } |
| 114 | + |
| 115 | + private void WriteIndexMarkdownFile(string name, DirectoryInfo directoryInfo) |
| 116 | + { |
| 117 | + var filePath = Path.Combine(directoryInfo.FullName, "index.md"); |
| 118 | + File.WriteAllText(filePath, |
| 119 | + $""" |
| 120 | + --- |
| 121 | + title: {name} Documentation Set |
| 122 | + --- |
| 123 | +
|
| 124 | + """); |
| 125 | + File.AppendAllText(filePath, "This docset is generated using docs-generator"); |
| 126 | + File.AppendAllText(filePath, Environment.NewLine); |
| 127 | + } |
| 128 | + |
| 129 | + private void WriteMarkdownFile(DirectoryInfo directoryInfo, MarkdownFile markdownFile) |
| 130 | + { |
| 131 | + var filePath = Path.Combine(directoryInfo.FullName, markdownFile.RelativePath); |
| 132 | + File.WriteAllText(filePath, |
| 133 | + $""" |
| 134 | + --- |
| 135 | + title: {markdownFile.Title} |
| 136 | + --- |
| 137 | +
|
| 138 | + """); |
| 139 | + foreach (var section in markdownFile.Sections) |
| 140 | + { |
| 141 | + File.AppendAllText(filePath, Environment.NewLine); |
| 142 | + var header = new string('#', section.Level); |
| 143 | + File.AppendAllText(filePath, $"{header} {section.Header}{Environment.NewLine}"); |
| 144 | + File.AppendAllText(filePath, Environment.NewLine); |
| 145 | + |
| 146 | + File.AppendAllText(filePath, section.Paragraphs); |
| 147 | + File.AppendAllText(filePath, Environment.NewLine); |
| 148 | + } |
| 149 | + } |
| 150 | + |
| 151 | + void LoadStateFromFile(FileInfo fileInfo, bool? clear, ref int? seedFs, ref bool cleanOutput) |
| 152 | + { |
| 153 | + if (!fileInfo.Exists) return; |
| 154 | + var state = File.ReadAllText(fileInfo.FullName).Split("|"); |
| 155 | + if (state.Length != 2) return; |
| 156 | + seedFs ??= int.TryParse(state[0], out var seed) ? seed : seedFs; |
| 157 | + _logger.LogInformation($"Seeding with {seedFs} from previous run {fileInfo.FullName}"); |
| 158 | + cleanOutput = clear ?? false; |
| 159 | + } |
| 160 | +} |
0 commit comments